parser.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package date // import "miniflux.app/reader/date"
  4. import (
  5. "errors"
  6. "fmt"
  7. "strconv"
  8. "strings"
  9. "time"
  10. )
  11. // DateFormats taken from github.com/mjibson/goread
  12. var dateFormats = []string{
  13. time.RFC822, // RSS
  14. time.RFC822Z, // RSS
  15. time.RFC3339, // Atom
  16. time.UnixDate,
  17. time.RubyDate,
  18. time.RFC850,
  19. time.RFC1123Z,
  20. time.RFC1123,
  21. time.ANSIC,
  22. "Mon, 02 Jan 2006 15:04:05 MST -07:00",
  23. "Mon, January 2, 2006, 3:04 PM MST",
  24. "Mon, January 2 2006 15:04:05 -0700",
  25. "Mon, January 02, 2006, 15:04:05 MST",
  26. "Mon, January 02, 2006 15:04:05 MST",
  27. "Mon, Jan 2, 2006 15:04 MST",
  28. "Mon, Jan 2 2006 15:04 MST",
  29. "Mon, Jan 2 2006 15:04:05 MST",
  30. "Mon, Jan 2, 2006 15:04:05 MST",
  31. "Mon, Jan 2 2006 15:04:05 -700",
  32. "Mon, Jan 2 2006 15:04:05 -0700",
  33. "Mon Jan 2 15:04 2006",
  34. "Mon Jan 2 15:04:05 2006 MST",
  35. "Mon Jan 02, 2006 3:04 pm",
  36. "Mon, Jan 02,2006 15:04:05 MST",
  37. "Mon Jan 02 2006 15:04:05 -0700",
  38. "Mon, 02/01/2006",
  39. "Monday, 2. January 2006 - 15:04",
  40. "Monday 02 January 2006",
  41. "Monday, January 2, 2006 15:04:05 MST",
  42. "Monday, January 2, 2006 03:04 PM",
  43. "Monday, January 2, 2006",
  44. "Monday, January 02, 2006",
  45. "Monday, 2 January 2006 15:04:05 MST",
  46. "Monday, 2 January 2006 15:04:05 -0700",
  47. "Monday, 2 Jan 2006 15:04:05 MST",
  48. "Monday, 2 Jan 2006 15:04:05 -0700",
  49. "Monday, 02 January 2006 15:04:05 MST",
  50. "Monday, 02 January 2006 15:04:05 -0700",
  51. "Monday, 02 January 2006 15:04:05",
  52. "Monday, January 02, 2006 - 3:04pm",
  53. "Monday, January 2, 2006 - 3:04pm",
  54. "Mon, 01/02/2006 - 15:04",
  55. "Mon, 2 January 2006 15:04 MST",
  56. "Mon, 2 January 2006, 15:04 -0700",
  57. "Mon, 2 January 2006, 15:04:05 MST",
  58. "Mon, 2 January 2006 15:04:05 MST",
  59. "Mon, 2 January 2006 15:04:05 -0700",
  60. "Mon, 2 January 2006",
  61. "Mon, 2 Jan 2006 3:04:05 PM -0700",
  62. "Mon, 2 Jan 2006 15:4:5 MST",
  63. "Mon, 2 Jan 2006 15:4:5 -0700 GMT",
  64. "Mon, 2, Jan 2006 15:4",
  65. "Mon, 2 Jan 2006 15:04 MST",
  66. "Mon, 2 Jan 2006, 15:04 -0700",
  67. "Mon, 2 Jan 2006 15:04 -0700",
  68. "Mon, 2 Jan 2006 15:04:05 UT",
  69. "Mon, 2 Jan 2006 15:04:05MST",
  70. "Mon, 2 Jan 2006 15:04:05 MST",
  71. "Mon 2 Jan 2006 15:04:05 MST",
  72. "mon,2 Jan 2006 15:04:05 MST",
  73. "Mon, 2 Jan 2006 15:04:05 -0700 MST",
  74. "Mon, 2 Jan 2006 15:04:05-0700",
  75. "Mon, 2 Jan 2006 15:04:05 -0700",
  76. "Mon, 2 Jan 2006 15:04:05",
  77. "Mon, 2 Jan 2006 15:04",
  78. "Mon, 02 Jan 2006, 15:04",
  79. "Mon, 2 Jan 2006, 15:04",
  80. "Mon,2 Jan 2006",
  81. "Mon, 2 Jan 2006",
  82. "Mon, 2 Jan 15:04:05 MST",
  83. "Mon, 2 Jan 06 15:04:05 MST",
  84. "Mon, 2 Jan 06 15:04:05 -0700",
  85. "Mon, 2006-01-02 15:04",
  86. "Mon,02 January 2006 14:04:05 MST",
  87. "Mon, 02 January 2006",
  88. "Mon, 02 Jan 2006 3:04:05 PM MST",
  89. "Mon, 02 Jan 2006 15 -0700",
  90. "Mon,02 Jan 2006 15:04 MST",
  91. "Mon, 02 Jan 2006 15:04 MST",
  92. "Mon, 02 Jan 2006 15:04 -0700",
  93. "Mon, 02 Jan 2006 15:04:05 Z",
  94. "Mon, 02 Jan 2006 15:04:05 UT",
  95. "Mon, 02 Jan 2006 15:04:05 MST-07:00",
  96. "Mon, 02 Jan 2006 15:04:05 MST -0700",
  97. "Mon, 02 Jan 2006, 15:04:05 MST",
  98. "Mon, 02 Jan 2006 15:04:05MST",
  99. "Mon, 02 Jan 2006 15:04:05 MST",
  100. "Mon , 02 Jan 2006 15:04:05 MST",
  101. "Mon, 02 Jan 2006 15:04:05 GMT-0700",
  102. "Mon,02 Jan 2006 15:04:05 -0700",
  103. "Mon, 02 Jan 2006 15:04:05 -0700",
  104. "Mon, 02 Jan 2006 15:04:05 -07:00",
  105. "Mon, 02 Jan 2006 15:04:05 --0700",
  106. "Mon 02 Jan 2006 15:04:05 -0700",
  107. "Mon 02 Jan 2006, 15:04:05 MST",
  108. "Mon, 02 Jan 2006 15:04:05 MST",
  109. "Mon, 02 Jan 2006 15:04:05 -07",
  110. "Mon, 02 Jan 2006 15:04:05 00",
  111. "Mon, 02 Jan 2006 15:04:05",
  112. "Mon, 02 Jan 2006",
  113. "Mon, 02 Jan 06 15:04:05 MST",
  114. "Mon, 02 Jan 2006 3:04 PM MST",
  115. "Mon Jan 02 2006 15:04:05 MST",
  116. "Mon, 01 02 2006 15:04:05 -0700",
  117. "Mon, 2th Jan 2006 15:05:05 MST",
  118. "Jan. 2, 2006, 3:04 a.m.",
  119. "fri, 02 jan 2006 15:04:05 -0700",
  120. "January 02 2006 03:04:05 PM",
  121. "January 2, 2006 3:04 PM",
  122. "January 2, 2006, 3:04 p.m.",
  123. "January 2, 2006 15:04:05 MST",
  124. "January 2, 2006 15:04:05",
  125. "January 2, 2006 03:04 PM",
  126. "January 2, 2006",
  127. "January 02, 2006 15:04:05 MST",
  128. "January 02, 2006 15:04",
  129. "January 02, 2006 03:04 PM",
  130. "January 02, 2006",
  131. "Jan 2, 2006 3:04:05 PM MST",
  132. "Jan 2, 2006 3:04:05 PM",
  133. "Jan 2, 2006 15:04:05 MST",
  134. "Jan 2, 2006",
  135. "Jan 02 2006 03:04:05PM",
  136. "Jan 02, 2006",
  137. "6/1/2 15:04",
  138. "6-1-2 15:04",
  139. "2 January 2006 15:04:05 MST",
  140. "2 January 2006 15:04:05 -0700",
  141. "2 January 2006",
  142. "2 Jan 2006 15:04:05 Z",
  143. "2 Jan 2006 15:04:05 MST",
  144. "2 Jan 2006 15:04:05 -0700",
  145. "2 Jan 2006",
  146. "2 Jan 2006 15:04 MST",
  147. "2.1.2006 15:04:05",
  148. "2/1/2006",
  149. "2-1-2006",
  150. "2006 January 02",
  151. "2006-1-2T15:04:05Z",
  152. "2006-1-2 15:04:05",
  153. "2006-1-2",
  154. "2006-01-02T15:04:05-07:00Z",
  155. "2006-1-02T15:04:05Z",
  156. "2006-01-02T15:04Z",
  157. "2006-01-02T15:04-07:00",
  158. "2006-01-02T15:04:05Z",
  159. "2006-01-02T15:04:05-07:00:00",
  160. "2006-01-02T15:04:05:-0700",
  161. "2006-01-02T15:04:05-0700",
  162. "2006-01-02T15:04:05-07:00",
  163. "2006-01-02T15:04:05 -0700",
  164. "2006-01-02T15:04:05:00",
  165. "2006-01-02T15:04:05",
  166. "2006-01-02T15:04",
  167. "2006-01-02 at 15:04:05",
  168. "2006-01-02 15:04:05Z",
  169. "2006-01-02 15:04:05 MST",
  170. "2006-01-02 15:04:05-0700",
  171. "2006-01-02 15:04:05-07:00",
  172. "2006-01-02 15:04:05 -0700",
  173. "2006-01-02 15:04",
  174. "2006-01-02 00:00:00.0 15:04:05.0 -0700",
  175. "2006/01/02",
  176. "2006-01-02",
  177. "15:04 02.01.2006 -0700",
  178. "1/2/2006 3:04 PM MST",
  179. "1/2/2006 3:04:05 PM MST",
  180. "1/2/2006 3:04:05 PM",
  181. "1/2/2006 15:04:05 MST",
  182. "1/2/2006",
  183. "06/1/2 15:04",
  184. "06-1-2 15:04",
  185. "02 Monday, Jan 2006 15:04",
  186. "02 Jan 2006 15:04 MST",
  187. "02 Jan 2006 15:04:05 UT",
  188. "02 Jan 2006 15:04:05 MST",
  189. "02 Jan 2006 15:04:05 -0700",
  190. "02 Jan 2006 15:04:05",
  191. "02 Jan 2006",
  192. "02/01/2006 15:04 MST",
  193. "02-01-2006 15:04:05 MST",
  194. "02.01.2006 15:04:05",
  195. "02/01/2006 15:04:05",
  196. "02.01.2006 15:04",
  197. "02/01/2006 - 15:04",
  198. "02.01.2006 -0700",
  199. "02/01/2006",
  200. "02-01-2006",
  201. "01/02/2006 3:04 PM",
  202. "01/02/2006 15:04:05 MST",
  203. "01/02/2006 - 15:04",
  204. "01/02/2006",
  205. "01-02-2006",
  206. "Jan. 2006",
  207. "Jan. 2, 2006, 03:04 p.m.",
  208. "2006-01-02 15:04:05 -07:00",
  209. "2 January, 2006",
  210. }
  211. var invalidTimezoneReplacer = strings.NewReplacer(
  212. "Europe/Brussels", "CET",
  213. "GMT+0000 (Coordinated Universal Time)", "GMT",
  214. "GMT-", "GMT -",
  215. )
  216. var invalidLocalizedDateReplacer = strings.NewReplacer(
  217. "Mo,", "Mon,",
  218. "Di,", "Tue,",
  219. "Mi,", "Wed,",
  220. "Do,", "Thu,",
  221. "Fr,", "Fri,",
  222. "Sa,", "Sat,",
  223. "So,", "Sun,",
  224. "Mär ", "Mar ",
  225. "Mai ", "May ",
  226. "Okt ", "Oct ",
  227. "Dez ", "Dec ",
  228. "lun,", "Mon,",
  229. "mar,", "Tue,",
  230. "mer,", "Wed,",
  231. "jeu,", "Thu,",
  232. "ven,", "Fri,",
  233. "sam,", "Sat,",
  234. "dim,", "Sun,",
  235. "lun.", "Mon",
  236. "mar.", "Tue",
  237. "mer.", "Wed",
  238. "jeu.", "Thu",
  239. "ven.", "Fri",
  240. "sam.", "Sat",
  241. "dim.", "Sun",
  242. "Lundi,", "Monday,",
  243. "Mardi,", "Tuesday,",
  244. "Mercredi,", "Wednesday,",
  245. "Jeudi,", "Thursday,",
  246. "Vendredi,", "Friday,",
  247. "Samedi,", "Saturday,",
  248. "Dimanche,", "Sunday,",
  249. "jan.", "January ",
  250. "feb.", "February ",
  251. "mars.", "March ",
  252. "avril.", "April ",
  253. "mai.", "May ",
  254. "juin.", "June ",
  255. "juil.", "July",
  256. "août.", "August",
  257. "sept.", "September",
  258. "oct.", "October",
  259. "nov.", "November",
  260. "dec.", "December",
  261. "déc.", "December",
  262. "janvier ", "January ",
  263. "février ", "February ",
  264. "mars ", "March ",
  265. "avril ", "April ",
  266. "mai ", "May ",
  267. "juin ", "June ",
  268. "juillet ", "July",
  269. "août ", "August",
  270. "septembre ", "September",
  271. "octobre ", "October",
  272. "november ", "November",
  273. "décembre ", "December",
  274. "Janvier", "January",
  275. "Février", "February",
  276. "Mars", "March",
  277. "Avril", "April",
  278. "Mai", "May",
  279. "Juin", "June",
  280. "Juillet", "July",
  281. "Août", "August",
  282. "Septembre", "September",
  283. "Octobre", "October",
  284. "Novembre", "November",
  285. "Décembre", "December",
  286. "avr ", "Apr ",
  287. "mai ", "May ",
  288. "jui ", "Jun ",
  289. "juin ", "June ",
  290. "Thurs,", "Thu,",
  291. "Thur,", "Thu,",
  292. )
  293. // Parse parses a given date string using a large
  294. // list of commonly found feed date formats.
  295. func Parse(rawInput string) (t time.Time, err error) {
  296. timestamp, err := strconv.ParseInt(rawInput, 10, 64)
  297. if err == nil {
  298. return time.Unix(timestamp, 0), nil
  299. }
  300. processedInput := invalidLocalizedDateReplacer.Replace(rawInput)
  301. processedInput = invalidTimezoneReplacer.Replace(processedInput)
  302. processedInput = strings.TrimSpace(processedInput)
  303. if processedInput == "" {
  304. return t, errors.New(`date parser: empty value`)
  305. }
  306. for _, layout := range dateFormats {
  307. switch layout {
  308. case time.RFC822, time.RFC850, time.RFC1123:
  309. if t, err = parseLocalTimeDates(layout, processedInput); err == nil {
  310. return
  311. }
  312. }
  313. if t, err = time.Parse(layout, processedInput); err == nil {
  314. return
  315. }
  316. }
  317. err = fmt.Errorf(`date parser: failed to parse date "%s"`, rawInput)
  318. return
  319. }
  320. // According to Golang documentation:
  321. //
  322. // RFC822, RFC850, and RFC1123 formats should be applied only to local times.
  323. // Applying them to UTC times will use "UTC" as the time zone abbreviation,
  324. // while strictly speaking those RFCs require the use of "GMT" in that case.
  325. func parseLocalTimeDates(layout, ds string) (t time.Time, err error) {
  326. loc := time.UTC
  327. // Workaround for dates that don't use GMT.
  328. if strings.HasSuffix(ds, "PST") || strings.HasSuffix(ds, "PDT") {
  329. loc, _ = time.LoadLocation("America/Los_Angeles")
  330. }
  331. if strings.HasSuffix(ds, "EST") || strings.HasSuffix(ds, "EDT") {
  332. loc, _ = time.LoadLocation("America/New_York")
  333. }
  334. return time.ParseInLocation(layout, ds, loc)
  335. }