parser.go 9.9 KB

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