date_parser.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. package goja
  2. import (
  3. "strings"
  4. )
  5. type date struct {
  6. year, month, day int
  7. hour, min, sec, msec int
  8. timeZoneOffset int // time zone offset in minutes
  9. isLocal bool
  10. }
  11. func skip(s string, c byte) (string, bool) {
  12. if len(s) > 0 && s[0] == c {
  13. return s[1:], true
  14. }
  15. return s, false
  16. }
  17. func skipSpaces(s string) string {
  18. for len(s) > 0 && s[0] == ' ' {
  19. s = s[1:]
  20. }
  21. return s
  22. }
  23. func skipUntil(s string, stopList string) string {
  24. for len(s) > 0 && !strings.ContainsRune(stopList, rune(s[0])) {
  25. s = s[1:]
  26. }
  27. return s
  28. }
  29. func match(s string, lower string) (string, bool) {
  30. if len(s) < len(lower) {
  31. return s, false
  32. }
  33. for i := 0; i < len(lower); i++ {
  34. c1 := s[i]
  35. c2 := lower[i]
  36. if c1 != c2 {
  37. // switch to lower-case; 'a'-'A' is known to be a single bit
  38. c1 |= 'a' - 'A'
  39. if c1 != c2 || c1 < 'a' || c1 > 'z' {
  40. return s, false
  41. }
  42. }
  43. }
  44. return s[len(lower):], true
  45. }
  46. func getDigits(s string, minDigits, maxDigits int) (int, string, bool) {
  47. var i, v int
  48. for i < len(s) && i < maxDigits && s[i] >= '0' && s[i] <= '9' {
  49. v = v*10 + int(s[i]-'0')
  50. i++
  51. }
  52. if i < minDigits {
  53. return 0, s, false
  54. }
  55. return v, s[i:], true
  56. }
  57. func getMilliseconds(s string) (int, string) {
  58. mul, v := 100, 0
  59. if len(s) > 0 && (s[0] == '.' || s[0] == ',') {
  60. const I_START = 1
  61. i := I_START
  62. for i < len(s) && i-I_START < 9 && s[i] >= '0' && s[i] <= '9' {
  63. v += int(s[i]-'0') * mul
  64. mul /= 10
  65. i++
  66. }
  67. if i > I_START {
  68. // only consume the separator if digits are present
  69. return v, s[i:]
  70. }
  71. }
  72. return 0, s
  73. }
  74. // [+-]HH:mm or [+-]HHmm or Z
  75. func getTimeZoneOffset(s string, strict bool) (int, string, bool) {
  76. if len(s) == 0 {
  77. return 0, s, false
  78. }
  79. sign := s[0]
  80. if sign == '+' || sign == '-' {
  81. var hh, mm, v int
  82. var ok bool
  83. t := s[1:]
  84. n := len(t)
  85. if hh, t, ok = getDigits(t, 1, 9); !ok {
  86. return 0, s, false
  87. }
  88. n -= len(t)
  89. if strict && n != 2 && n != 4 {
  90. return 0, s, false
  91. }
  92. for n > 4 {
  93. n -= 2
  94. hh /= 100
  95. }
  96. if n > 2 {
  97. mm = hh % 100
  98. hh = hh / 100
  99. } else if t, ok = skip(t, ':'); ok {
  100. if mm, t, ok = getDigits(t, 2, 2); !ok {
  101. return 0, s, false
  102. }
  103. }
  104. if hh > 23 || mm > 59 {
  105. return 0, s, false
  106. }
  107. v = hh*60 + mm
  108. if sign == '-' {
  109. v = -v
  110. }
  111. return v, t, true
  112. } else if sign == 'Z' {
  113. return 0, s[1:], true
  114. }
  115. return 0, s, false
  116. }
  117. var tzAbbrs = []struct {
  118. nameLower string
  119. offset int
  120. }{
  121. {"gmt", 0}, // Greenwich Mean Time
  122. {"utc", 0}, // Coordinated Universal Time
  123. {"ut", 0}, // Universal Time
  124. {"z", 0}, // Zulu Time
  125. {"edt", -4 * 60}, // Eastern Daylight Time
  126. {"est", -5 * 60}, // Eastern Standard Time
  127. {"cdt", -5 * 60}, // Central Daylight Time
  128. {"cst", -6 * 60}, // Central Standard Time
  129. {"mdt", -6 * 60}, // Mountain Daylight Time
  130. {"mst", -7 * 60}, // Mountain Standard Time
  131. {"pdt", -7 * 60}, // Pacific Daylight Time
  132. {"pst", -8 * 60}, // Pacific Standard Time
  133. {"wet", +0 * 60}, // Western European Time
  134. {"west", +1 * 60}, // Western European Summer Time
  135. {"cet", +1 * 60}, // Central European Time
  136. {"cest", +2 * 60}, // Central European Summer Time
  137. {"eet", +2 * 60}, // Eastern European Time
  138. {"eest", +3 * 60}, // Eastern European Summer Time
  139. }
  140. func getTimeZoneAbbr(s string) (int, string, bool) {
  141. for _, tzAbbr := range tzAbbrs {
  142. if s, ok := match(s, tzAbbr.nameLower); ok {
  143. return tzAbbr.offset, s, true
  144. }
  145. }
  146. return 0, s, false
  147. }
  148. var monthNamesLower = []string{
  149. "jan",
  150. "feb",
  151. "mar",
  152. "apr",
  153. "may",
  154. "jun",
  155. "jul",
  156. "aug",
  157. "sep",
  158. "oct",
  159. "nov",
  160. "dec",
  161. }
  162. func getMonth(s string) (int, string, bool) {
  163. for i, monthNameLower := range monthNamesLower {
  164. if s, ok := match(s, monthNameLower); ok {
  165. return i + 1, s, true
  166. }
  167. }
  168. return 0, s, false
  169. }
  170. func parseDateISOString(s string) (date, bool) {
  171. if len(s) == 0 {
  172. return date{}, false
  173. }
  174. var d = date{month: 1, day: 1}
  175. var ok bool
  176. // year is either yyyy digits or [+-]yyyyyy
  177. sign := s[0]
  178. if sign == '-' || sign == '+' {
  179. s = s[1:]
  180. if d.year, s, ok = getDigits(s, 6, 6); !ok {
  181. return date{}, false
  182. }
  183. if sign == '-' {
  184. if d.year == 0 {
  185. // reject -000000
  186. return date{}, false
  187. }
  188. d.year = -d.year
  189. }
  190. } else if d.year, s, ok = getDigits(s, 4, 4); !ok {
  191. return date{}, false
  192. }
  193. if s, ok = skip(s, '-'); ok {
  194. if d.month, s, ok = getDigits(s, 2, 2); !ok || d.month < 1 {
  195. return date{}, false
  196. }
  197. if s, ok = skip(s, '-'); ok {
  198. if d.day, s, ok = getDigits(s, 2, 2); !ok || d.day < 1 {
  199. return date{}, false
  200. }
  201. }
  202. }
  203. if s, ok = skip(s, 'T'); ok {
  204. if d.hour, s, ok = getDigits(s, 2, 2); !ok {
  205. return date{}, false
  206. }
  207. if s, ok = skip(s, ':'); !ok {
  208. return date{}, false
  209. }
  210. if d.min, s, ok = getDigits(s, 2, 2); !ok {
  211. return date{}, false
  212. }
  213. if s, ok = skip(s, ':'); ok {
  214. if d.sec, s, ok = getDigits(s, 2, 2); !ok {
  215. return date{}, false
  216. }
  217. d.msec, s = getMilliseconds(s)
  218. }
  219. d.isLocal = true
  220. }
  221. // parse the time zone offset if present
  222. if len(s) > 0 {
  223. if d.timeZoneOffset, s, ok = getTimeZoneOffset(s, true); !ok {
  224. return date{}, false
  225. }
  226. d.isLocal = false
  227. }
  228. // error if extraneous characters
  229. return d, len(s) == 0
  230. }
  231. func parseDateOtherString(s string) (date, bool) {
  232. var d = date{
  233. year: 2001,
  234. month: 1,
  235. day: 1,
  236. isLocal: true,
  237. }
  238. var nums [3]int
  239. var numIndex int
  240. var hasYear, hasMon, hasTime, ok bool
  241. for {
  242. s = skipSpaces(s)
  243. if len(s) == 0 {
  244. break
  245. }
  246. c := s[0]
  247. n, val := len(s), 0
  248. if c == '+' || c == '-' {
  249. if hasTime {
  250. if val, s, ok = getTimeZoneOffset(s, false); ok {
  251. d.timeZoneOffset = val
  252. d.isLocal = false
  253. }
  254. }
  255. if !hasTime || !ok {
  256. s = s[1:]
  257. if val, s, ok = getDigits(s, 1, 9); ok {
  258. d.year = val
  259. if c == '-' {
  260. if d.year == 0 {
  261. return date{}, false
  262. }
  263. d.year = -d.year
  264. }
  265. hasYear = true
  266. }
  267. }
  268. } else if val, s, ok = getDigits(s, 1, 9); ok {
  269. if s, ok = skip(s, ':'); ok {
  270. // time part
  271. d.hour = val
  272. if d.min, s, ok = getDigits(s, 1, 2); !ok {
  273. return date{}, false
  274. }
  275. if s, ok = skip(s, ':'); ok {
  276. if d.sec, s, ok = getDigits(s, 1, 2); !ok {
  277. return date{}, false
  278. }
  279. d.msec, s = getMilliseconds(s)
  280. }
  281. hasTime = true
  282. if t := skipSpaces(s); len(t) > 0 {
  283. if t, ok = match(t, "pm"); ok {
  284. if d.hour < 12 {
  285. d.hour += 12
  286. }
  287. s = t
  288. continue
  289. } else if t, ok = match(t, "am"); ok {
  290. if d.hour == 12 {
  291. d.hour = 0
  292. }
  293. s = t
  294. continue
  295. }
  296. }
  297. } else if n-len(s) > 2 {
  298. d.year = val
  299. hasYear = true
  300. } else if val < 1 || val > 31 {
  301. d.year = val
  302. if val < 100 {
  303. d.year += 1900
  304. }
  305. if val < 50 {
  306. d.year += 100
  307. }
  308. hasYear = true
  309. } else {
  310. if numIndex == 3 {
  311. return date{}, false
  312. }
  313. nums[numIndex] = val
  314. numIndex++
  315. }
  316. } else if val, s, ok = getMonth(s); ok {
  317. d.month = val
  318. hasMon = true
  319. s = skipUntil(s, "0123456789 -/(")
  320. } else if val, s, ok = getTimeZoneAbbr(s); ok {
  321. d.timeZoneOffset = val
  322. if len(s) > 0 {
  323. if c := s[0]; (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') {
  324. return date{}, false
  325. }
  326. }
  327. d.isLocal = false
  328. continue
  329. } else if c == '(' {
  330. // skip parenthesized phrase
  331. level := 1
  332. s = s[1:]
  333. for len(s) > 0 && level != 0 {
  334. if s[0] == '(' {
  335. level++
  336. } else if s[0] == ')' {
  337. level--
  338. }
  339. s = s[1:]
  340. }
  341. if level > 0 {
  342. return date{}, false
  343. }
  344. } else if c == ')' {
  345. return date{}, false
  346. } else {
  347. if hasYear || hasMon || hasTime || numIndex > 0 {
  348. return date{}, false
  349. }
  350. // skip a word
  351. s = skipUntil(s, " -/(")
  352. }
  353. for len(s) > 0 && strings.ContainsRune("-/.,", rune(s[0])) {
  354. s = s[1:]
  355. }
  356. }
  357. n := numIndex
  358. if hasYear {
  359. n++
  360. }
  361. if hasMon {
  362. n++
  363. }
  364. if n > 3 {
  365. return date{}, false
  366. }
  367. switch numIndex {
  368. case 0:
  369. if !hasYear {
  370. return date{}, false
  371. }
  372. case 1:
  373. if hasMon {
  374. d.day = nums[0]
  375. } else {
  376. d.month = nums[0]
  377. }
  378. case 2:
  379. if hasYear {
  380. d.month = nums[0]
  381. d.day = nums[1]
  382. } else if hasMon {
  383. d.year = nums[1]
  384. if nums[1] < 100 {
  385. d.year += 1900
  386. }
  387. if nums[1] < 50 {
  388. d.year += 100
  389. }
  390. d.day = nums[0]
  391. } else {
  392. d.month = nums[0]
  393. d.day = nums[1]
  394. }
  395. case 3:
  396. d.year = nums[2]
  397. if nums[2] < 100 {
  398. d.year += 1900
  399. }
  400. if nums[2] < 50 {
  401. d.year += 100
  402. }
  403. d.month = nums[0]
  404. d.day = nums[1]
  405. default:
  406. return date{}, false
  407. }
  408. return d, d.month > 0 && d.day > 0
  409. }