1 /*
2  *  Date built-ins
3  *
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.
9  *
10  *  See doc/datetime.rst.
11  *
12  */
13 
14 #include "duk_internal.h"
15 
16 /*
17  *  Forward declarations
18  */
19 
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);
24 
25 /*
26  *  Other file level defines
27  */
28 
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])); \
40 	} while (0)
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])); \
47 	} while (0)
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])); \
54 	} while (0)
55 
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
60  */
61 #define DUK__YEAR(x) ((duk_uint8_t) ((x) - 1970))
62 DUK_LOCAL duk_uint8_t duk__date_equivyear[14] = {
63 #if 1
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
66 	 */
67 
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),
71 
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)
75 #endif
76 
77 #if 0
78 	/* This is based on Rhino EquivalentYear() algorithm:
79 	 * https://github.com/mozilla/rhino/blob/f99cc11d616f0cdda2c42bde72b3484df6182947/src/org/mozilla/javascript/NativeDate.java
80 	 */
81 
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),
85 
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)
89 #endif
90 };
91 #undef DUK__YEAR
92 
93 /*
94  *  ISO 8601 subset parser.
95  */
96 
97 /* Parser part count. */
98 #define DUK__NUM_ISO8601_PARSER_PARTS  9
99 
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
110 
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)
121 
122 /* Parser separator indices. */
123 #define DUK__SI_PLUS         0
124 #define DUK__SI_MINUS        1
125 #define DUK__SI_T            2
126 #define DUK__SI_SPACE        3
127 #define DUK__SI_COLON        4
128 #define DUK__SI_PERIOD       5
129 #define DUK__SI_Z            6
130 #define DUK__SI_NUL          7
131 
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)
141 
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) */
146 
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))
152 
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); \
156 	} while (0)
157 
158 #define DUK__RULE_MASK_PART_SEP  0x1ffffUL
159 
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*/
164 };
165 
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)
179 
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".
182 	 *
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"
185 	 */
186 };
187 
duk__parse_string_iso8601_subset(duk_context * ctx,const char * str)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];
191 	duk_double_t d;
192 	const duk_uint8_t *p;
193 	duk_small_uint_t part_idx = 0;
194 	duk_int_t accum = 0;
195 	duk_small_uint_t ndigits = 0;
196 	duk_bool_t neg_year = 0;
197 	duk_bool_t neg_tzoffset = 0;
198 	duk_uint_fast8_t ch;
199 	duk_small_uint_t i;
200 
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;
206 
207 	/* Special handling for year sign. */
208 	p = (const duk_uint8_t *) str;
209 	ch = p[0];
210 	if (ch == DUK_ASC_PLUS) {
211 		p++;
212 	} else if (ch == DUK_ASC_MINUS) {
213 		neg_year = 1;
214 		p++;
215 	}
216 
217 	for (;;) {
218 		ch = *p++;
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)));
222 
223 		if (ch >= DUK_ASC_0 && ch <= DUK_ASC_9) {
224 			if (ndigits >= 9) {
225 				DUK_DDD(DUK_DDDPRINT("too many digits -> reject"));
226 				goto reject;
227 			}
228 			if (part_idx == DUK__PI_MILLISECOND /*msec*/ && ndigits >= 3) {
229 				/* ignore millisecond fractions after 3 */
230 			} else {
231 				accum = accum * 10 + ((duk_int_t) ch) - ((duk_int_t) DUK_ASC_0) + 0x00;
232 				ndigits++;
233 			}
234 		} else {
235 			duk_uint_fast32_t match_val;
236 			duk_small_int_t sep_idx;
237 
238 			if (ndigits <= 0) {
239 				goto reject;
240 			}
241 			if (part_idx == DUK__PI_MILLISECOND) {
242 				/* complete the millisecond field */
243 				while (ndigits < 3) {
244 					accum *= 10;
245 					ndigits++;
246 				}
247 			}
248 			parts[part_idx] = accum;
249 			DUK_DDD(DUK_DDDPRINT("wrote part %ld -> value %ld", (long) part_idx, (long) accum));
250 
251 			accum = 0;
252 			ndigits = 0;
253 
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) {
256 					break;
257 				}
258 			}
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"));
261 				goto reject;
262 			}
263 
264 			sep_idx = i;
265 			match_val = (1UL << part_idx) + (1UL << (sep_idx + 9));  /* match against rule part/sep bits */
266 
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;
271 
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));
275 
276 				if ((rule & match_val) != match_val) {
277 					continue;
278 				}
279 
280 				DUK__UNPACK_RULE(rule, nextpart, cflags);
281 
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));
287 
288 				if (cflags & DUK__CF_NEG) {
289 					neg_tzoffset = 1;
290 				}
291 
292 				if (cflags & DUK__CF_ACCEPT) {
293 					goto accept;
294 				}
295 
296 				if (cflags & DUK__CF_ACCEPT_NUL) {
297 					DUK_ASSERT(*(p - 1) != (char) 0);
298 					if (*p == DUK_ASC_NUL) {
299 						goto accept;
300 					}
301 					goto reject;
302 				}
303 
304 				part_idx = nextpart;
305 				break;
306 			}  /* rule match */
307 
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"));
310 				goto reject;
311 			}
312 
313 			if (ch == 0) {
314 				/* This shouldn't be necessary, but check just in case
315 				 * to avoid any chance of overruns.
316 				 */
317 				DUK_DDD(DUK_DDDPRINT("NUL after rule matching (should not happen) -> reject"));
318 				goto reject;
319 			}
320 		}  /* if-digit-else-ctrl */
321 	}  /* char loop */
322 
323 	/* We should never exit the loop above. */
324 	DUK_UNREACHABLE();
325 
326  reject:
327 	DUK_DDD(DUK_DDDPRINT("reject"));
328 	return 0;
329 
330  accept:
331 	DUK_DDD(DUK_DDDPRINT("accept"));
332 
333 	/* Apply timezone offset to get the main parts in UTC */
334 	if (neg_year) {
335 		parts[DUK__PI_YEAR] = -parts[DUK__PI_YEAR];
336 	}
337 	if (neg_tzoffset) {
338 		parts[DUK__PI_HOUR] += parts[DUK__PI_TZHOUR];
339 		parts[DUK__PI_MINUTE] += parts[DUK__PI_TZMINUTE];
340 	} else {
341 		parts[DUK__PI_HOUR] -= parts[DUK__PI_TZHOUR];
342 		parts[DUK__PI_MINUTE] -= parts[DUK__PI_TZMINUTE];
343 	}
344 	parts[DUK__PI_MONTH] -= 1;  /* zero-based month */
345 	parts[DUK__PI_DAY] -= 1;  /* zero-based day */
346 
347 	/* Use double parts, they tolerate unnormalized time.
348 	 *
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.
353 	 */
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];
357 	}
358 
359 	d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
360 	duk_push_number(ctx, d);
361 	return 1;
362 }
363 
364 /*
365  *  Date/time parsing helper.
366  *
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).
372  *
373  *  Note in particular that we must parse whatever toString(), toUTCString(),
374  *  and toISOString() can produce; see E5.1 Section 15.9.4.2.
375  *
376  *  Returns 1 to allow tail calling.
377  *
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.
381  */
382 
duk__parse_string(duk_context * ctx,const char * str)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.
387 	 */
388 
389 	DUK_ASSERT(str != NULL);
390 	DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str));
391 
392 	if (duk__parse_string_iso8601_subset(ctx, str) != 0) {
393 		return 1;
394 	}
395 
396 #if defined(DUK_USE_DATE_PARSE_STRING)
397 	/* Contract, either:
398 	 * - Push value on stack and return 1
399 	 * - Don't push anything on stack and return 0
400 	 */
401 
402 	if (DUK_USE_DATE_PARSE_STRING(ctx, str) != 0) {
403 		return 1;
404 	}
405 #else
406 	/* No platform-specific parsing, this is not an error. */
407 #endif
408 
409 	duk_push_nan(ctx);
410 	return 1;
411 }
412 
413 /*
414  *  Calendar helpers
415  *
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.
420  */
421 
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
426 };
427 
428 /* Maximum iteration count for computing UTC-to-local time offset when
429  * creating an Ecmascript time value from local parts.
430  */
431 #define DUK__LOCAL_TZOFFSET_MAXITER   4
432 
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.
438  */
439 #define DUK__WEEKDAY_MOD_ADDER  (20000000 * 7)  /* 0x08583b00 */
440 
duk_bi_date_is_leap_year(duk_int_t year)441 DUK_INTERNAL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year) {
442 	if ((year % 4) != 0) {
443 		return 0;
444 	}
445 	if ((year % 100) != 0) {
446 		return 1;
447 	}
448 	if ((year % 400) != 0) {
449 		return 0;
450 	}
451 	return 1;
452 }
453 
duk_bi_date_timeval_in_valid_range(duk_double_t x)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);
456 }
457 
duk_bi_date_timeval_in_leeway_range(duk_double_t x)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);
460 }
461 
duk_bi_date_year_in_valid_range(duk_double_t x)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);
464 }
465 
duk__timeclip(duk_double_t x)466 DUK_LOCAL duk_double_t duk__timeclip(duk_double_t x) {
467 	if (!DUK_ISFINITE(x)) {
468 		return DUK_DOUBLE_NAN;
469 	}
470 
471 	if (!duk_bi_date_timeval_in_valid_range(x)) {
472 		return DUK_DOUBLE_NAN;
473 	}
474 
475 	x = duk_js_tointeger_number(x);
476 
477 	/* Here we'd have the option to normalize -0 to +0. */
478 	return x;
479 }
480 
481 /* Integer division which floors also negative values correctly. */
duk__div_floor(duk_int_t a,duk_int_t b)482 DUK_LOCAL duk_int_t duk__div_floor(duk_int_t a, duk_int_t b) {
483 	DUK_ASSERT(b > 0);
484 	if (a >= 0) {
485 		return a / b;
486 	} else {
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
490 		 */
491 		return (a - b + 1) / b;
492 	}
493 }
494 
495 /* Compute day number of the first day of a given year. */
duk__day_from_year(duk_int_t 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.
499 	 */
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);
504 }
505 
506 /* Given a day number, determine year and day-within-year. */
duk__year_from_day(duk_int_t day,duk_small_int_t * out_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) {
508 	duk_int_t year;
509 	duk_int_t diff_days;
510 
511 	/* estimate year upwards (towards positive infinity), then back down;
512 	 * two iterations should be enough
513 	 */
514 
515 	if (day >= 0) {
516 		year = 1970 + day / 365;
517 	} else {
518 		year = 1970 + day / 366;
519 	}
520 
521 	for (;;) {
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));
531 			return year;
532 		}
533 
534 		/* Note: this is very tricky; we must never 'overshoot' the
535 		 * correction downwards.
536 		 */
537 		year -= 1 + (diff_days - 1) / 366;  /* conservative */
538 	}
539 }
540 
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.
543  */
duk__make_day(duk_double_t year,duk_double_t month,duk_double_t day)544 DUK_LOCAL duk_double_t duk__make_day(duk_double_t year, duk_double_t month, duk_double_t day) {
545 	duk_int_t day_num;
546 	duk_bool_t is_leap;
547 	duk_small_int_t i, n;
548 
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.
554 	 */
555 
556 	if (!DUK_ISFINITE(year) || !DUK_ISFINITE(month)) {
557 		return DUK_DOUBLE_NAN;
558 	}
559 
560 	year += DUK_FLOOR(month / 12.0);
561 
562 	month = DUK_FMOD(month, 12.0);
563 	if (month < 0.0) {
564 		/* handle negative values */
565 		month += 12.0;
566 	}
567 
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.
572 	 *
573 	 * We assume that duk__day_from_year() is exact here.
574 	 *
575 	 * Without an explicit infinity / NaN check in the beginning,
576 	 * day_num would be a bogus integer here.
577 	 *
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.
581 	 */
582 
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;
586 	}
587 	day_num = duk__day_from_year((duk_int_t) year);
588 	is_leap = duk_bi_date_is_leap_year((duk_int_t) year);
589 
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) {
594 			day_num++;
595 		}
596 	}
597 
598 	/* If 'day' is NaN, returns NaN. */
599 	return (duk_double_t) day_num + day;
600 }
601 
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.
605  */
duk_bi_date_timeval_to_parts(duk_double_t d,duk_int_t * parts,duk_double_t * dparts,duk_small_uint_t flags)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) {
607 	duk_double_t d1, d2;
608 	duk_int_t t1, t2;
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;
613 	duk_small_int_t day;
614 	duk_small_int_t dim;
615 	duk_int_t jan1_since_epoch;
616 	duk_small_int_t jan1_weekday;
617 	duk_int_t equiv_year;
618 	duk_small_uint_t i;
619 	duk_bool_t is_leap;
620 	duk_small_int_t arridx;
621 
622 	DUK_ASSERT(DUK_ISFINITE(d));    /* caller checks */
623 	DUK_ASSERT(DUK_FLOOR(d) == d);  /* no fractions in internal time */
624 
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.
629 	 */
630 	DUK_UNREF(duk_bi_date_timeval_in_leeway_range);
631 	DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d));
632 
633 	/* these computations are guaranteed to be exact for the valid
634 	 * E5 time value range, assuming milliseconds without fractions.
635 	 */
636 	d1 = (duk_double_t) DUK_FMOD(d, (double) DUK_DATE_MSEC_DAY);
637 	if (d1 < 0.0) {
638 		/* deal with negative values */
639 		d1 += (duk_double_t) DUK_DATE_MSEC_DAY;
640 	}
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 */
644 	t1 = (duk_int_t) d1;
645 	t2 = (duk_int_t) d2;
646 	day_since_epoch = t2;
647 	DUK_ASSERT((duk_double_t) t1 == d1);
648 	DUK_ASSERT((duk_double_t) t2 == d2);
649 
650 	/* t1 = milliseconds within day (fits 32 bit)
651 	 * t2 = day number from epoch (fits 32 bit, may be negative)
652 	 */
653 
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);
662 
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]));
669 
670 	/* This assert depends on the input parts representing time inside
671 	 * the Ecmascript range.
672 	 */
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);
676 
677 	year = duk__year_from_day(t2, &day_in_year);
678 	day = 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) {
683 			dim++;
684 		}
685 		DUK_DDD(DUK_DDDPRINT("month=%ld, dim=%ld, day=%ld",
686 		                     (long) month, (long) dim, (long) day));
687 		if (day < dim) {
688 			break;
689 		}
690 		day -= dim;
691 	}
692 	DUK_DDD(DUK_DDDPRINT("final month=%ld", (long) month));
693 	DUK_ASSERT(month >= 0 && month <= 11);
694 	DUK_ASSERT(day >= 0 && day <= 31);
695 
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
700 	 * (Jan 1).
701 	 *
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.
707 	 */
708 	if ((flags & DUK_DATE_FLAG_EQUIVYEAR) && (year < 1971 || year > 2037)) {
709 		DUK_ASSERT(is_leap == 0 || is_leap == 1);
710 
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;
716 		if (is_leap) {
717 			arridx += 7;
718 		}
719 		DUK_ASSERT(arridx >= 0 && arridx < (duk_small_int_t) (sizeof(duk__date_equivyear) / sizeof(duk_uint8_t)));
720 
721 		equiv_year = (duk_int_t) duk__date_equivyear[arridx] + 1970;
722 		year = equiv_year;
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));
727 	}
728 
729 	parts[DUK_DATE_IDX_YEAR] = year;
730 	parts[DUK_DATE_IDX_MONTH] = month;
731 	parts[DUK_DATE_IDX_DAY] = day;
732 
733 	if (flags & DUK_DATE_FLAG_ONEBASED) {
734 		parts[DUK_DATE_IDX_MONTH]++;  /* zero-based -> one-based */
735 		parts[DUK_DATE_IDX_DAY]++;    /* -""- */
736 	}
737 
738 	if (dparts != NULL) {
739 		for (i = 0; i < DUK_DATE_IDX_NUM_PARTS; i++) {
740 			dparts[i] = (duk_double_t) parts[i];
741 		}
742 	}
743 }
744 
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).
750  */
duk_bi_date_get_timeval_from_dparts(duk_double_t * dparts,duk_small_uint_t flags)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;
757 #else
758 	duk_double_t tmp_time;
759 	duk_double_t tmp_day;
760 	duk_double_t d;
761 #endif
762 	duk_small_uint_t i;
763 	duk_int_t tzoff, tzoffprev1, tzoffprev2;
764 
765 	/* Expects 'this' at top of stack on entry. */
766 
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.
771 	 *
772 	 * Don't read dparts[DUK_DATE_IDX_WEEKDAY]; it will cause Valgrind
773 	 * issues if the value is uninitialized.
774 	 */
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.
780 		 */
781 		d = dparts[i];
782 		if (DUK_ISFINITE(d)) {
783 			dparts[i] = duk_js_tointeger_number(d);
784 		}
785 	}
786 
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).
792 	 *
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).
800 	 */
801 
802 	/* MakeTime */
803 	tmp_time = 0.0;
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];
808 
809 	/* MakeDay */
810 	tmp_day = duk__make_day(dparts[DUK_DATE_IDX_YEAR], dparts[DUK_DATE_IDX_MONTH], dparts[DUK_DATE_IDX_DAY]);
811 
812 	/* MakeDate */
813 	d = tmp_day * ((duk_double_t) DUK_DATE_MSEC_DAY) + tmp_time;
814 
815 	DUK_DDD(DUK_DDDPRINT("time=%lf day=%lf --> timeval=%lf",
816 	                     (double) tmp_time, (double) tmp_day, (double) d));
817 
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.
828 		 *
829 		 * E5.1 Section 15.9.1.9:
830 		 * UTC(t) = t - LocalTZA - DaylightSavingTA(t - LocalTZA)
831 		 *
832 		 * For NaN/inf, DUK_USE_DATE_GET_LOCAL_TZOFFSET() returns 0.
833 		 */
834 
835 #if 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));
839 		d -= tzoff * 1000L;
840 		DUK_UNREF(tzoffprev1);
841 		DUK_UNREF(tzoffprev2);
842 #endif
843 
844 		/* Iteration solution */
845 		tzoff = 0;
846 		tzoffprev1 = 999999999L;  /* invalid value which never matches */
847 		for (i = 0; i < DUK__LOCAL_TZOFFSET_MAXITER; i++) {
848 			tzoffprev2 = tzoffprev1;
849 			tzoffprev1 = tzoff;
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));
856 				break;
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.
862 				 */
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) {
866 					tzoff = tzoffprev1;
867 				}
868 				break;
869 			}
870 		}
871 		DUK_DDD(DUK_DDDPRINT("tzoffset iteration, tzoff=%ld", (long) tzoff));
872 		d -= tzoff * 1000L;
873 	}
874 
875 	/* TimeClip(), which also handles Infinity -> NaN conversion */
876 	d = duk__timeclip(d);
877 
878 	return d;
879 }
880 
881 /*
882  *  API oriented helpers
883  */
884 
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.
888  */
duk__push_this_get_timeval_tzoffset(duk_context * ctx,duk_small_uint_t flags,duk_int_t * out_tzoffset)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;
891 	duk_hobject *h;
892 	duk_double_t d;
893 	duk_int_t tzoffset = 0;
894 
895 	duk_push_this(ctx);
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");
899 	}
900 
901 	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE);
902 	d = duk_to_number(ctx, -1);
903 	duk_pop(ctx);
904 
905 	if (DUK_ISNAN(d)) {
906 		if (flags & DUK_DATE_FLAG_NAN_TO_ZERO) {
907 			d = 0.0;
908 		}
909 		if (flags & DUK_DATE_FLAG_NAN_TO_RANGE_ERROR) {
910 			DUK_ERROR_RANGE(thr, "Invalid Date");
911 		}
912 	}
913 	/* if no NaN handling flag, may still be NaN here, but not Inf */
914 	DUK_ASSERT(!DUK_ISINF(d));
915 
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.
919 		 */
920 		tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);  /* seconds */
921 		d += tzoffset * 1000L;
922 	}
923 	if (out_tzoffset) {
924 		*out_tzoffset = tzoffset;
925 	}
926 
927 	/* [ ... this ] */
928 	return d;
929 }
930 
duk__push_this_get_timeval(duk_context * ctx,duk_small_uint_t flags)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);
933 }
934 
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.
938  */
duk__set_this_timeval_from_dparts(duk_context * ctx,duk_double_t * dparts,duk_small_uint_t flags)939 DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk_double_t *dparts, duk_small_uint_t flags) {
940 	duk_double_t d;
941 
942 	/* [ ... this ] */
943 
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);
948 
949 	/* stack top: new time value, return 1 to allow tail calls */
950 	return 1;
951 }
952 
953 /* 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. */
duk__format_parts_iso8601(duk_int_t * parts,duk_int_t tzoffset,duk_small_uint_t flags,duk_uint8_t * out_buf)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;
958 
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);
962 
963 	/* Note: %06d for positive value, %07d for negative value to include
964 	 * sign and 6 digits.
965 	 */
966 	DUK_SNPRINTF(yearstr,
967 	             sizeof(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;
972 
973 	if (flags & DUK_DATE_FLAG_LOCALTIME) {
974 		/* tzoffset seconds are dropped; 16 bits suffice for
975 		 * time offset in minutes
976 		 */
977 		if (tzoffset >= 0) {
978 			duk_small_int_t tmp = tzoffset / 60;
979 			DUK_SNPRINTF(tzstr, sizeof(tzstr), "+%02d:%02d", (int) (tmp / 60), (int) (tmp % 60));
980 		} else {
981 			duk_small_int_t tmp = -tzoffset / 60;
982 			DUK_SNPRINTF(tzstr, sizeof(tzstr), "-%02d:%02d", (int) (tmp / 60), (int) (tmp % 60));
983 		}
984 		tzstr[sizeof(tzstr) - 1] = (char) 0;
985 	} else {
986 		tzstr[0] = DUK_ASC_UC_Z;
987 		tzstr[1] = (char) 0;
988 	}
989 
990 	/* Unlike year, the other parts fit into 16 bits so %d format
991 	 * is portable.
992 	 */
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]);
1001 	} else {
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);
1007 	}
1008 }
1009 
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.
1013  */
duk__to_string_helper(duk_context * ctx,duk_small_uint_t flags)1014 DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t flags) {
1015 	duk_double_t d;
1016 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
1017 	duk_int_t tzoffset;  /* seconds, doesn't fit into 16 bits */
1018 	duk_bool_t rc;
1019 	duk_uint8_t buf[DUK_BI_DATE_ISO8601_BUFSIZE];
1020 
1021 	DUK_UNREF(rc);  /* unreferenced with some options */
1022 
1023 	d = duk__push_this_get_timeval_tzoffset(ctx, flags, &tzoffset);
1024 	if (DUK_ISNAN(d)) {
1025 		duk_push_hstring_stridx(ctx, DUK_STRIDX_INVALID_DATE);
1026 		return 1;
1027 	}
1028 	DUK_ASSERT(DUK_ISFINITE(d));
1029 
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);
1034 
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
1038 		 * time.
1039 		 */
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
1044 		 */
1045 
1046 		rc = DUK_USE_DATE_FORMAT_STRING(ctx, parts, tzoffset, flags);
1047 		if (rc != 0) {
1048 			return 1;
1049 		}
1050 #else
1051 		/* No locale specific formatter; this is OK, we fall back
1052 		 * to ISO 8601.
1053 		 */
1054 #endif
1055 	}
1056 
1057 	/* Different calling convention than above used because the helper
1058 	 * is shared.
1059 	 */
1060 	duk__format_parts_iso8601(parts, tzoffset, flags, buf);
1061 	duk_push_string(ctx, (const char *) buf);
1062 	return 1;
1063 }
1064 
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).
1069  */
duk__get_part_helper(duk_context * ctx,duk_small_uint_t flags_and_idx)1070 DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flags_and_idx) {
1071 	duk_double_t d;
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 */
1074 
1075 	DUK_ASSERT_DISABLE(idx_part >= 0);  /* unsigned */
1076 	DUK_ASSERT(idx_part < DUK_DATE_IDX_NUM_PARTS);
1077 
1078 	d = duk__push_this_get_timeval(ctx, flags_and_idx);
1079 	if (DUK_ISNAN(d)) {
1080 		duk_push_nan(ctx);
1081 		return 1;
1082 	}
1083 	DUK_ASSERT(DUK_ISFINITE(d));
1084 
1085 	duk_bi_date_timeval_to_parts(d, parts, NULL, flags_and_idx);  /* no need to mask idx portion */
1086 
1087 	/* Setter APIs detect special year numbers (0...99) and apply a +1900
1088 	 * only in certain cases.  The legacy getYear() getter applies -1900
1089 	 * unconditionally.
1090 	 */
1091 	duk_push_int(ctx, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]);
1092 	return 1;
1093 }
1094 
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).
1101  */
duk__set_part_helper(duk_context * ctx,duk_small_uint_t flags_and_maxnargs)1102 DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flags_and_maxnargs) {
1103 	duk_double_t d;
1104 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
1105 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
1106 	duk_idx_t nargs;
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;
1109 	duk_small_uint_t i;
1110 
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));
1114 
1115 	if (DUK_ISFINITE(d)) {
1116 		duk_bi_date_timeval_to_parts(d, parts, dparts, flags_and_maxnargs);
1117 	} else {
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.
1122 		 */
1123 	}
1124 
1125 	/*
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.
1129 	 *
1130 	 *  If DUK_DATE_FLAG_TIMESETTER, maxnargs indicates setter type:
1131 	 *
1132 	 *   1 -> millisecond
1133 	 *   2 -> second, [millisecond]
1134 	 *   3 -> minute, [second], [millisecond]
1135 	 *   4 -> hour, [minute], [second], [millisecond]
1136 	 *
1137 	 *  Else:
1138 	 *
1139 	 *   1 -> date
1140 	 *   2 -> month, [date]
1141 	 *   3 -> year, [month], [date]
1142 	 *
1143 	 *  By comparing nargs and maxnargs (and flags) we know which
1144 	 *  components to override.  We rely on part index ordering.
1145 	 */
1146 
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);
1150 	} else {
1151 		DUK_ASSERT(maxnargs >= 1 && maxnargs <= 3);
1152 		idx_first = DUK_DATE_IDX_DAY - (maxnargs - 1);
1153 	}
1154 	DUK_ASSERT_DISABLE(idx_first >= 0);  /* unsigned */
1155 	DUK_ASSERT(idx_first < DUK_DATE_IDX_NUM_PARTS);
1156 
1157 	for (i = 0; i < maxnargs; i++) {
1158 		if ((duk_idx_t) i >= nargs) {
1159 			/* no argument given -> leave components untouched */
1160 			break;
1161 		}
1162 		idx = idx_first + i;
1163 		DUK_ASSERT_DISABLE(idx >= 0);  /* unsigned */
1164 		DUK_ASSERT(idx < DUK_DATE_IDX_NUM_PARTS);
1165 
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);
1168 		}
1169 
1170 		dparts[idx] = duk_to_number(ctx, i);
1171 
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.
1176 			 */
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.
1181 			 */
1182 			dparts[idx] -= 1.0;
1183 		}
1184 	}
1185 
1186 	/* Leaves new timevalue on stack top and returns 1, which is correct
1187 	 * for part setters.
1188 	 */
1189 	if (DUK_ISFINITE(d)) {
1190 		return duk__set_this_timeval_from_dparts(ctx, dparts, flags_and_maxnargs);
1191 	} else {
1192 		/* Internal timevalue is already NaN, so don't touch it. */
1193 		duk_push_nan(ctx);
1194 		return 1;
1195 	}
1196 }
1197 
1198 /* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add
1199  * 1900 and replace value at idx_val.
1200  */
duk__twodigit_year_fixup(duk_context * ctx,duk_idx_t idx_val)1201 DUK_LOCAL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val) {
1202 	duk_double_t d;
1203 
1204 	/* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t
1205 	 * might not generate better code due to casting.
1206 	 */
1207 
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)) {
1211 		return;
1212 	}
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) {
1217 		d += 1900.0;
1218 		duk_push_number(ctx, d);
1219 		duk_replace(ctx, idx_val);
1220 	}
1221 	duk_pop(ctx);
1222 }
1223 
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.
1226  */
duk__set_parts_from_args(duk_context * ctx,duk_double_t * dparts,duk_idx_t nargs)1227 DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, duk_idx_t nargs) {
1228 	duk_double_t d;
1229 	duk_small_uint_t i;
1230 	duk_small_uint_t idx;
1231 
1232 	/* Causes a ToNumber() coercion, but doesn't break coercion order since
1233 	 * year is coerced first anyway.
1234 	 */
1235 	duk__twodigit_year_fixup(ctx, 0);
1236 
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.
1240 	 */
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.
1249 				 */
1250 				d -= 1.0;
1251 			}
1252 		} else {
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.
1256 			 */
1257 			d = 0.0;
1258 		}
1259 		dparts[idx] = d;
1260 	}
1261 
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]));
1267 }
1268 
1269 /*
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.
1272  */
1273 
duk_bi_date_format_timeval(duk_double_t timeval,duk_uint8_t * out_buf)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];
1276 
1277 	duk_bi_date_timeval_to_parts(timeval,
1278 	                             parts,
1279 	                             NULL,
1280 	                             DUK_DATE_FLAG_ONEBASED);
1281 
1282 	duk__format_parts_iso8601(parts,
1283 	                          0 /*tzoffset*/,
1284 	                          DUK_DATE_FLAG_TOSTRING_DATE |
1285 	                          DUK_DATE_FLAG_TOSTRING_TIME |
1286 	                          DUK_DATE_FLAG_SEP_T /*flags*/,
1287 	                          out_buf);
1288 }
1289 
1290 /*
1291  *  Indirect magic value lookup for Date methods.
1292  *
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
1296  *  below.
1297  *
1298  *  This must be kept in strict sync with genbuiltins.py!
1299  */
1300 
1301 static duk_uint16_t duk__date_magics[] = {
1302 	/* 0: toString */
1303 	DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME,
1304 
1305 	/* 1: toDateString */
1306 	DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_LOCALTIME,
1307 
1308 	/* 2: toTimeString */
1309 	DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME,
1310 
1311 	/* 3: toLocaleString */
1312 	DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,
1313 
1314 	/* 4: toLocaleDateString */
1315 	DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,
1316 
1317 	/* 5: toLocaleTimeString */
1318 	DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME,
1319 
1320 	/* 6: toUTCString */
1321 	DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME,
1322 
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,
1325 
1326 	/* 8: getFullYear */
1327 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),
1328 
1329 	/* 9: getUTCFullYear */
1330 	0 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),
1331 
1332 	/* 10: getMonth */
1333 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT),
1334 
1335 	/* 11: getUTCMonth */
1336 	0 + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT),
1337 
1338 	/* 12: getDate */
1339 	DUK_DATE_FLAG_ONEBASED + DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT),
1340 
1341 	/* 13: getUTCDate */
1342 	DUK_DATE_FLAG_ONEBASED + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT),
1343 
1344 	/* 14: getDay */
1345 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT),
1346 
1347 	/* 15: getUTCDay */
1348 	0 + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT),
1349 
1350 	/* 16: getHours */
1351 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT),
1352 
1353 	/* 17: getUTCHours */
1354 	0 + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT),
1355 
1356 	/* 18: getMinutes */
1357 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT),
1358 
1359 	/* 19: getUTCMinutes */
1360 	0 + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT),
1361 
1362 	/* 20: getSeconds */
1363 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT),
1364 
1365 	/* 21: getUTCSeconds */
1366 	0 + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT),
1367 
1368 	/* 22: getMilliseconds */
1369 	DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT),
1370 
1371 	/* 23: getUTCMilliseconds */
1372 	0 + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT),
1373 
1374 	/* 24: setMilliseconds */
1375 	DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT),
1376 
1377 	/* 25: setUTCMilliseconds */
1378 	DUK_DATE_FLAG_TIMESETTER + (1 << DUK_DATE_FLAG_VALUE_SHIFT),
1379 
1380 	/* 26: setSeconds */
1381 	DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT),
1382 
1383 	/* 27: setUTCSeconds */
1384 	DUK_DATE_FLAG_TIMESETTER + (2 << DUK_DATE_FLAG_VALUE_SHIFT),
1385 
1386 	/* 28: setMinutes */
1387 	DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
1388 
1389 	/* 29: setUTCMinutes */
1390 	DUK_DATE_FLAG_TIMESETTER + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
1391 
1392 	/* 30: setHours */
1393 	DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (4 << DUK_DATE_FLAG_VALUE_SHIFT),
1394 
1395 	/* 31: setUTCHours */
1396 	DUK_DATE_FLAG_TIMESETTER + (4 << DUK_DATE_FLAG_VALUE_SHIFT),
1397 
1398 	/* 32: setDate */
1399 	DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT),
1400 
1401 	/* 33: setUTCDate */
1402 	0 + (1 << DUK_DATE_FLAG_VALUE_SHIFT),
1403 
1404 	/* 34: setMonth */
1405 	DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT),
1406 
1407 	/* 35: setUTCMonth */
1408 	0 + (2 << DUK_DATE_FLAG_VALUE_SHIFT),
1409 
1410 	/* 36: setFullYear */
1411 	DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
1412 
1413 	/* 37: setUTCFullYear */
1414 	DUK_DATE_FLAG_NAN_TO_ZERO + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
1415 
1416 	/* 38: getYear */
1417 	DUK_DATE_FLAG_LOCALTIME + DUK_DATE_FLAG_SUB1900 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT),
1418 
1419 	/* 39: setYear */
1420 	DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_YEAR_FIXUP + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
1421 };
1422 
duk__date_get_indirect_magic(duk_context * ctx)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];
1427 }
1428 
1429 /*
1430  *  Constructor calls
1431  */
1432 
duk_bi_date_constructor(duk_context * ctx)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];
1437 	duk_double_t d;
1438 
1439 	DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs, (long) is_cons));
1440 
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);
1445 
1446 	/* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date
1447 	 * is mutable.
1448 	 */
1449 
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);
1454 		if (!is_cons) {
1455 			/* called as a normal function: return new Date().toString() */
1456 			duk_to_string(ctx, -1);
1457 		}
1458 		return 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 */
1464 		}
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);
1468 		return 1;
1469 	}
1470 
1471 	duk__set_parts_from_args(ctx, dparts, nargs);
1472 
1473 	/* Parts are in local time, convert when setting. */
1474 
1475 	(void) duk__set_this_timeval_from_dparts(ctx, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/);  /* -> [ ... this timeval ] */
1476 	duk_pop(ctx);  /* -> [ ... this ] */
1477 	return 1;
1478 }
1479 
duk_bi_date_constructor_parse(duk_context * ctx)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));
1482 }
1483 
duk_bi_date_constructor_utc(duk_context * ctx)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];
1487 	duk_double_t d;
1488 
1489 	/* Behavior for nargs < 2 is implementation dependent: currently we'll
1490 	 * set a NaN time value (matching V8 behavior) in this case.
1491 	 */
1492 
1493 	if (nargs < 2) {
1494 		duk_push_nan(ctx);
1495 	} else {
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);
1499 	}
1500 	return 1;
1501 }
1502 
duk_bi_date_constructor_now(duk_context * ctx)1503 DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) {
1504 	duk_double_t d;
1505 
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);
1509 	return 1;
1510 }
1511 
1512 /*
1513  *  String/JSON conversions
1514  *
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.
1518  *
1519  *  A shared native helper to provide many conversions.  Magic value contains
1520  *  a set of flags.  The helper provides:
1521  *
1522  *    toString()
1523  *    toDateString()
1524  *    toTimeString()
1525  *    toLocaleString()
1526  *    toLocaleDateString()
1527  *    toLocaleTimeString()
1528  *    toUTCString()
1529  *    toISOString()
1530  *
1531  *  Notes:
1532  *
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.
1536  *
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.
1541  *
1542  *    - Date.prototype.toISOString(): unlike other conversion functions,
1543  *      toISOString() requires a RangeError for invalid date values.
1544  */
1545 
duk_bi_date_prototype_tostring_shared(duk_context * ctx)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);
1549 }
1550 
duk_bi_date_prototype_value_of(duk_context * ctx)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.
1554 	 */
1555 
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);
1559 	return 1;
1560 }
1561 
duk_bi_date_prototype_to_json(duk_context * ctx)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.
1565 	 */
1566 
1567 	duk_push_this(ctx);
1568 	duk_to_object(ctx, -1);
1569 
1570 	duk_dup_top(ctx);
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)) {
1575 			duk_push_null(ctx);
1576 			return 1;
1577 		}
1578 	}
1579 	duk_pop(ctx);
1580 
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);
1584 	return 1;
1585 }
1586 
1587 /*
1588  *  Getters.
1589  *
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.
1594  *
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
1597  *  helper provides:
1598  *
1599  *    getFullYear()
1600  *    getUTCFullYear()
1601  *    getMonth()
1602  *    getUTCMonth()
1603  *    getDate()
1604  *    getUTCDate()
1605  *    getDay()
1606  *    getUTCDay()
1607  *    getHours()
1608  *    getUTCHours()
1609  *    getMinutes()
1610  *    getUTCMinutes()
1611  *    getSeconds()
1612  *    getUTCSeconds()
1613  *    getMilliseconds()
1614  *    getUTCMilliseconds()
1615  *    getYear()
1616  *
1617  *  Notes:
1618  *
1619  *    - Date.prototype.getDate(): 'date' means day-of-month, and is
1620  *      zero-based in internal calculations but public API expects it to
1621  *      be one-based.
1622  *
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).
1626  */
1627 
duk_bi_date_prototype_get_shared(duk_context * ctx)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);
1631 }
1632 
duk_bi_date_prototype_get_timezone_offset(duk_context * ctx)1633 DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx) {
1634 	/*
1635 	 *  Return (t - LocalTime(t)) in minutes:
1636 	 *
1637 	 *    t - LocalTime(t) = t - (t + LocalTZA + DaylightSavingTA(t))
1638 	 *                     = -(LocalTZA + DaylightSavingTA(t))
1639 	 *
1640 	 *  where DaylightSavingTA() is checked for time 't'.
1641 	 *
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.
1645 	 *
1646 	 */
1647 
1648 	duk_double_t d;
1649 	duk_int_t tzoffset;
1650 
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));
1654 	if (DUK_ISNAN(d)) {
1655 		duk_push_nan(ctx);
1656 	} else {
1657 		DUK_ASSERT(DUK_ISFINITE(d));
1658 		tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);
1659 		duk_push_int(ctx, -tzoffset / 60);
1660 	}
1661 	return 1;
1662 }
1663 
1664 /*
1665  *  Setters.
1666  *
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:
1672  *
1673  *    var d = new Date(1234567890);
1674  *
1675  *  is equivalent to:
1676  *
1677  *    var d = new Date(0);
1678  *    d.setUTCMilliseconds(1234567890);
1679  *
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
1682  *  helper provides:
1683  *
1684  *    setMilliseconds()
1685  *    setUTCMilliseconds()
1686  *    setSeconds()
1687  *    setUTCSeconds()
1688  *    setMinutes()
1689  *    setUTCMinutes()
1690  *    setHours()
1691  *    setUTCHours()
1692  *    setDate()
1693  *    setUTCDate()
1694  *    setMonth()
1695  *    setUTCMonth()
1696  *    setFullYear()
1697  *    setUTCFullYear()
1698  *    setYear()
1699  *
1700  *  Notes:
1701  *
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.
1705  *
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.
1711  */
1712 
duk_bi_date_prototype_set_shared(duk_context * ctx)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);
1716 }
1717 
duk_bi_date_prototype_set_time(duk_context * ctx)1718 DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx) {
1719 	duk_double_t d;
1720 
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);
1724 	duk_dup_top(ctx);
1725 	duk_put_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE); /* -> [ timeval this timeval ] */
1726 
1727 	return 1;
1728 }
1729