4 * Unlike most built-ins, Date has some platform dependencies for getting
5 * UTC time, converting between UTC and local time, and parsing and
6 * formatting time values. These are all abstracted behind DUK_USE_xxx
7 * config options. There are built-in platform specific providers for
8 * POSIX and Windows, but external providers can also be used.
10 * See doc/datetime.rst.
14 #include "duk_internal.h"
17 * Forward declarations
20 DUK_LOCAL_DECL duk_double_t
duk__push_this_get_timeval_tzoffset(duk_context
*ctx
, duk_small_uint_t flags
, duk_int_t
*out_tzoffset
);
21 DUK_LOCAL_DECL duk_double_t
duk__push_this_get_timeval(duk_context
*ctx
, duk_small_uint_t flags
);
22 DUK_LOCAL_DECL
void duk__twodigit_year_fixup(duk_context
*ctx
, duk_idx_t idx_val
);
23 DUK_LOCAL_DECL duk_ret_t
duk__set_this_timeval_from_dparts(duk_context
*ctx
, duk_double_t
*dparts
, duk_small_uint_t flags
);
26 * Other file level defines
29 /* Debug macro to print all parts and dparts (used manually because of debug level). */
30 #define DUK__DPRINT_PARTS_AND_DPARTS(parts,dparts) do { \
31 DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld, dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \
32 (long) (parts)[0], (long) (parts)[1], \
33 (long) (parts)[2], (long) (parts)[3], \
34 (long) (parts)[4], (long) (parts)[5], \
35 (long) (parts)[6], (long) (parts)[7], \
36 (double) (dparts)[0], (double) (dparts)[1], \
37 (double) (dparts)[2], (double) (dparts)[3], \
38 (double) (dparts)[4], (double) (dparts)[5], \
39 (double) (dparts)[6], (double) (dparts)[7])); \
41 #define DUK__DPRINT_PARTS(parts) do { \
42 DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld", \
43 (long) (parts)[0], (long) (parts)[1], \
44 (long) (parts)[2], (long) (parts)[3], \
45 (long) (parts)[4], (long) (parts)[5], \
46 (long) (parts)[6], (long) (parts)[7])); \
48 #define DUK__DPRINT_DPARTS(dparts) do { \
49 DUK_D(DUK_DPRINT("dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \
50 (double) (dparts)[0], (double) (dparts)[1], \
51 (double) (dparts)[2], (double) (dparts)[3], \
52 (double) (dparts)[4], (double) (dparts)[5], \
53 (double) (dparts)[6], (double) (dparts)[7])); \
56 /* Equivalent year for DST calculations outside [1970,2038[ range, see
57 * E5 Section 15.9.1.8. Equivalent year has the same leap-year-ness and
58 * starts with the same weekday on Jan 1.
59 * https://bugzilla.mozilla.org/show_bug.cgi?id=351066
61 #define DUK__YEAR(x) ((duk_uint8_t) ((x) - 1970))
62 DUK_LOCAL duk_uint8_t duk__date_equivyear
[14] = {
64 /* This is based on V8 EquivalentYear() algorithm (see src/genequivyear.py):
65 * http://code.google.com/p/v8/source/browse/trunk/src/date.h#146
68 /* non-leap year: sunday, monday, ... */
69 DUK__YEAR(2023), DUK__YEAR(2035), DUK__YEAR(2019), DUK__YEAR(2031),
70 DUK__YEAR(2015), DUK__YEAR(2027), DUK__YEAR(2011),
72 /* leap year: sunday, monday, ... */
73 DUK__YEAR(2012), DUK__YEAR(2024), DUK__YEAR(2008), DUK__YEAR(2020),
74 DUK__YEAR(2032), DUK__YEAR(2016), DUK__YEAR(2028)
78 /* This is based on Rhino EquivalentYear() algorithm:
79 * https://github.com/mozilla/rhino/blob/f99cc11d616f0cdda2c42bde72b3484df6182947/src/org/mozilla/javascript/NativeDate.java
82 /* non-leap year: sunday, monday, ... */
83 DUK__YEAR(1978), DUK__YEAR(1973), DUK__YEAR(1985), DUK__YEAR(1986),
84 DUK__YEAR(1981), DUK__YEAR(1971), DUK__YEAR(1977),
86 /* leap year: sunday, monday, ... */
87 DUK__YEAR(1984), DUK__YEAR(1996), DUK__YEAR(1980), DUK__YEAR(1992),
88 DUK__YEAR(1976), DUK__YEAR(1988), DUK__YEAR(1972)
94 * ISO 8601 subset parser.
97 /* Parser part count. */
98 #define DUK__NUM_ISO8601_PARSER_PARTS 9
100 /* Parser part indices. */
101 #define DUK__PI_YEAR 0
102 #define DUK__PI_MONTH 1
103 #define DUK__PI_DAY 2
104 #define DUK__PI_HOUR 3
105 #define DUK__PI_MINUTE 4
106 #define DUK__PI_SECOND 5
107 #define DUK__PI_MILLISECOND 6
108 #define DUK__PI_TZHOUR 7
109 #define DUK__PI_TZMINUTE 8
111 /* Parser part masks. */
112 #define DUK__PM_YEAR (1 << DUK__PI_YEAR)
113 #define DUK__PM_MONTH (1 << DUK__PI_MONTH)
114 #define DUK__PM_DAY (1 << DUK__PI_DAY)
115 #define DUK__PM_HOUR (1 << DUK__PI_HOUR)
116 #define DUK__PM_MINUTE (1 << DUK__PI_MINUTE)
117 #define DUK__PM_SECOND (1 << DUK__PI_SECOND)
118 #define DUK__PM_MILLISECOND (1 << DUK__PI_MILLISECOND)
119 #define DUK__PM_TZHOUR (1 << DUK__PI_TZHOUR)
120 #define DUK__PM_TZMINUTE (1 << DUK__PI_TZMINUTE)
122 /* Parser separator indices. */
123 #define DUK__SI_PLUS 0
124 #define DUK__SI_MINUS 1
126 #define DUK__SI_SPACE 3
127 #define DUK__SI_COLON 4
128 #define DUK__SI_PERIOD 5
130 #define DUK__SI_NUL 7
132 /* Parser separator masks. */
133 #define DUK__SM_PLUS (1 << DUK__SI_PLUS)
134 #define DUK__SM_MINUS (1 << DUK__SI_MINUS)
135 #define DUK__SM_T (1 << DUK__SI_T)
136 #define DUK__SM_SPACE (1 << DUK__SI_SPACE)
137 #define DUK__SM_COLON (1 << DUK__SI_COLON)
138 #define DUK__SM_PERIOD (1 << DUK__SI_PERIOD)
139 #define DUK__SM_Z (1 << DUK__SI_Z)
140 #define DUK__SM_NUL (1 << DUK__SI_NUL)
142 /* Rule control flags. */
143 #define DUK__CF_NEG (1 << 0) /* continue matching, set neg_tzoffset flag */
144 #define DUK__CF_ACCEPT (1 << 1) /* accept string */
145 #define DUK__CF_ACCEPT_NUL (1 << 2) /* accept string if next char is NUL (otherwise reject) */
147 #define DUK__PACK_RULE(partmask,sepmask,nextpart,flags) \
148 ((duk_uint32_t) (partmask) + \
149 (((duk_uint32_t) (sepmask)) << 9) + \
150 (((duk_uint32_t) (nextpart)) << 17) + \
151 (((duk_uint32_t) (flags)) << 21))
153 #define DUK__UNPACK_RULE(rule,var_nextidx,var_flags) do { \
154 (var_nextidx) = (duk_small_uint_t) (((rule) >> 17) & 0x0f); \
155 (var_flags) = (duk_small_uint_t) ((rule) >> 21); \
158 #define DUK__RULE_MASK_PART_SEP 0x1ffffUL
160 /* Matching separator index is used in the control table */
161 DUK_LOCAL
const duk_uint8_t duk__parse_iso8601_seps
[] = {
162 DUK_ASC_PLUS
/*0*/, DUK_ASC_MINUS
/*1*/, DUK_ASC_UC_T
/*2*/, DUK_ASC_SPACE
/*3*/,
163 DUK_ASC_COLON
/*4*/, DUK_ASC_PERIOD
/*5*/, DUK_ASC_UC_Z
/*6*/, DUK_ASC_NUL
/*7*/
166 /* Rule table: first matching rule is used to determine what to do next. */
167 DUK_LOCAL
const duk_uint32_t duk__parse_iso8601_control
[] = {
168 DUK__PACK_RULE(DUK__PM_YEAR
, DUK__SM_MINUS
, DUK__PI_MONTH
, 0),
169 DUK__PACK_RULE(DUK__PM_MONTH
, DUK__SM_MINUS
, DUK__PI_DAY
, 0),
170 DUK__PACK_RULE(DUK__PM_YEAR
| DUK__PM_MONTH
| DUK__PM_DAY
, DUK__SM_T
| DUK__SM_SPACE
, DUK__PI_HOUR
, 0),
171 DUK__PACK_RULE(DUK__PM_HOUR
, DUK__SM_COLON
, DUK__PI_MINUTE
, 0),
172 DUK__PACK_RULE(DUK__PM_MINUTE
, DUK__SM_COLON
, DUK__PI_SECOND
, 0),
173 DUK__PACK_RULE(DUK__PM_SECOND
, DUK__SM_PERIOD
, DUK__PI_MILLISECOND
, 0),
174 DUK__PACK_RULE(DUK__PM_TZHOUR
, DUK__SM_COLON
, DUK__PI_TZMINUTE
, 0),
175 DUK__PACK_RULE(DUK__PM_YEAR
| DUK__PM_MONTH
| DUK__PM_DAY
| DUK__PM_HOUR
/*Note1*/ | DUK__PM_MINUTE
| DUK__PM_SECOND
| DUK__PM_MILLISECOND
, DUK__SM_PLUS
, DUK__PI_TZHOUR
, 0),
176 DUK__PACK_RULE(DUK__PM_YEAR
| DUK__PM_MONTH
| DUK__PM_DAY
| DUK__PM_HOUR
/*Note1*/ | DUK__PM_MINUTE
| DUK__PM_SECOND
| DUK__PM_MILLISECOND
, DUK__SM_MINUS
, DUK__PI_TZHOUR
, DUK__CF_NEG
),
177 DUK__PACK_RULE(DUK__PM_YEAR
| DUK__PM_MONTH
| DUK__PM_DAY
| DUK__PM_HOUR
/*Note1*/ | DUK__PM_MINUTE
| DUK__PM_SECOND
| DUK__PM_MILLISECOND
, DUK__SM_Z
, 0, DUK__CF_ACCEPT_NUL
),
178 DUK__PACK_RULE(DUK__PM_YEAR
| DUK__PM_MONTH
| DUK__PM_DAY
| DUK__PM_HOUR
/*Note1*/ | DUK__PM_MINUTE
| DUK__PM_SECOND
| DUK__PM_MILLISECOND
| DUK__PM_TZHOUR
/*Note2*/ | DUK__PM_TZMINUTE
, DUK__SM_NUL
, 0, DUK__CF_ACCEPT
)
180 /* Note1: the specification doesn't require matching a time form with
181 * just hours ("HH"), but we accept it here, e.g. "2012-01-02T12Z".
183 * Note2: the specification doesn't require matching a timezone offset
184 * with just hours ("HH"), but accept it here, e.g. "2012-01-02T03:04:05+02"
188 DUK_LOCAL duk_bool_t
duk__parse_string_iso8601_subset(duk_context
*ctx
, const char *str
) {
189 duk_int_t parts
[DUK__NUM_ISO8601_PARSER_PARTS
];
190 duk_double_t dparts
[DUK_DATE_IDX_NUM_PARTS
];
192 const duk_uint8_t
*p
;
193 duk_small_uint_t part_idx
= 0;
195 duk_small_uint_t ndigits
= 0;
196 duk_bool_t neg_year
= 0;
197 duk_bool_t neg_tzoffset
= 0;
201 /* During parsing, month and day are one-based; set defaults here. */
202 DUK_MEMZERO(parts
, sizeof(parts
));
203 DUK_ASSERT(parts
[DUK_DATE_IDX_YEAR
] == 0); /* don't care value, year is mandatory */
204 parts
[DUK_DATE_IDX_MONTH
] = 1;
205 parts
[DUK_DATE_IDX_DAY
] = 1;
207 /* Special handling for year sign. */
208 p
= (const duk_uint8_t
*) str
;
210 if (ch
== DUK_ASC_PLUS
) {
212 } else if (ch
== DUK_ASC_MINUS
) {
219 DUK_DDD(DUK_DDDPRINT("parsing, part_idx=%ld, char=%ld ('%c')",
220 (long) part_idx
, (long) ch
,
221 (int) ((ch
>= 0x20 && ch
<= 0x7e) ? ch
: DUK_ASC_QUESTION
)));
223 if (ch
>= DUK_ASC_0
&& ch
<= DUK_ASC_9
) {
225 DUK_DDD(DUK_DDDPRINT("too many digits -> reject"));
228 if (part_idx
== DUK__PI_MILLISECOND
/*msec*/ && ndigits
>= 3) {
229 /* ignore millisecond fractions after 3 */
231 accum
= accum
* 10 + ((duk_int_t
) ch
) - ((duk_int_t
) DUK_ASC_0
) + 0x00;
235 duk_uint_fast32_t match_val
;
236 duk_small_int_t sep_idx
;
241 if (part_idx
== DUK__PI_MILLISECOND
) {
242 /* complete the millisecond field */
243 while (ndigits
< 3) {
248 parts
[part_idx
] = accum
;
249 DUK_DDD(DUK_DDDPRINT("wrote part %ld -> value %ld", (long) part_idx
, (long) accum
));
254 for (i
= 0; i
< (duk_small_uint_t
) (sizeof(duk__parse_iso8601_seps
) / sizeof(duk_uint8_t
)); i
++) {
255 if (duk__parse_iso8601_seps
[i
] == ch
) {
259 if (i
== (duk_small_uint_t
) (sizeof(duk__parse_iso8601_seps
) / sizeof(duk_uint8_t
))) {
260 DUK_DDD(DUK_DDDPRINT("separator character doesn't match -> reject"));
265 match_val
= (1UL << part_idx
) + (1UL << (sep_idx
+ 9)); /* match against rule part/sep bits */
267 for (i
= 0; i
< (duk_small_uint_t
) (sizeof(duk__parse_iso8601_control
) / sizeof(duk_uint32_t
)); i
++) {
268 duk_uint_fast32_t rule
= duk__parse_iso8601_control
[i
];
269 duk_small_uint_t nextpart
;
270 duk_small_uint_t cflags
;
272 DUK_DDD(DUK_DDDPRINT("part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, considering rule=0x%08lx",
273 (long) part_idx
, (long) sep_idx
,
274 (unsigned long) match_val
, (unsigned long) rule
));
276 if ((rule
& match_val
) != match_val
) {
280 DUK__UNPACK_RULE(rule
, nextpart
, cflags
);
282 DUK_DDD(DUK_DDDPRINT("rule match -> part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, "
283 "rule=0x%08lx -> nextpart=%ld, cflags=0x%02lx",
284 (long) part_idx
, (long) sep_idx
,
285 (unsigned long) match_val
, (unsigned long) rule
,
286 (long) nextpart
, (unsigned long) cflags
));
288 if (cflags
& DUK__CF_NEG
) {
292 if (cflags
& DUK__CF_ACCEPT
) {
296 if (cflags
& DUK__CF_ACCEPT_NUL
) {
297 DUK_ASSERT(*(p
- 1) != (char) 0);
298 if (*p
== DUK_ASC_NUL
) {
308 if (i
== (duk_small_uint_t
) (sizeof(duk__parse_iso8601_control
) / sizeof(duk_uint32_t
))) {
309 DUK_DDD(DUK_DDDPRINT("no rule matches -> reject"));
314 /* This shouldn't be necessary, but check just in case
315 * to avoid any chance of overruns.
317 DUK_DDD(DUK_DDDPRINT("NUL after rule matching (should not happen) -> reject"));
320 } /* if-digit-else-ctrl */
323 /* We should never exit the loop above. */
327 DUK_DDD(DUK_DDDPRINT("reject"));
331 DUK_DDD(DUK_DDDPRINT("accept"));
333 /* Apply timezone offset to get the main parts in UTC */
335 parts
[DUK__PI_YEAR
] = -parts
[DUK__PI_YEAR
];
338 parts
[DUK__PI_HOUR
] += parts
[DUK__PI_TZHOUR
];
339 parts
[DUK__PI_MINUTE
] += parts
[DUK__PI_TZMINUTE
];
341 parts
[DUK__PI_HOUR
] -= parts
[DUK__PI_TZHOUR
];
342 parts
[DUK__PI_MINUTE
] -= parts
[DUK__PI_TZMINUTE
];
344 parts
[DUK__PI_MONTH
] -= 1; /* zero-based month */
345 parts
[DUK__PI_DAY
] -= 1; /* zero-based day */
347 /* Use double parts, they tolerate unnormalized time.
349 * Note: DUK_DATE_IDX_WEEKDAY is initialized with a bogus value (DUK__PI_TZHOUR)
350 * on purpose. It won't be actually used by duk_bi_date_get_timeval_from_dparts(),
351 * but will make the value initialized just in case, and avoid any
352 * potential for Valgrind issues.
354 for (i
= 0; i
< DUK_DATE_IDX_NUM_PARTS
; i
++) {
355 DUK_DDD(DUK_DDDPRINT("part[%ld] = %ld", (long) i
, (long) parts
[i
]));
356 dparts
[i
] = parts
[i
];
359 d
= duk_bi_date_get_timeval_from_dparts(dparts
, 0 /*flags*/);
360 duk_push_number(ctx
, d
);
365 * Date/time parsing helper.
367 * Parse a datetime string into a time value. We must first try to parse
368 * the input according to the standard format in E5.1 Section 15.9.1.15.
369 * If that fails, we can try to parse using custom parsing, which can
370 * either be platform neutral (custom code) or platform specific (using
371 * existing platform API calls).
373 * Note in particular that we must parse whatever toString(), toUTCString(),
374 * and toISOString() can produce; see E5.1 Section 15.9.4.2.
376 * Returns 1 to allow tail calling.
378 * There is much room for improvement here with respect to supporting
379 * alternative datetime formats. For instance, V8 parses '2012-01-01' as
380 * UTC and '2012/01/01' as local time.
383 DUK_LOCAL duk_ret_t
duk__parse_string(duk_context
*ctx
, const char *str
) {
384 /* XXX: there is a small risk here: because the ISO 8601 parser is
385 * very loose, it may end up parsing some datetime values which
386 * would be better parsed with a platform specific parser.
389 DUK_ASSERT(str
!= NULL
);
390 DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str
));
392 if (duk__parse_string_iso8601_subset(ctx
, str
) != 0) {
396 #if defined(DUK_USE_DATE_PARSE_STRING)
398 * - Push value on stack and return 1
399 * - Don't push anything on stack and return 0
402 if (DUK_USE_DATE_PARSE_STRING(ctx
, str
) != 0) {
406 /* No platform-specific parsing, this is not an error. */
416 * Some helpers are used for getters and can operate on normalized values
417 * which can be represented with 32-bit signed integers. Other helpers are
418 * needed by setters and operate on un-normalized double values, must watch
419 * out for non-finite numbers etc.
422 DUK_LOCAL duk_uint8_t duk__days_in_month
[12] = {
423 (duk_uint8_t
) 31, (duk_uint8_t
) 28, (duk_uint8_t
) 31, (duk_uint8_t
) 30,
424 (duk_uint8_t
) 31, (duk_uint8_t
) 30, (duk_uint8_t
) 31, (duk_uint8_t
) 31,
425 (duk_uint8_t
) 30, (duk_uint8_t
) 31, (duk_uint8_t
) 30, (duk_uint8_t
) 31
428 /* Maximum iteration count for computing UTC-to-local time offset when
429 * creating an Ecmascript time value from local parts.
431 #define DUK__LOCAL_TZOFFSET_MAXITER 4
433 /* Because 'day since epoch' can be negative and is used to compute weekday
434 * using a modulo operation, add this multiple of 7 to avoid negative values
435 * when year is below 1970 epoch. Ecmascript time values are restricted to
436 * +/- 100 million days from epoch, so this adder fits nicely into 32 bits.
437 * Round to a multiple of 7 (= floor(100000000 / 7) * 7) and add margin.
439 #define DUK__WEEKDAY_MOD_ADDER (20000000 * 7) /* 0x08583b00 */
441 DUK_INTERNAL duk_bool_t
duk_bi_date_is_leap_year(duk_int_t year
) {
442 if ((year
% 4) != 0) {
445 if ((year
% 100) != 0) {
448 if ((year
% 400) != 0) {
454 DUK_INTERNAL duk_bool_t
duk_bi_date_timeval_in_valid_range(duk_double_t x
) {
455 return (x
>= -DUK_DATE_MSEC_100M_DAYS
&& x
<= DUK_DATE_MSEC_100M_DAYS
);
458 DUK_INTERNAL duk_bool_t
duk_bi_date_timeval_in_leeway_range(duk_double_t x
) {
459 return (x
>= -DUK_DATE_MSEC_100M_DAYS_LEEWAY
&& x
<= DUK_DATE_MSEC_100M_DAYS_LEEWAY
);
462 DUK_INTERNAL duk_bool_t
duk_bi_date_year_in_valid_range(duk_double_t x
) {
463 return (x
>= DUK_DATE_MIN_ECMA_YEAR
&& x
<= DUK_DATE_MAX_ECMA_YEAR
);
466 DUK_LOCAL duk_double_t
duk__timeclip(duk_double_t x
) {
467 if (!DUK_ISFINITE(x
)) {
468 return DUK_DOUBLE_NAN
;
471 if (!duk_bi_date_timeval_in_valid_range(x
)) {
472 return DUK_DOUBLE_NAN
;
475 x
= duk_js_tointeger_number(x
);
477 /* Here we'd have the option to normalize -0 to +0. */
481 /* Integer division which floors also negative values correctly. */
482 DUK_LOCAL duk_int_t
duk__div_floor(duk_int_t a
, duk_int_t b
) {
487 /* e.g. a = -4, b = 5 --> -4 - 5 + 1 / 5 --> -8 / 5 --> -1
488 * a = -5, b = 5 --> -5 - 5 + 1 / 5 --> -9 / 5 --> -1
489 * a = -6, b = 5 --> -6 - 5 + 1 / 5 --> -10 / 5 --> -2
491 return (a
- b
+ 1) / b
;
495 /* Compute day number of the first day of a given year. */
496 DUK_LOCAL duk_int_t
duk__day_from_year(duk_int_t year
) {
497 /* Note: in integer arithmetic, (x / 4) is same as floor(x / 4) for non-negative
498 * values, but is incorrect for negative ones.
500 return 365 * (year
- 1970)
501 + duk__div_floor(year
- 1969, 4)
502 - duk__div_floor(year
- 1901, 100)
503 + duk__div_floor(year
- 1601, 400);
506 /* Given a day number, determine year and day-within-year. */
507 DUK_LOCAL duk_int_t
duk__year_from_day(duk_int_t day
, duk_small_int_t
*out_day_within_year
) {
511 /* estimate year upwards (towards positive infinity), then back down;
512 * two iterations should be enough
516 year
= 1970 + day
/ 365;
518 year
= 1970 + day
/ 366;
522 diff_days
= duk__day_from_year(year
) - day
;
523 DUK_DDD(DUK_DDDPRINT("year=%ld day=%ld, diff_days=%ld", (long) year
, (long) day
, (long) diff_days
));
524 if (diff_days
<= 0) {
525 DUK_ASSERT(-diff_days
< 366); /* fits into duk_small_int_t */
526 *out_day_within_year
= -diff_days
;
527 DUK_DDD(DUK_DDDPRINT("--> year=%ld, day-within-year=%ld",
528 (long) year
, (long) *out_day_within_year
));
529 DUK_ASSERT(*out_day_within_year
>= 0);
530 DUK_ASSERT(*out_day_within_year
< (duk_bi_date_is_leap_year(year
) ? 366 : 365));
534 /* Note: this is very tricky; we must never 'overshoot' the
535 * correction downwards.
537 year
-= 1 + (diff_days
- 1) / 366; /* conservative */
541 /* Given a (year, month, day-within-month) triple, compute day number.
542 * The input triple is un-normalized and may contain non-finite values.
544 DUK_LOCAL duk_double_t
duk__make_day(duk_double_t year
, duk_double_t month
, duk_double_t day
) {
547 duk_small_int_t i
, n
;
549 /* Assume that year, month, day are all coerced to whole numbers.
550 * They may also be NaN or infinity, in which case this function
551 * must return NaN or infinity to ensure time value becomes NaN.
552 * If 'day' is NaN, the final return will end up returning a NaN,
553 * so it doesn't need to be checked here.
556 if (!DUK_ISFINITE(year
) || !DUK_ISFINITE(month
)) {
557 return DUK_DOUBLE_NAN
;
560 year
+= DUK_FLOOR(month
/ 12.0);
562 month
= DUK_FMOD(month
, 12.0);
564 /* handle negative values */
568 /* The algorithm in E5.1 Section 15.9.1.12 normalizes month, but
569 * does not normalize the day-of-month (nor check whether or not
570 * it is finite) because it's not necessary for finding the day
571 * number which matches the (year,month) pair.
573 * We assume that duk__day_from_year() is exact here.
575 * Without an explicit infinity / NaN check in the beginning,
576 * day_num would be a bogus integer here.
578 * It's possible for 'year' to be out of integer range here.
579 * If so, we need to return NaN without integer overflow.
580 * This fixes test-bug-setyear-overflow.js.
583 if (!duk_bi_date_year_in_valid_range(year
)) {
584 DUK_DD(DUK_DDPRINT("year not in ecmascript valid range, avoid integer overflow: %lf", (double) year
));
585 return DUK_DOUBLE_NAN
;
587 day_num
= duk__day_from_year((duk_int_t
) year
);
588 is_leap
= duk_bi_date_is_leap_year((duk_int_t
) year
);
590 n
= (duk_small_int_t
) month
;
591 for (i
= 0; i
< n
; i
++) {
592 day_num
+= duk__days_in_month
[i
];
593 if (i
== 1 && is_leap
) {
598 /* If 'day' is NaN, returns NaN. */
599 return (duk_double_t
) day_num
+ day
;
602 /* Split time value into parts. The time value is assumed to be an internal
603 * one, i.e. finite, no fractions. Possible local time adjustment has already
604 * been applied when reading the time value.
606 DUK_INTERNAL
void duk_bi_date_timeval_to_parts(duk_double_t d
, duk_int_t
*parts
, duk_double_t
*dparts
, duk_small_uint_t flags
) {
609 duk_int_t day_since_epoch
;
610 duk_int_t year
; /* does not fit into 16 bits */
611 duk_small_int_t day_in_year
;
612 duk_small_int_t month
;
615 duk_int_t jan1_since_epoch
;
616 duk_small_int_t jan1_weekday
;
617 duk_int_t equiv_year
;
620 duk_small_int_t arridx
;
622 DUK_ASSERT(DUK_ISFINITE(d
)); /* caller checks */
623 DUK_ASSERT(DUK_FLOOR(d
) == d
); /* no fractions in internal time */
625 /* The timevalue must be in valid Ecmascript range, but since a local
626 * time offset can be applied, we need to allow a +/- 24h leeway to
627 * the value. In other words, although the UTC time is within the
628 * Ecmascript range, the local part values can be just outside of it.
630 DUK_UNREF(duk_bi_date_timeval_in_leeway_range
);
631 DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d
));
633 /* these computations are guaranteed to be exact for the valid
634 * E5 time value range, assuming milliseconds without fractions.
636 d1
= (duk_double_t
) DUK_FMOD(d
, (double) DUK_DATE_MSEC_DAY
);
638 /* deal with negative values */
639 d1
+= (duk_double_t
) DUK_DATE_MSEC_DAY
;
641 d2
= DUK_FLOOR((double) (d
/ (duk_double_t
) DUK_DATE_MSEC_DAY
));
642 DUK_ASSERT(d2
* ((duk_double_t
) DUK_DATE_MSEC_DAY
) + d1
== d
);
643 /* now expected to fit into a 32-bit integer */
646 day_since_epoch
= t2
;
647 DUK_ASSERT((duk_double_t
) t1
== d1
);
648 DUK_ASSERT((duk_double_t
) t2
== d2
);
650 /* t1 = milliseconds within day (fits 32 bit)
651 * t2 = day number from epoch (fits 32 bit, may be negative)
654 parts
[DUK_DATE_IDX_MILLISECOND
] = t1
% 1000; t1
/= 1000;
655 parts
[DUK_DATE_IDX_SECOND
] = t1
% 60; t1
/= 60;
656 parts
[DUK_DATE_IDX_MINUTE
] = t1
% 60; t1
/= 60;
657 parts
[DUK_DATE_IDX_HOUR
] = t1
;
658 DUK_ASSERT(parts
[DUK_DATE_IDX_MILLISECOND
] >= 0 && parts
[DUK_DATE_IDX_MILLISECOND
] <= 999);
659 DUK_ASSERT(parts
[DUK_DATE_IDX_SECOND
] >= 0 && parts
[DUK_DATE_IDX_SECOND
] <= 59);
660 DUK_ASSERT(parts
[DUK_DATE_IDX_MINUTE
] >= 0 && parts
[DUK_DATE_IDX_MINUTE
] <= 59);
661 DUK_ASSERT(parts
[DUK_DATE_IDX_HOUR
] >= 0 && parts
[DUK_DATE_IDX_HOUR
] <= 23);
663 DUK_DDD(DUK_DDDPRINT("d=%lf, d1=%lf, d2=%lf, t1=%ld, t2=%ld, parts: hour=%ld min=%ld sec=%ld msec=%ld",
664 (double) d
, (double) d1
, (double) d2
, (long) t1
, (long) t2
,
665 (long) parts
[DUK_DATE_IDX_HOUR
],
666 (long) parts
[DUK_DATE_IDX_MINUTE
],
667 (long) parts
[DUK_DATE_IDX_SECOND
],
668 (long) parts
[DUK_DATE_IDX_MILLISECOND
]));
670 /* This assert depends on the input parts representing time inside
671 * the Ecmascript range.
673 DUK_ASSERT(t2
+ DUK__WEEKDAY_MOD_ADDER
>= 0);
674 parts
[DUK_DATE_IDX_WEEKDAY
] = (t2
+ 4 + DUK__WEEKDAY_MOD_ADDER
) % 7; /* E5.1 Section 15.9.1.6 */
675 DUK_ASSERT(parts
[DUK_DATE_IDX_WEEKDAY
] >= 0 && parts
[DUK_DATE_IDX_WEEKDAY
] <= 6);
677 year
= duk__year_from_day(t2
, &day_in_year
);
679 is_leap
= duk_bi_date_is_leap_year(year
);
680 for (month
= 0; month
< 12; month
++) {
681 dim
= duk__days_in_month
[month
];
682 if (month
== 1 && is_leap
) {
685 DUK_DDD(DUK_DDDPRINT("month=%ld, dim=%ld, day=%ld",
686 (long) month
, (long) dim
, (long) day
));
692 DUK_DDD(DUK_DDDPRINT("final month=%ld", (long) month
));
693 DUK_ASSERT(month
>= 0 && month
<= 11);
694 DUK_ASSERT(day
>= 0 && day
<= 31);
696 /* Equivalent year mapping, used to avoid DST trouble when platform
697 * may fail to provide reasonable DST answers for dates outside the
698 * ordinary range (e.g. 1970-2038). An equivalent year has the same
699 * leap-year-ness as the original year and begins on the same weekday
702 * The year 2038 is avoided because there seem to be problems with it
703 * on some platforms. The year 1970 is also avoided as there were
704 * practical problems with it; an equivalent year is used for it too,
705 * which breaks some DST computations for 1970 right now, see e.g.
706 * test-bi-date-tzoffset-brute-fi.js.
708 if ((flags
& DUK_DATE_FLAG_EQUIVYEAR
) && (year
< 1971 || year
> 2037)) {
709 DUK_ASSERT(is_leap
== 0 || is_leap
== 1);
711 jan1_since_epoch
= day_since_epoch
- day_in_year
; /* day number for Jan 1 since epoch */
712 DUK_ASSERT(jan1_since_epoch
+ DUK__WEEKDAY_MOD_ADDER
>= 0);
713 jan1_weekday
= (jan1_since_epoch
+ 4 + DUK__WEEKDAY_MOD_ADDER
) % 7; /* E5.1 Section 15.9.1.6 */
714 DUK_ASSERT(jan1_weekday
>= 0 && jan1_weekday
<= 6);
715 arridx
= jan1_weekday
;
719 DUK_ASSERT(arridx
>= 0 && arridx
< (duk_small_int_t
) (sizeof(duk__date_equivyear
) / sizeof(duk_uint8_t
)));
721 equiv_year
= (duk_int_t
) duk__date_equivyear
[arridx
] + 1970;
723 DUK_DDD(DUK_DDDPRINT("equiv year mapping, year=%ld, day_in_year=%ld, day_since_epoch=%ld, "
724 "jan1_since_epoch=%ld, jan1_weekday=%ld -> equiv year %ld",
725 (long) year
, (long) day_in_year
, (long) day_since_epoch
,
726 (long) jan1_since_epoch
, (long) jan1_weekday
, (long) equiv_year
));
729 parts
[DUK_DATE_IDX_YEAR
] = year
;
730 parts
[DUK_DATE_IDX_MONTH
] = month
;
731 parts
[DUK_DATE_IDX_DAY
] = day
;
733 if (flags
& DUK_DATE_FLAG_ONEBASED
) {
734 parts
[DUK_DATE_IDX_MONTH
]++; /* zero-based -> one-based */
735 parts
[DUK_DATE_IDX_DAY
]++; /* -""- */
738 if (dparts
!= NULL
) {
739 for (i
= 0; i
< DUK_DATE_IDX_NUM_PARTS
; i
++) {
740 dparts
[i
] = (duk_double_t
) parts
[i
];
745 /* Compute time value from (double) parts. The parts can be either UTC
746 * or local time; if local, they need to be (conceptually) converted into
747 * UTC time. The parts may represent valid or invalid time, and may be
748 * wildly out of range (but may cancel each other and still come out in
749 * the valid Date range).
751 DUK_INTERNAL duk_double_t
duk_bi_date_get_timeval_from_dparts(duk_double_t
*dparts
, duk_small_uint_t flags
) {
752 #if defined(DUK_USE_PARANOID_DATE_COMPUTATION)
753 /* See comments below on MakeTime why these are volatile. */
754 volatile duk_double_t tmp_time
;
755 volatile duk_double_t tmp_day
;
756 volatile duk_double_t d
;
758 duk_double_t tmp_time
;
759 duk_double_t tmp_day
;
763 duk_int_t tzoff
, tzoffprev1
, tzoffprev2
;
765 /* Expects 'this' at top of stack on entry. */
767 /* Coerce all finite parts with ToInteger(). ToInteger() must not
768 * be called for NaN/Infinity because it will convert e.g. NaN to
769 * zero. If ToInteger() has already been called, this has no side
770 * effects and is idempotent.
772 * Don't read dparts[DUK_DATE_IDX_WEEKDAY]; it will cause Valgrind
773 * issues if the value is uninitialized.
775 for (i
= 0; i
<= DUK_DATE_IDX_MILLISECOND
; i
++) {
776 /* SCANBUILD: scan-build complains here about assigned value
777 * being garbage or undefined. This is correct but operating
778 * on undefined values has no ill effect and is ignored by the
779 * caller in the case where this happens.
782 if (DUK_ISFINITE(d
)) {
783 dparts
[i
] = duk_js_tointeger_number(d
);
787 /* Use explicit steps in computation to try to ensure that
788 * computation happens with intermediate results coerced to
789 * double values (instead of using something more accurate).
790 * E.g. E5.1 Section 15.9.1.11 requires use of IEEE 754
791 * rules (= Ecmascript '+' and '*' operators).
793 * Without 'volatile' even this approach fails on some platform
794 * and compiler combinations. For instance, gcc 4.8.1 on Ubuntu
795 * 64-bit, with -m32 and without -std=c99, test-bi-date-canceling.js
796 * would fail because of some optimizations when computing tmp_time
797 * (MakeTime below). Adding 'volatile' to tmp_time solved this
798 * particular problem (annoyingly, also adding debug prints or
799 * running the executable under valgrind hides it).
804 tmp_time
+= dparts
[DUK_DATE_IDX_HOUR
] * ((duk_double_t
) DUK_DATE_MSEC_HOUR
);
805 tmp_time
+= dparts
[DUK_DATE_IDX_MINUTE
] * ((duk_double_t
) DUK_DATE_MSEC_MINUTE
);
806 tmp_time
+= dparts
[DUK_DATE_IDX_SECOND
] * ((duk_double_t
) DUK_DATE_MSEC_SECOND
);
807 tmp_time
+= dparts
[DUK_DATE_IDX_MILLISECOND
];
810 tmp_day
= duk__make_day(dparts
[DUK_DATE_IDX_YEAR
], dparts
[DUK_DATE_IDX_MONTH
], dparts
[DUK_DATE_IDX_DAY
]);
813 d
= tmp_day
* ((duk_double_t
) DUK_DATE_MSEC_DAY
) + tmp_time
;
815 DUK_DDD(DUK_DDDPRINT("time=%lf day=%lf --> timeval=%lf",
816 (double) tmp_time
, (double) tmp_day
, (double) d
));
818 /* Optional UTC conversion. */
819 if (flags
& DUK_DATE_FLAG_LOCALTIME
) {
820 /* DUK_USE_DATE_GET_LOCAL_TZOFFSET() needs to be called with a
821 * time value computed from UTC parts. At this point we only
822 * have 'd' which is a time value computed from local parts, so
823 * it is off by the UTC-to-local time offset which we don't know
824 * yet. The current solution for computing the UTC-to-local
825 * time offset is to iterate a few times and detect a fixed
826 * point or a two-cycle loop (or a sanity iteration limit),
827 * see test-bi-date-local-parts.js and test-bi-date-tzoffset-basic-fi.js.
829 * E5.1 Section 15.9.1.9:
830 * UTC(t) = t - LocalTZA - DaylightSavingTA(t - LocalTZA)
832 * For NaN/inf, DUK_USE_DATE_GET_LOCAL_TZOFFSET() returns 0.
836 /* Old solution: don't iterate, incorrect */
837 tzoff
= DUK_USE_DATE_GET_LOCAL_TZOFFSET(d
);
838 DUK_DDD(DUK_DDDPRINT("tzoffset w/o iteration, tzoff=%ld", (long) tzoff
));
840 DUK_UNREF(tzoffprev1
);
841 DUK_UNREF(tzoffprev2
);
844 /* Iteration solution */
846 tzoffprev1
= 999999999L; /* invalid value which never matches */
847 for (i
= 0; i
< DUK__LOCAL_TZOFFSET_MAXITER
; i
++) {
848 tzoffprev2
= tzoffprev1
;
850 tzoff
= DUK_USE_DATE_GET_LOCAL_TZOFFSET(d
- tzoff
* 1000L);
851 DUK_DDD(DUK_DDDPRINT("tzoffset iteration, i=%d, tzoff=%ld, tzoffprev1=%ld tzoffprev2=%ld",
852 (int) i
, (long) tzoff
, (long) tzoffprev1
, (long) tzoffprev2
));
853 if (tzoff
== tzoffprev1
) {
854 DUK_DDD(DUK_DDDPRINT("tzoffset iteration finished, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld",
855 (int) i
, (long) tzoff
, (long) tzoffprev1
, (long) tzoffprev2
));
857 } else if (tzoff
== tzoffprev2
) {
858 /* Two value cycle, see e.g. test-bi-date-tzoffset-basic-fi.js.
859 * In these cases, favor a higher tzoffset to get a consistent
860 * result which is independent of iteration count. Not sure if
861 * this is a generically correct solution.
863 DUK_DDD(DUK_DDDPRINT("tzoffset iteration two-value cycle, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld",
864 (int) i
, (long) tzoff
, (long) tzoffprev1
, (long) tzoffprev2
));
865 if (tzoffprev1
> tzoff
) {
871 DUK_DDD(DUK_DDDPRINT("tzoffset iteration, tzoff=%ld", (long) tzoff
));
875 /* TimeClip(), which also handles Infinity -> NaN conversion */
876 d
= duk__timeclip(d
);
882 * API oriented helpers
885 /* Push 'this' binding, check that it is a Date object; then push the
886 * internal time value. At the end, stack is: [ ... this timeval ].
887 * Returns the time value. Local time adjustment is done if requested.
889 DUK_LOCAL duk_double_t
duk__push_this_get_timeval_tzoffset(duk_context
*ctx
, duk_small_uint_t flags
, duk_int_t
*out_tzoffset
) {
890 duk_hthread
*thr
= (duk_hthread
*) ctx
;
893 duk_int_t tzoffset
= 0;
896 h
= duk_get_hobject(ctx
, -1); /* XXX: getter with class check, useful in built-ins */
897 if (h
== NULL
|| DUK_HOBJECT_GET_CLASS_NUMBER(h
) != DUK_HOBJECT_CLASS_DATE
) {
898 DUK_ERROR_TYPE(thr
, "expected Date");
901 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VALUE
);
902 d
= duk_to_number(ctx
, -1);
906 if (flags
& DUK_DATE_FLAG_NAN_TO_ZERO
) {
909 if (flags
& DUK_DATE_FLAG_NAN_TO_RANGE_ERROR
) {
910 DUK_ERROR_RANGE(thr
, "Invalid Date");
913 /* if no NaN handling flag, may still be NaN here, but not Inf */
914 DUK_ASSERT(!DUK_ISINF(d
));
916 if (flags
& DUK_DATE_FLAG_LOCALTIME
) {
917 /* Note: DST adjustment is determined using UTC time.
918 * If 'd' is NaN, tzoffset will be 0.
920 tzoffset
= DUK_USE_DATE_GET_LOCAL_TZOFFSET(d
); /* seconds */
921 d
+= tzoffset
* 1000L;
924 *out_tzoffset
= tzoffset
;
931 DUK_LOCAL duk_double_t
duk__push_this_get_timeval(duk_context
*ctx
, duk_small_uint_t flags
) {
932 return duk__push_this_get_timeval_tzoffset(ctx
, flags
, NULL
);
935 /* Set timeval to 'this' from dparts, push the new time value onto the
936 * value stack and return 1 (caller can then tail call us). Expects
937 * the value stack to contain 'this' on the stack top.
939 DUK_LOCAL duk_ret_t
duk__set_this_timeval_from_dparts(duk_context
*ctx
, duk_double_t
*dparts
, duk_small_uint_t flags
) {
944 d
= duk_bi_date_get_timeval_from_dparts(dparts
, flags
);
945 duk_push_number(ctx
, d
); /* -> [ ... this timeval_new ] */
946 duk_dup_top(ctx
); /* -> [ ... this timeval_new timeval_new ] */
947 duk_put_prop_stridx(ctx
, -3, DUK_STRIDX_INT_VALUE
);
949 /* stack top: new time value, return 1 to allow tail calls */
953 /* 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. */
954 DUK_LOCAL
void duk__format_parts_iso8601(duk_int_t
*parts
, duk_int_t tzoffset
, duk_small_uint_t flags
, duk_uint8_t
*out_buf
) {
955 char yearstr
[8]; /* "-123456\0" */
956 char tzstr
[8]; /* "+11:22\0" */
957 char sep
= (flags
& DUK_DATE_FLAG_SEP_T
) ? DUK_ASC_UC_T
: DUK_ASC_SPACE
;
959 DUK_ASSERT(parts
[DUK_DATE_IDX_MONTH
] >= 1 && parts
[DUK_DATE_IDX_MONTH
] <= 12);
960 DUK_ASSERT(parts
[DUK_DATE_IDX_DAY
] >= 1 && parts
[DUK_DATE_IDX_DAY
] <= 31);
961 DUK_ASSERT(parts
[DUK_DATE_IDX_YEAR
] >= -999999 && parts
[DUK_DATE_IDX_YEAR
] <= 999999);
963 /* Note: %06d for positive value, %07d for negative value to include
966 DUK_SNPRINTF(yearstr
,
968 (parts
[DUK_DATE_IDX_YEAR
] >= 0 && parts
[DUK_DATE_IDX_YEAR
] <= 9999) ? "%04ld" :
969 ((parts
[DUK_DATE_IDX_YEAR
] >= 0) ? "+%06ld" : "%07ld"),
970 (long) parts
[DUK_DATE_IDX_YEAR
]);
971 yearstr
[sizeof(yearstr
) - 1] = (char) 0;
973 if (flags
& DUK_DATE_FLAG_LOCALTIME
) {
974 /* tzoffset seconds are dropped; 16 bits suffice for
975 * time offset in minutes
978 duk_small_int_t tmp
= tzoffset
/ 60;
979 DUK_SNPRINTF(tzstr
, sizeof(tzstr
), "+%02d:%02d", (int) (tmp
/ 60), (int) (tmp
% 60));
981 duk_small_int_t tmp
= -tzoffset
/ 60;
982 DUK_SNPRINTF(tzstr
, sizeof(tzstr
), "-%02d:%02d", (int) (tmp
/ 60), (int) (tmp
% 60));
984 tzstr
[sizeof(tzstr
) - 1] = (char) 0;
986 tzstr
[0] = DUK_ASC_UC_Z
;
990 /* Unlike year, the other parts fit into 16 bits so %d format
993 if ((flags
& DUK_DATE_FLAG_TOSTRING_DATE
) && (flags
& DUK_DATE_FLAG_TOSTRING_TIME
)) {
994 DUK_SPRINTF((char *) out_buf
, "%s-%02d-%02d%c%02d:%02d:%02d.%03d%s",
995 (const char *) yearstr
, (int) parts
[DUK_DATE_IDX_MONTH
], (int) parts
[DUK_DATE_IDX_DAY
], (int) sep
,
996 (int) parts
[DUK_DATE_IDX_HOUR
], (int) parts
[DUK_DATE_IDX_MINUTE
],
997 (int) parts
[DUK_DATE_IDX_SECOND
], (int) parts
[DUK_DATE_IDX_MILLISECOND
], (const char *) tzstr
);
998 } else if (flags
& DUK_DATE_FLAG_TOSTRING_DATE
) {
999 DUK_SPRINTF((char *) out_buf
, "%s-%02d-%02d",
1000 (const char *) yearstr
, (int) parts
[DUK_DATE_IDX_MONTH
], (int) parts
[DUK_DATE_IDX_DAY
]);
1002 DUK_ASSERT(flags
& DUK_DATE_FLAG_TOSTRING_TIME
);
1003 DUK_SPRINTF((char *) out_buf
, "%02d:%02d:%02d.%03d%s",
1004 (int) parts
[DUK_DATE_IDX_HOUR
], (int) parts
[DUK_DATE_IDX_MINUTE
],
1005 (int) parts
[DUK_DATE_IDX_SECOND
], (int) parts
[DUK_DATE_IDX_MILLISECOND
],
1006 (const char *) tzstr
);
1010 /* Helper for string conversion calls: check 'this' binding, get the
1011 * internal time value, and format date and/or time in a few formats.
1012 * Return value allows tail calls.
1014 DUK_LOCAL duk_ret_t
duk__to_string_helper(duk_context
*ctx
, duk_small_uint_t flags
) {
1016 duk_int_t parts
[DUK_DATE_IDX_NUM_PARTS
];
1017 duk_int_t tzoffset
; /* seconds, doesn't fit into 16 bits */
1019 duk_uint8_t buf
[DUK_BI_DATE_ISO8601_BUFSIZE
];
1021 DUK_UNREF(rc
); /* unreferenced with some options */
1023 d
= duk__push_this_get_timeval_tzoffset(ctx
, flags
, &tzoffset
);
1025 duk_push_hstring_stridx(ctx
, DUK_STRIDX_INVALID_DATE
);
1028 DUK_ASSERT(DUK_ISFINITE(d
));
1030 /* formatters always get one-based month/day-of-month */
1031 duk_bi_date_timeval_to_parts(d
, parts
, NULL
, DUK_DATE_FLAG_ONEBASED
);
1032 DUK_ASSERT(parts
[DUK_DATE_IDX_MONTH
] >= 1 && parts
[DUK_DATE_IDX_MONTH
] <= 12);
1033 DUK_ASSERT(parts
[DUK_DATE_IDX_DAY
] >= 1 && parts
[DUK_DATE_IDX_DAY
] <= 31);
1035 if (flags
& DUK_DATE_FLAG_TOSTRING_LOCALE
) {
1036 /* try locale specific formatter; if it refuses to format the
1037 * string, fall back to an ISO 8601 formatted value in local
1040 #if defined(DUK_USE_DATE_FORMAT_STRING)
1041 /* Contract, either:
1042 * - Push string to value stack and return 1
1043 * - Don't push anything and return 0
1046 rc
= DUK_USE_DATE_FORMAT_STRING(ctx
, parts
, tzoffset
, flags
);
1051 /* No locale specific formatter; this is OK, we fall back
1057 /* Different calling convention than above used because the helper
1060 duk__format_parts_iso8601(parts
, tzoffset
, flags
, buf
);
1061 duk_push_string(ctx
, (const char *) buf
);
1065 /* Helper for component getter calls: check 'this' binding, get the
1066 * internal time value, split it into parts (either as UTC time or
1067 * local time), push a specified component as a return value to the
1068 * value stack and return 1 (caller can then tail call us).
1070 DUK_LOCAL duk_ret_t
duk__get_part_helper(duk_context
*ctx
, duk_small_uint_t flags_and_idx
) {
1072 duk_int_t parts
[DUK_DATE_IDX_NUM_PARTS
];
1073 duk_small_uint_t idx_part
= (duk_small_uint_t
) (flags_and_idx
>> DUK_DATE_FLAG_VALUE_SHIFT
); /* unpack args */
1075 DUK_ASSERT_DISABLE(idx_part
>= 0); /* unsigned */
1076 DUK_ASSERT(idx_part
< DUK_DATE_IDX_NUM_PARTS
);
1078 d
= duk__push_this_get_timeval(ctx
, flags_and_idx
);
1083 DUK_ASSERT(DUK_ISFINITE(d
));
1085 duk_bi_date_timeval_to_parts(d
, parts
, NULL
, flags_and_idx
); /* no need to mask idx portion */
1087 /* Setter APIs detect special year numbers (0...99) and apply a +1900
1088 * only in certain cases. The legacy getYear() getter applies -1900
1091 duk_push_int(ctx
, (flags_and_idx
& DUK_DATE_FLAG_SUB1900
) ? parts
[idx_part
] - 1900 : parts
[idx_part
]);
1095 /* Helper for component setter calls: check 'this' binding, get the
1096 * internal time value, split it into parts (either as UTC time or
1097 * local time), modify one or more components as specified, recompute
1098 * the time value, set it as the internal value. Finally, push the
1099 * new time value as a return value to the value stack and return 1
1100 * (caller can then tail call us).
1102 DUK_LOCAL duk_ret_t
duk__set_part_helper(duk_context
*ctx
, duk_small_uint_t flags_and_maxnargs
) {
1104 duk_int_t parts
[DUK_DATE_IDX_NUM_PARTS
];
1105 duk_double_t dparts
[DUK_DATE_IDX_NUM_PARTS
];
1107 duk_small_uint_t maxnargs
= (duk_small_uint_t
) (flags_and_maxnargs
>> DUK_DATE_FLAG_VALUE_SHIFT
); /* unpack args */
1108 duk_small_uint_t idx_first
, idx
;
1111 nargs
= duk_get_top(ctx
);
1112 d
= duk__push_this_get_timeval(ctx
, flags_and_maxnargs
);
1113 DUK_ASSERT(DUK_ISFINITE(d
) || DUK_ISNAN(d
));
1115 if (DUK_ISFINITE(d
)) {
1116 duk_bi_date_timeval_to_parts(d
, parts
, dparts
, flags_and_maxnargs
);
1118 /* NaN timevalue: we need to coerce the arguments, but
1119 * the resulting internal timestamp needs to remain NaN.
1120 * This works but is not pretty: parts and dparts will
1121 * be partially uninitialized, but we only write to them.
1126 * Determining which datetime components to overwrite based on
1127 * stack arguments is a bit complicated, but important to factor
1128 * out from setters themselves for compactness.
1130 * If DUK_DATE_FLAG_TIMESETTER, maxnargs indicates setter type:
1133 * 2 -> second, [millisecond]
1134 * 3 -> minute, [second], [millisecond]
1135 * 4 -> hour, [minute], [second], [millisecond]
1140 * 2 -> month, [date]
1141 * 3 -> year, [month], [date]
1143 * By comparing nargs and maxnargs (and flags) we know which
1144 * components to override. We rely on part index ordering.
1147 if (flags_and_maxnargs
& DUK_DATE_FLAG_TIMESETTER
) {
1148 DUK_ASSERT(maxnargs
>= 1 && maxnargs
<= 4);
1149 idx_first
= DUK_DATE_IDX_MILLISECOND
- (maxnargs
- 1);
1151 DUK_ASSERT(maxnargs
>= 1 && maxnargs
<= 3);
1152 idx_first
= DUK_DATE_IDX_DAY
- (maxnargs
- 1);
1154 DUK_ASSERT_DISABLE(idx_first
>= 0); /* unsigned */
1155 DUK_ASSERT(idx_first
< DUK_DATE_IDX_NUM_PARTS
);
1157 for (i
= 0; i
< maxnargs
; i
++) {
1158 if ((duk_idx_t
) i
>= nargs
) {
1159 /* no argument given -> leave components untouched */
1162 idx
= idx_first
+ i
;
1163 DUK_ASSERT_DISABLE(idx
>= 0); /* unsigned */
1164 DUK_ASSERT(idx
< DUK_DATE_IDX_NUM_PARTS
);
1166 if (idx
== DUK_DATE_IDX_YEAR
&& (flags_and_maxnargs
& DUK_DATE_FLAG_YEAR_FIXUP
)) {
1167 duk__twodigit_year_fixup(ctx
, (duk_idx_t
) i
);
1170 dparts
[idx
] = duk_to_number(ctx
, i
);
1172 if (idx
== DUK_DATE_IDX_DAY
) {
1173 /* Day-of-month is one-based in the API, but zero-based
1174 * internally, so fix here. Note that month is zero-based
1175 * both in the API and internally.
1177 /* SCANBUILD: complains about use of uninitialized values.
1178 * The complaint is correct, but operating in undefined
1179 * values here is intentional in some cases and the caller
1180 * ignores the results.
1186 /* Leaves new timevalue on stack top and returns 1, which is correct
1189 if (DUK_ISFINITE(d
)) {
1190 return duk__set_this_timeval_from_dparts(ctx
, dparts
, flags_and_maxnargs
);
1192 /* Internal timevalue is already NaN, so don't touch it. */
1198 /* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add
1199 * 1900 and replace value at idx_val.
1201 DUK_LOCAL
void duk__twodigit_year_fixup(duk_context
*ctx
, duk_idx_t idx_val
) {
1204 /* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t
1205 * might not generate better code due to casting.
1208 /* E5 Sections 15.9.3.1, B.2.4, B.2.5 */
1209 duk_to_number(ctx
, idx_val
);
1210 if (duk_is_nan(ctx
, idx_val
)) {
1213 duk_dup(ctx
, idx_val
);
1214 duk_to_int(ctx
, -1);
1215 d
= duk_get_number(ctx
, -1); /* get as double to handle huge numbers correctly */
1216 if (d
>= 0.0 && d
<= 99.0) {
1218 duk_push_number(ctx
, d
);
1219 duk_replace(ctx
, idx_val
);
1224 /* Set datetime parts from stack arguments, defaulting any missing values.
1225 * Day-of-week is not set; it is not required when setting the time value.
1227 DUK_LOCAL
void duk__set_parts_from_args(duk_context
*ctx
, duk_double_t
*dparts
, duk_idx_t nargs
) {
1230 duk_small_uint_t idx
;
1232 /* Causes a ToNumber() coercion, but doesn't break coercion order since
1233 * year is coerced first anyway.
1235 duk__twodigit_year_fixup(ctx
, 0);
1237 /* There are at most 7 args, but we use 8 here so that also
1238 * DUK_DATE_IDX_WEEKDAY gets initialized (to zero) to avoid the potential
1239 * for any Valgrind gripes later.
1241 for (i
= 0; i
< 8; i
++) {
1242 /* Note: rely on index ordering */
1243 idx
= DUK_DATE_IDX_YEAR
+ i
;
1244 if ((duk_idx_t
) i
< nargs
) {
1245 d
= duk_to_number(ctx
, (duk_idx_t
) i
);
1246 if (idx
== DUK_DATE_IDX_DAY
) {
1247 /* Convert day from one-based to zero-based (internal). This may
1248 * cause the day part to be negative, which is OK.
1253 /* All components default to 0 except day-of-month which defaults
1254 * to 1. However, because our internal day-of-month is zero-based,
1255 * it also defaults to zero here.
1262 DUK_DDD(DUK_DDDPRINT("parts from args -> %lf %lf %lf %lf %lf %lf %lf %lf",
1263 (double) dparts
[0], (double) dparts
[1],
1264 (double) dparts
[2], (double) dparts
[3],
1265 (double) dparts
[4], (double) dparts
[5],
1266 (double) dparts
[6], (double) dparts
[7]));
1270 * Helper to format a time value into caller buffer, used by logging.
1271 * 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long.
1274 DUK_INTERNAL
void duk_bi_date_format_timeval(duk_double_t timeval
, duk_uint8_t
*out_buf
) {
1275 duk_int_t parts
[DUK_DATE_IDX_NUM_PARTS
];
1277 duk_bi_date_timeval_to_parts(timeval
,
1280 DUK_DATE_FLAG_ONEBASED
);
1282 duk__format_parts_iso8601(parts
,
1284 DUK_DATE_FLAG_TOSTRING_DATE
|
1285 DUK_DATE_FLAG_TOSTRING_TIME
|
1286 DUK_DATE_FLAG_SEP_T
/*flags*/,
1291 * Indirect magic value lookup for Date methods.
1293 * Date methods don't put their control flags into the function magic value
1294 * because they wouldn't fit into a LIGHTFUNC's magic field. Instead, the
1295 * magic value is set to an index pointing to the array of control flags
1298 * This must be kept in strict sync with genbuiltins.py!
1301 static duk_uint16_t duk__date_magics
[] = {
1303 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_TOSTRING_TIME
+ DUK_DATE_FLAG_LOCALTIME
,
1305 /* 1: toDateString */
1306 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_LOCALTIME
,
1308 /* 2: toTimeString */
1309 DUK_DATE_FLAG_TOSTRING_TIME
+ DUK_DATE_FLAG_LOCALTIME
,
1311 /* 3: toLocaleString */
1312 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_TOSTRING_TIME
+ DUK_DATE_FLAG_TOSTRING_LOCALE
+ DUK_DATE_FLAG_LOCALTIME
,
1314 /* 4: toLocaleDateString */
1315 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_TOSTRING_LOCALE
+ DUK_DATE_FLAG_LOCALTIME
,
1317 /* 5: toLocaleTimeString */
1318 DUK_DATE_FLAG_TOSTRING_TIME
+ DUK_DATE_FLAG_TOSTRING_LOCALE
+ DUK_DATE_FLAG_LOCALTIME
,
1320 /* 6: toUTCString */
1321 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_TOSTRING_TIME
,
1323 /* 7: toISOString */
1324 DUK_DATE_FLAG_TOSTRING_DATE
+ DUK_DATE_FLAG_TOSTRING_TIME
+ DUK_DATE_FLAG_NAN_TO_RANGE_ERROR
+ DUK_DATE_FLAG_SEP_T
,
1326 /* 8: getFullYear */
1327 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_YEAR
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1329 /* 9: getUTCFullYear */
1330 0 + (DUK_DATE_IDX_YEAR
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1333 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_MONTH
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1335 /* 11: getUTCMonth */
1336 0 + (DUK_DATE_IDX_MONTH
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1339 DUK_DATE_FLAG_ONEBASED
+ DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_DAY
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1341 /* 13: getUTCDate */
1342 DUK_DATE_FLAG_ONEBASED
+ (DUK_DATE_IDX_DAY
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1345 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_WEEKDAY
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1348 0 + (DUK_DATE_IDX_WEEKDAY
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1351 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_HOUR
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1353 /* 17: getUTCHours */
1354 0 + (DUK_DATE_IDX_HOUR
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1356 /* 18: getMinutes */
1357 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_MINUTE
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1359 /* 19: getUTCMinutes */
1360 0 + (DUK_DATE_IDX_MINUTE
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1362 /* 20: getSeconds */
1363 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_SECOND
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1365 /* 21: getUTCSeconds */
1366 0 + (DUK_DATE_IDX_SECOND
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1368 /* 22: getMilliseconds */
1369 DUK_DATE_FLAG_LOCALTIME
+ (DUK_DATE_IDX_MILLISECOND
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1371 /* 23: getUTCMilliseconds */
1372 0 + (DUK_DATE_IDX_MILLISECOND
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1374 /* 24: setMilliseconds */
1375 DUK_DATE_FLAG_TIMESETTER
+ DUK_DATE_FLAG_LOCALTIME
+ (1 << DUK_DATE_FLAG_VALUE_SHIFT
),
1377 /* 25: setUTCMilliseconds */
1378 DUK_DATE_FLAG_TIMESETTER
+ (1 << DUK_DATE_FLAG_VALUE_SHIFT
),
1380 /* 26: setSeconds */
1381 DUK_DATE_FLAG_TIMESETTER
+ DUK_DATE_FLAG_LOCALTIME
+ (2 << DUK_DATE_FLAG_VALUE_SHIFT
),
1383 /* 27: setUTCSeconds */
1384 DUK_DATE_FLAG_TIMESETTER
+ (2 << DUK_DATE_FLAG_VALUE_SHIFT
),
1386 /* 28: setMinutes */
1387 DUK_DATE_FLAG_TIMESETTER
+ DUK_DATE_FLAG_LOCALTIME
+ (3 << DUK_DATE_FLAG_VALUE_SHIFT
),
1389 /* 29: setUTCMinutes */
1390 DUK_DATE_FLAG_TIMESETTER
+ (3 << DUK_DATE_FLAG_VALUE_SHIFT
),
1393 DUK_DATE_FLAG_TIMESETTER
+ DUK_DATE_FLAG_LOCALTIME
+ (4 << DUK_DATE_FLAG_VALUE_SHIFT
),
1395 /* 31: setUTCHours */
1396 DUK_DATE_FLAG_TIMESETTER
+ (4 << DUK_DATE_FLAG_VALUE_SHIFT
),
1399 DUK_DATE_FLAG_LOCALTIME
+ (1 << DUK_DATE_FLAG_VALUE_SHIFT
),
1401 /* 33: setUTCDate */
1402 0 + (1 << DUK_DATE_FLAG_VALUE_SHIFT
),
1405 DUK_DATE_FLAG_LOCALTIME
+ (2 << DUK_DATE_FLAG_VALUE_SHIFT
),
1407 /* 35: setUTCMonth */
1408 0 + (2 << DUK_DATE_FLAG_VALUE_SHIFT
),
1410 /* 36: setFullYear */
1411 DUK_DATE_FLAG_NAN_TO_ZERO
+ DUK_DATE_FLAG_LOCALTIME
+ (3 << DUK_DATE_FLAG_VALUE_SHIFT
),
1413 /* 37: setUTCFullYear */
1414 DUK_DATE_FLAG_NAN_TO_ZERO
+ (3 << DUK_DATE_FLAG_VALUE_SHIFT
),
1417 DUK_DATE_FLAG_LOCALTIME
+ DUK_DATE_FLAG_SUB1900
+ (DUK_DATE_IDX_YEAR
<< DUK_DATE_FLAG_VALUE_SHIFT
),
1420 DUK_DATE_FLAG_NAN_TO_ZERO
+ DUK_DATE_FLAG_YEAR_FIXUP
+ (3 << DUK_DATE_FLAG_VALUE_SHIFT
),
1423 DUK_LOCAL duk_small_uint_t
duk__date_get_indirect_magic(duk_context
*ctx
) {
1424 duk_small_int_t magicidx
= (duk_small_uint_t
) duk_get_current_magic(ctx
);
1425 DUK_ASSERT(magicidx
>= 0 && magicidx
< (duk_small_int_t
) (sizeof(duk__date_magics
) / sizeof(duk_uint16_t
)));
1426 return (duk_small_uint_t
) duk__date_magics
[magicidx
];
1433 DUK_INTERNAL duk_ret_t
duk_bi_date_constructor(duk_context
*ctx
) {
1434 duk_idx_t nargs
= duk_get_top(ctx
);
1435 duk_bool_t is_cons
= duk_is_constructor_call(ctx
);
1436 duk_double_t dparts
[DUK_DATE_IDX_NUM_PARTS
];
1439 DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs
, (long) is_cons
));
1441 duk_push_object_helper(ctx
,
1442 DUK_HOBJECT_FLAG_EXTENSIBLE
|
1443 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE
),
1444 DUK_BIDX_DATE_PROTOTYPE
);
1446 /* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date
1450 if (nargs
== 0 || !is_cons
) {
1451 d
= duk__timeclip(DUK_USE_DATE_GET_NOW(ctx
));
1452 duk_push_number(ctx
, d
);
1453 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_W
);
1455 /* called as a normal function: return new Date().toString() */
1456 duk_to_string(ctx
, -1);
1459 } else if (nargs
== 1) {
1460 duk_to_primitive(ctx
, 0, DUK_HINT_NONE
);
1461 if (duk_is_string(ctx
, 0)) {
1462 duk__parse_string(ctx
, duk_to_string(ctx
, 0));
1463 duk_replace(ctx
, 0); /* may be NaN */
1465 d
= duk__timeclip(duk_to_number(ctx
, 0));
1466 duk_push_number(ctx
, d
);
1467 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_W
);
1471 duk__set_parts_from_args(ctx
, dparts
, nargs
);
1473 /* Parts are in local time, convert when setting. */
1475 (void) duk__set_this_timeval_from_dparts(ctx
, dparts
, DUK_DATE_FLAG_LOCALTIME
/*flags*/); /* -> [ ... this timeval ] */
1476 duk_pop(ctx
); /* -> [ ... this ] */
1480 DUK_INTERNAL duk_ret_t
duk_bi_date_constructor_parse(duk_context
*ctx
) {
1481 return duk__parse_string(ctx
, duk_to_string(ctx
, 0));
1484 DUK_INTERNAL duk_ret_t
duk_bi_date_constructor_utc(duk_context
*ctx
) {
1485 duk_idx_t nargs
= duk_get_top(ctx
);
1486 duk_double_t dparts
[DUK_DATE_IDX_NUM_PARTS
];
1489 /* Behavior for nargs < 2 is implementation dependent: currently we'll
1490 * set a NaN time value (matching V8 behavior) in this case.
1496 duk__set_parts_from_args(ctx
, dparts
, nargs
);
1497 d
= duk_bi_date_get_timeval_from_dparts(dparts
, 0 /*flags*/);
1498 duk_push_number(ctx
, d
);
1503 DUK_INTERNAL duk_ret_t
duk_bi_date_constructor_now(duk_context
*ctx
) {
1506 d
= DUK_USE_DATE_GET_NOW(ctx
);
1507 DUK_ASSERT(duk__timeclip(d
) == d
); /* TimeClip() should never be necessary */
1508 duk_push_number(ctx
, d
);
1513 * String/JSON conversions
1515 * Human readable conversions are now basically ISO 8601 with a space
1516 * (instead of 'T') as the date/time separator. This is a good baseline
1517 * and is platform independent.
1519 * A shared native helper to provide many conversions. Magic value contains
1520 * a set of flags. The helper provides:
1526 * toLocaleDateString()
1527 * toLocaleTimeString()
1533 * - Date.prototype.toGMTString() and Date.prototype.toUTCString() are
1534 * required to be the same Ecmascript function object (!), so it is
1535 * omitted from here.
1537 * - Date.prototype.toUTCString(): E5.1 specification does not require a
1538 * specific format, but result should be human readable. The
1539 * specification suggests using ISO 8601 format with a space (instead
1540 * of 'T') separator if a more human readable format is not available.
1542 * - Date.prototype.toISOString(): unlike other conversion functions,
1543 * toISOString() requires a RangeError for invalid date values.
1546 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_tostring_shared(duk_context
*ctx
) {
1547 duk_small_uint_t flags
= duk__date_get_indirect_magic(ctx
);
1548 return duk__to_string_helper(ctx
, flags
);
1551 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_value_of(duk_context
*ctx
) {
1552 /* This native function is also used for Date.prototype.getTime()
1553 * as their behavior is identical.
1556 duk_double_t d
= duk__push_this_get_timeval(ctx
, 0 /*flags*/); /* -> [ this ] */
1557 DUK_ASSERT(DUK_ISFINITE(d
) || DUK_ISNAN(d
));
1558 duk_push_number(ctx
, d
);
1562 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_to_json(duk_context
*ctx
) {
1563 /* Note: toJSON() is a generic function which works even if 'this'
1564 * is not a Date. The sole argument is ignored.
1568 duk_to_object(ctx
, -1);
1571 duk_to_primitive(ctx
, -1, DUK_HINT_NUMBER
);
1572 if (duk_is_number(ctx
, -1)) {
1573 duk_double_t d
= duk_get_number(ctx
, -1);
1574 if (!DUK_ISFINITE(d
)) {
1581 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_TO_ISO_STRING
);
1582 duk_dup(ctx
, -2); /* -> [ O toIsoString O ] */
1583 duk_call_method(ctx
, 0);
1590 * Implementing getters is quite easy. The internal time value is either
1591 * NaN, or represents milliseconds (without fractions) from Jan 1, 1970.
1592 * The internal time value can be converted to integer parts, and each
1593 * part will be normalized and will fit into a 32-bit signed integer.
1595 * A shared native helper to provide all getters. Magic value contains
1596 * a set of flags and also packs the date component index argument. The
1614 * getUTCMilliseconds()
1619 * - Date.prototype.getDate(): 'date' means day-of-month, and is
1620 * zero-based in internal calculations but public API expects it to
1623 * - Date.prototype.getTime() and Date.prototype.valueOf() have identical
1624 * behavior. They have separate function objects, but share the same C
1625 * function (duk_bi_date_prototype_value_of).
1628 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_get_shared(duk_context
*ctx
) {
1629 duk_small_uint_t flags_and_idx
= duk__date_get_indirect_magic(ctx
);
1630 return duk__get_part_helper(ctx
, flags_and_idx
);
1633 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_get_timezone_offset(duk_context
*ctx
) {
1635 * Return (t - LocalTime(t)) in minutes:
1637 * t - LocalTime(t) = t - (t + LocalTZA + DaylightSavingTA(t))
1638 * = -(LocalTZA + DaylightSavingTA(t))
1640 * where DaylightSavingTA() is checked for time 't'.
1642 * Note that the sign of the result is opposite to common usage,
1643 * e.g. for EE(S)T which normally is +2h or +3h from UTC, this
1644 * function returns -120 or -180.
1651 /* Note: DST adjustment is determined using UTC time. */
1652 d
= duk__push_this_get_timeval(ctx
, 0 /*flags*/);
1653 DUK_ASSERT(DUK_ISFINITE(d
) || DUK_ISNAN(d
));
1657 DUK_ASSERT(DUK_ISFINITE(d
));
1658 tzoffset
= DUK_USE_DATE_GET_LOCAL_TZOFFSET(d
);
1659 duk_push_int(ctx
, -tzoffset
/ 60);
1667 * Setters are a bit more complicated than getters. Component setters
1668 * break down the current time value into its (normalized) component
1669 * parts, replace one or more components with -unnormalized- new values,
1670 * and the components are then converted back into a time value. As an
1671 * example of using unnormalized values:
1673 * var d = new Date(1234567890);
1677 * var d = new Date(0);
1678 * d.setUTCMilliseconds(1234567890);
1680 * A shared native helper to provide almost all setters. Magic value
1681 * contains a set of flags and also packs the "maxnargs" argument. The
1685 * setUTCMilliseconds()
1702 * - Date.prototype.setYear() (Section B addition): special year check
1703 * is omitted. NaN / Infinity will just flow through and ultimately
1704 * result in a NaN internal time value.
1706 * - Date.prototype.setYear() does not have optional arguments for
1707 * setting month and day-in-month (like setFullYear()), but we indicate
1708 * 'maxnargs' to be 3 to get the year written to the correct component
1709 * index in duk__set_part_helper(). The function has nargs == 1, so only
1710 * the year will be set regardless of actual argument count.
1713 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_set_shared(duk_context
*ctx
) {
1714 duk_small_uint_t flags_and_maxnargs
= duk__date_get_indirect_magic(ctx
);
1715 return duk__set_part_helper(ctx
, flags_and_maxnargs
);
1718 DUK_INTERNAL duk_ret_t
duk_bi_date_prototype_set_time(duk_context
*ctx
) {
1721 (void) duk__push_this_get_timeval(ctx
, 0 /*flags*/); /* -> [ timeval this ] */
1722 d
= duk__timeclip(duk_to_number(ctx
, 0));
1723 duk_push_number(ctx
, d
);
1725 duk_put_prop_stridx(ctx
, -3, DUK_STRIDX_INT_VALUE
); /* -> [ timeval this timeval ] */