builtin_date.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. package otto
  2. import (
  3. "math"
  4. "time"
  5. )
  6. // Date
  7. const (
  8. // TODO Be like V8?
  9. // builtinDateDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)".
  10. builtinDateDateTimeLayout = time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
  11. builtinDateDateLayout = "Mon, 02 Jan 2006"
  12. builtinDateTimeLayout = "15:04:05 MST"
  13. )
  14. // utcTimeZone is the time zone used for UTC calculations.
  15. // It is GMT not UTC as that's what Javascript does because toUTCString is
  16. // actually an alias to toGMTString.
  17. var utcTimeZone = time.FixedZone("GMT", 0)
  18. func builtinDate(call FunctionCall) Value {
  19. date := &dateObject{}
  20. date.Set(newDateTime([]Value{}, time.Local)) //nolint:gosmopolitan
  21. return stringValue(date.Time().Format(builtinDateDateTimeLayout))
  22. }
  23. func builtinNewDate(obj *object, argumentList []Value) Value {
  24. return objectValue(obj.runtime.newDate(newDateTime(argumentList, time.Local))) //nolint:gosmopolitan
  25. }
  26. func builtinDateToString(call FunctionCall) Value {
  27. date := dateObjectOf(call.runtime, call.thisObject())
  28. if date.isNaN {
  29. return stringValue("Invalid Date")
  30. }
  31. return stringValue(date.Time().Local().Format(builtinDateDateTimeLayout)) //nolint:gosmopolitan
  32. }
  33. func builtinDateToDateString(call FunctionCall) Value {
  34. date := dateObjectOf(call.runtime, call.thisObject())
  35. if date.isNaN {
  36. return stringValue("Invalid Date")
  37. }
  38. return stringValue(date.Time().Local().Format(builtinDateDateLayout)) //nolint:gosmopolitan
  39. }
  40. func builtinDateToTimeString(call FunctionCall) Value {
  41. date := dateObjectOf(call.runtime, call.thisObject())
  42. if date.isNaN {
  43. return stringValue("Invalid Date")
  44. }
  45. return stringValue(date.Time().Local().Format(builtinDateTimeLayout)) //nolint:gosmopolitan
  46. }
  47. func builtinDateToUTCString(call FunctionCall) Value {
  48. date := dateObjectOf(call.runtime, call.thisObject())
  49. if date.isNaN {
  50. return stringValue("Invalid Date")
  51. }
  52. return stringValue(date.Time().In(utcTimeZone).Format(builtinDateDateTimeLayout))
  53. }
  54. func builtinDateToISOString(call FunctionCall) Value {
  55. date := dateObjectOf(call.runtime, call.thisObject())
  56. if date.isNaN {
  57. return stringValue("Invalid Date")
  58. }
  59. return stringValue(date.Time().Format("2006-01-02T15:04:05.000Z"))
  60. }
  61. func builtinDateToJSON(call FunctionCall) Value {
  62. obj := call.thisObject()
  63. value := obj.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
  64. // FIXME fv.isFinite
  65. if fv := value.float64(); math.IsNaN(fv) || math.IsInf(fv, 0) {
  66. return nullValue
  67. }
  68. toISOString := obj.get("toISOString")
  69. if !toISOString.isCallable() {
  70. // FIXME
  71. panic(call.runtime.panicTypeError("Date.toJSON toISOString %q is not callable", toISOString))
  72. }
  73. return toISOString.call(call.runtime, objectValue(obj), []Value{})
  74. }
  75. func builtinDateToGMTString(call FunctionCall) Value {
  76. date := dateObjectOf(call.runtime, call.thisObject())
  77. if date.isNaN {
  78. return stringValue("Invalid Date")
  79. }
  80. return stringValue(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
  81. }
  82. func builtinDateGetTime(call FunctionCall) Value {
  83. date := dateObjectOf(call.runtime, call.thisObject())
  84. if date.isNaN {
  85. return NaNValue()
  86. }
  87. // We do this (convert away from a float) so the user
  88. // does not get something back in exponential notation
  89. return int64Value(date.Epoch())
  90. }
  91. func builtinDateSetTime(call FunctionCall) Value {
  92. obj := call.thisObject()
  93. date := dateObjectOf(call.runtime, call.thisObject())
  94. date.Set(call.Argument(0).float64())
  95. obj.value = date
  96. return date.Value()
  97. }
  98. func builtinDateBeforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*object, *dateObject, *ecmaTime, []int) {
  99. obj := call.thisObject()
  100. date := dateObjectOf(call.runtime, call.thisObject())
  101. if date.isNaN {
  102. return nil, nil, nil, nil
  103. }
  104. if argumentLimit > len(call.ArgumentList) {
  105. argumentLimit = len(call.ArgumentList)
  106. }
  107. if argumentLimit == 0 {
  108. obj.value = invalidDateObject
  109. return nil, nil, nil, nil
  110. }
  111. valueList := make([]int, argumentLimit)
  112. for index := range argumentLimit {
  113. value := call.ArgumentList[index]
  114. nm := value.number()
  115. switch nm.kind {
  116. case numberInteger, numberFloat:
  117. default:
  118. obj.value = invalidDateObject
  119. return nil, nil, nil, nil
  120. }
  121. valueList[index] = int(nm.int64)
  122. }
  123. baseTime := date.Time()
  124. if timeLocal {
  125. baseTime = baseTime.Local() //nolint:gosmopolitan
  126. }
  127. ecmaTime := newEcmaTime(baseTime)
  128. return obj, &date, &ecmaTime, valueList
  129. }
  130. func builtinDateParse(call FunctionCall) Value {
  131. date := call.Argument(0).string()
  132. return float64Value(dateParse(date))
  133. }
  134. func builtinDateUTC(call FunctionCall) Value {
  135. return float64Value(newDateTime(call.ArgumentList, time.UTC))
  136. }
  137. func builtinDateNow(call FunctionCall) Value {
  138. call.ArgumentList = []Value(nil)
  139. return builtinDateUTC(call)
  140. }
  141. // This is a placeholder.
  142. func builtinDateToLocaleString(call FunctionCall) Value {
  143. date := dateObjectOf(call.runtime, call.thisObject())
  144. if date.isNaN {
  145. return stringValue("Invalid Date")
  146. }
  147. return stringValue(date.Time().Local().Format("2006-01-02 15:04:05")) //nolint:gosmopolitan
  148. }
  149. // This is a placeholder.
  150. func builtinDateToLocaleDateString(call FunctionCall) Value {
  151. date := dateObjectOf(call.runtime, call.thisObject())
  152. if date.isNaN {
  153. return stringValue("Invalid Date")
  154. }
  155. return stringValue(date.Time().Local().Format("2006-01-02")) //nolint:gosmopolitan
  156. }
  157. // This is a placeholder.
  158. func builtinDateToLocaleTimeString(call FunctionCall) Value {
  159. date := dateObjectOf(call.runtime, call.thisObject())
  160. if date.isNaN {
  161. return stringValue("Invalid Date")
  162. }
  163. return stringValue(date.Time().Local().Format("15:04:05")) //nolint:gosmopolitan
  164. }
  165. func builtinDateValueOf(call FunctionCall) Value {
  166. date := dateObjectOf(call.runtime, call.thisObject())
  167. if date.isNaN {
  168. return NaNValue()
  169. }
  170. return date.Value()
  171. }
  172. func builtinDateGetYear(call FunctionCall) Value {
  173. // Will throw a TypeError is ThisObject is nil or
  174. // does not have Class of "Date"
  175. date := dateObjectOf(call.runtime, call.thisObject())
  176. if date.isNaN {
  177. return NaNValue()
  178. }
  179. return intValue(date.Time().Local().Year() - 1900) //nolint:gosmopolitan
  180. }
  181. func builtinDateGetFullYear(call FunctionCall) Value {
  182. // Will throw a TypeError is ThisObject is nil or
  183. // does not have Class of "Date"
  184. date := dateObjectOf(call.runtime, call.thisObject())
  185. if date.isNaN {
  186. return NaNValue()
  187. }
  188. return intValue(date.Time().Local().Year()) //nolint:gosmopolitan
  189. }
  190. func builtinDateGetUTCFullYear(call FunctionCall) Value {
  191. date := dateObjectOf(call.runtime, call.thisObject())
  192. if date.isNaN {
  193. return NaNValue()
  194. }
  195. return intValue(date.Time().Year())
  196. }
  197. func builtinDateGetMonth(call FunctionCall) Value {
  198. date := dateObjectOf(call.runtime, call.thisObject())
  199. if date.isNaN {
  200. return NaNValue()
  201. }
  202. return intValue(dateFromGoMonth(date.Time().Local().Month())) //nolint:gosmopolitan
  203. }
  204. func builtinDateGetUTCMonth(call FunctionCall) Value {
  205. date := dateObjectOf(call.runtime, call.thisObject())
  206. if date.isNaN {
  207. return NaNValue()
  208. }
  209. return intValue(dateFromGoMonth(date.Time().Month()))
  210. }
  211. func builtinDateGetDate(call FunctionCall) Value {
  212. date := dateObjectOf(call.runtime, call.thisObject())
  213. if date.isNaN {
  214. return NaNValue()
  215. }
  216. return intValue(date.Time().Local().Day()) //nolint:gosmopolitan
  217. }
  218. func builtinDateGetUTCDate(call FunctionCall) Value {
  219. date := dateObjectOf(call.runtime, call.thisObject())
  220. if date.isNaN {
  221. return NaNValue()
  222. }
  223. return intValue(date.Time().Day())
  224. }
  225. func builtinDateGetDay(call FunctionCall) Value {
  226. // Actually day of the week
  227. date := dateObjectOf(call.runtime, call.thisObject())
  228. if date.isNaN {
  229. return NaNValue()
  230. }
  231. return intValue(dateFromGoDay(date.Time().Local().Weekday())) //nolint:gosmopolitan
  232. }
  233. func builtinDateGetUTCDay(call FunctionCall) Value {
  234. date := dateObjectOf(call.runtime, call.thisObject())
  235. if date.isNaN {
  236. return NaNValue()
  237. }
  238. return intValue(dateFromGoDay(date.Time().Weekday()))
  239. }
  240. func builtinDateGetHours(call FunctionCall) Value {
  241. date := dateObjectOf(call.runtime, call.thisObject())
  242. if date.isNaN {
  243. return NaNValue()
  244. }
  245. return intValue(date.Time().Local().Hour()) //nolint:gosmopolitan
  246. }
  247. func builtinDateGetUTCHours(call FunctionCall) Value {
  248. date := dateObjectOf(call.runtime, call.thisObject())
  249. if date.isNaN {
  250. return NaNValue()
  251. }
  252. return intValue(date.Time().Hour())
  253. }
  254. func builtinDateGetMinutes(call FunctionCall) Value {
  255. date := dateObjectOf(call.runtime, call.thisObject())
  256. if date.isNaN {
  257. return NaNValue()
  258. }
  259. return intValue(date.Time().Local().Minute()) //nolint:gosmopolitan
  260. }
  261. func builtinDateGetUTCMinutes(call FunctionCall) Value {
  262. date := dateObjectOf(call.runtime, call.thisObject())
  263. if date.isNaN {
  264. return NaNValue()
  265. }
  266. return intValue(date.Time().Minute())
  267. }
  268. func builtinDateGetSeconds(call FunctionCall) Value {
  269. date := dateObjectOf(call.runtime, call.thisObject())
  270. if date.isNaN {
  271. return NaNValue()
  272. }
  273. return intValue(date.Time().Local().Second()) //nolint:gosmopolitan
  274. }
  275. func builtinDateGetUTCSeconds(call FunctionCall) Value {
  276. date := dateObjectOf(call.runtime, call.thisObject())
  277. if date.isNaN {
  278. return NaNValue()
  279. }
  280. return intValue(date.Time().Second())
  281. }
  282. func builtinDateGetMilliseconds(call FunctionCall) Value {
  283. date := dateObjectOf(call.runtime, call.thisObject())
  284. if date.isNaN {
  285. return NaNValue()
  286. }
  287. return intValue(date.Time().Local().Nanosecond() / (100 * 100 * 100)) //nolint:gosmopolitan
  288. }
  289. func builtinDateGetUTCMilliseconds(call FunctionCall) Value {
  290. date := dateObjectOf(call.runtime, call.thisObject())
  291. if date.isNaN {
  292. return NaNValue()
  293. }
  294. return intValue(date.Time().Nanosecond() / (100 * 100 * 100))
  295. }
  296. func builtinDateGetTimezoneOffset(call FunctionCall) Value {
  297. date := dateObjectOf(call.runtime, call.thisObject())
  298. if date.isNaN {
  299. return NaNValue()
  300. }
  301. timeLocal := date.Time().Local() //nolint:gosmopolitan
  302. // Is this kosher?
  303. timeLocalAsUTC := time.Date(
  304. timeLocal.Year(),
  305. timeLocal.Month(),
  306. timeLocal.Day(),
  307. timeLocal.Hour(),
  308. timeLocal.Minute(),
  309. timeLocal.Second(),
  310. timeLocal.Nanosecond(),
  311. time.UTC,
  312. )
  313. return float64Value(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
  314. }
  315. func builtinDateSetMilliseconds(call FunctionCall) Value {
  316. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
  317. if ecmaTime == nil {
  318. return NaNValue()
  319. }
  320. ecmaTime.millisecond = value[0]
  321. date.SetTime(ecmaTime.goTime())
  322. obj.value = *date
  323. return date.Value()
  324. }
  325. func builtinDateSetUTCMilliseconds(call FunctionCall) Value {
  326. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, false)
  327. if ecmaTime == nil {
  328. return NaNValue()
  329. }
  330. ecmaTime.millisecond = value[0]
  331. date.SetTime(ecmaTime.goTime())
  332. obj.value = *date
  333. return date.Value()
  334. }
  335. func builtinDateSetSeconds(call FunctionCall) Value {
  336. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, true)
  337. if ecmaTime == nil {
  338. return NaNValue()
  339. }
  340. if len(value) > 1 {
  341. ecmaTime.millisecond = value[1]
  342. }
  343. ecmaTime.second = value[0]
  344. date.SetTime(ecmaTime.goTime())
  345. obj.value = *date
  346. return date.Value()
  347. }
  348. func builtinDateSetUTCSeconds(call FunctionCall) Value {
  349. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, false)
  350. if ecmaTime == nil {
  351. return NaNValue()
  352. }
  353. if len(value) > 1 {
  354. ecmaTime.millisecond = value[1]
  355. }
  356. ecmaTime.second = value[0]
  357. date.SetTime(ecmaTime.goTime())
  358. obj.value = *date
  359. return date.Value()
  360. }
  361. func builtinDateSetMinutes(call FunctionCall) Value {
  362. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, true)
  363. if ecmaTime == nil {
  364. return NaNValue()
  365. }
  366. if len(value) > 2 {
  367. ecmaTime.millisecond = value[2]
  368. ecmaTime.second = value[1]
  369. } else if len(value) > 1 {
  370. ecmaTime.second = value[1]
  371. }
  372. ecmaTime.minute = value[0]
  373. date.SetTime(ecmaTime.goTime())
  374. obj.value = *date
  375. return date.Value()
  376. }
  377. func builtinDateSetUTCMinutes(call FunctionCall) Value {
  378. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, false)
  379. if ecmaTime == nil {
  380. return NaNValue()
  381. }
  382. if len(value) > 2 {
  383. ecmaTime.millisecond = value[2]
  384. ecmaTime.second = value[1]
  385. } else if len(value) > 1 {
  386. ecmaTime.second = value[1]
  387. }
  388. ecmaTime.minute = value[0]
  389. date.SetTime(ecmaTime.goTime())
  390. obj.value = *date
  391. return date.Value()
  392. }
  393. func builtinDateSetHours(call FunctionCall) Value {
  394. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 4, true)
  395. if ecmaTime == nil {
  396. return NaNValue()
  397. }
  398. switch {
  399. case len(value) > 3:
  400. ecmaTime.millisecond = value[3]
  401. fallthrough
  402. case len(value) > 2:
  403. ecmaTime.second = value[2]
  404. fallthrough
  405. case len(value) > 1:
  406. ecmaTime.minute = value[1]
  407. }
  408. ecmaTime.hour = value[0]
  409. date.SetTime(ecmaTime.goTime())
  410. obj.value = *date
  411. return date.Value()
  412. }
  413. func builtinDateSetUTCHours(call FunctionCall) Value {
  414. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 4, false)
  415. if ecmaTime == nil {
  416. return NaNValue()
  417. }
  418. switch {
  419. case len(value) > 3:
  420. ecmaTime.millisecond = value[3]
  421. fallthrough
  422. case len(value) > 2:
  423. ecmaTime.second = value[2]
  424. fallthrough
  425. case len(value) > 1:
  426. ecmaTime.minute = value[1]
  427. }
  428. ecmaTime.hour = value[0]
  429. date.SetTime(ecmaTime.goTime())
  430. obj.value = *date
  431. return date.Value()
  432. }
  433. func builtinDateSetDate(call FunctionCall) Value {
  434. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
  435. if ecmaTime == nil {
  436. return NaNValue()
  437. }
  438. ecmaTime.day = value[0]
  439. date.SetTime(ecmaTime.goTime())
  440. obj.value = *date
  441. return date.Value()
  442. }
  443. func builtinDateSetUTCDate(call FunctionCall) Value {
  444. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, false)
  445. if ecmaTime == nil {
  446. return NaNValue()
  447. }
  448. ecmaTime.day = value[0]
  449. date.SetTime(ecmaTime.goTime())
  450. obj.value = *date
  451. return date.Value()
  452. }
  453. func builtinDateSetMonth(call FunctionCall) Value {
  454. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, true)
  455. if ecmaTime == nil {
  456. return NaNValue()
  457. }
  458. if len(value) > 1 {
  459. ecmaTime.day = value[1]
  460. }
  461. ecmaTime.month = value[0]
  462. date.SetTime(ecmaTime.goTime())
  463. obj.value = *date
  464. return date.Value()
  465. }
  466. func builtinDateSetUTCMonth(call FunctionCall) Value {
  467. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, false)
  468. if ecmaTime == nil {
  469. return NaNValue()
  470. }
  471. if len(value) > 1 {
  472. ecmaTime.day = value[1]
  473. }
  474. ecmaTime.month = value[0]
  475. date.SetTime(ecmaTime.goTime())
  476. obj.value = *date
  477. return date.Value()
  478. }
  479. func builtinDateSetYear(call FunctionCall) Value {
  480. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
  481. if ecmaTime == nil {
  482. return NaNValue()
  483. }
  484. year := value[0]
  485. if 0 <= year && year <= 99 {
  486. year += 1900
  487. }
  488. ecmaTime.year = year
  489. date.SetTime(ecmaTime.goTime())
  490. obj.value = *date
  491. return date.Value()
  492. }
  493. func builtinDateSetFullYear(call FunctionCall) Value {
  494. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, true)
  495. if ecmaTime == nil {
  496. return NaNValue()
  497. }
  498. if len(value) > 2 {
  499. ecmaTime.day = value[2]
  500. ecmaTime.month = value[1]
  501. } else if len(value) > 1 {
  502. ecmaTime.month = value[1]
  503. }
  504. ecmaTime.year = value[0]
  505. date.SetTime(ecmaTime.goTime())
  506. obj.value = *date
  507. return date.Value()
  508. }
  509. func builtinDateSetUTCFullYear(call FunctionCall) Value {
  510. obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, false)
  511. if ecmaTime == nil {
  512. return NaNValue()
  513. }
  514. if len(value) > 2 {
  515. ecmaTime.day = value[2]
  516. ecmaTime.month = value[1]
  517. } else if len(value) > 1 {
  518. ecmaTime.month = value[1]
  519. }
  520. ecmaTime.year = value[0]
  521. date.SetTime(ecmaTime.goTime())
  522. obj.value = *date
  523. return date.Value()
  524. }
  525. // toUTCString
  526. // toISOString
  527. // toJSONString
  528. // toJSON