xref: /inferno-os/appl/lib/ecmascript/date.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1# the Date object is founded on the Daytime module
2
3UTC: con 1;
4msPerSec: con big 1000;
5
6# based on Daytime->Tm with big fields
7bigTm: adt {
8	ms:	big;
9	sec:	big;
10	min:	big;
11	hour:	big;
12	mday:	big;
13	mon:	big;
14	year:	big;
15	tzoff:	int;
16};
17
18isfinite(r: real): int
19{
20	if(math->isnan(r) || r == +Infinity || r == -Infinity)
21		return 0;
22	return 1;
23}
24
25time2Tm(t: real, utc: int): ref Daytime->Tm
26{
27	secs := int(big t / msPerSec);
28	if(big t % msPerSec < big 0)	# t<0?
29		secs -= 1;
30	if(utc)
31		tm := daytime->gmt(secs);
32	else
33		tm = daytime->local(secs);
34	return tm;
35}
36
37time2bigTm(t: real, utc: int): ref bigTm
38{
39	tm := time2Tm(t, utc);
40	btm := ref bigTm;
41	btm.ms = big t % msPerSec;
42	if(btm.ms < big 0)
43		btm.ms += msPerSec;
44	btm.sec = big tm.sec;
45	btm.min = big tm.min;
46	btm.hour = big tm.hour;
47	btm.mday = big tm.mday;
48	btm.mon = big tm.mon;
49	btm.year = big tm.year;
50	btm.tzoff = tm.tzoff;
51	return btm;
52}
53
54bigTm2time(btm: ref bigTm): real
55{
56	# normalize
57	if(btm.mon / big 12 != big 0){
58		btm.year += btm.mon / big 12;
59		btm.mon %= big 12;
60	}
61	if(btm.ms / msPerSec != big 0){
62		btm.sec += btm.ms / msPerSec;
63		btm.ms %= msPerSec;
64	}
65	if(btm.sec / big 60 != big 0){
66		btm.min += btm.sec / big 60;
67		btm.sec %= big 60;
68	}
69	if(btm.min / big 60 != big 0){
70		btm.hour += btm.hour / big 60;
71		btm.min %= big 60;
72	}
73	if(btm.hour / big 24 != big 0){
74		btm.mday += btm.mday / big 24;
75		btm.hour %= big 24;
76	}
77
78	tm := ref Tm;
79	tm.sec = int btm.sec;
80	tm.min = int btm.min;
81	tm.hour = int btm.hour;
82	tm.mday = int btm.mday;
83	tm.mon = int btm.mon;
84	tm.year = int btm.year;
85	tm.tzoff = btm.tzoff;
86	secs := daytime->tm2epoch(tm);
87	# check for out-of-band times
88	if(secs == daytime->tm2epoch(daytime->gmt(secs)))
89		r := real(big secs * msPerSec + btm.ms);
90	else
91		r = Math->NaN;
92	return r;
93}
94
95str2time(s: string): real
96{
97	tm := daytime->string2tm(s);
98	if(tm == nil)
99		r := Math->NaN;
100	else
101		r = real (big daytime->tm2epoch(tm) * msPerSec);
102	return r;
103}
104
105cdate(nil: ref Exec, nil, nil: ref Ecmascript->Obj, nil: array of ref Val): ref Val
106{
107	return strval(daytime->time());
108}
109
110# process arguments of Date() [called as constructor] and Date.UTC()
111datectorargs(ex: ref Exec, args: array of ref Val): (int, ref bigTm)
112{
113	x := array[7] of { * => big 0 };
114	ok := 1;
115	for(i := 0; i < 7 && i < len args; i++){
116		if(!isfinite(toNumber(ex, biarg(args, i))))
117			ok = 0;
118		else
119			x[i] = big toInteger(ex, biarg(args, i));
120	}
121	btm := ref bigTm;
122	yr := x[0];
123	if(yr >= big 0 && yr <= big 99)
124		btm.year = yr;
125	else
126		btm.year = yr - big 1900;
127	btm.mon = x[1];
128	btm.mday = x[2];
129	btm.hour = x[3];
130	btm.min = x[4];
131	btm.sec = x[5];
132	btm.ms = x[6];
133	return (ok, btm);
134}
135
136ndate(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj
137{
138	o := mkobj(ex.dateproto, "Date");
139	r := Math->NaN;
140	case len args{
141	0 =>
142		r = real(big daytime->now() * msPerSec);
143	1 =>
144		v := toPrimitive(ex, biarg(args, 0), NoHint);
145		if(isstr(v))
146			r = str2time(v.str);
147		else if(isfinite(toNumber(ex, v))){
148			t := big toInteger(ex, v);
149			secs := t / msPerSec;
150			if(big t % msPerSec < big 0)
151				secs -= big 1;
152			if(secs == big int secs)
153				r = real t;
154		}
155	* =>
156		(ok, btm) := datectorargs(ex, args);
157		if(ok){
158			tm := daytime->local(daytime->now());
159			btm.tzoff = tm.tzoff;
160			r = bigTm2time(btm);
161		}
162	}
163	o.val = numval(r);
164	return o;
165}
166
167cdateparse(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
168{
169	s := toString(ex, biarg(args, 0));
170	return numval(str2time(s));
171}
172
173cdateUTC(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val
174{
175	r := Math->NaN;
176	if(len args == 0)
177		r = real(big daytime->now() * msPerSec);
178	else{
179		(ok, btm) := datectorargs(ex, args);
180		if(ok){
181			btm.tzoff = 0;
182			r = bigTm2time(btm);
183		}
184	}
185	return numval(r);
186}
187
188datecheck(ex: ref Exec, o: ref Ecmascript->Obj, f: string)
189{
190	if(!isdateobj(o))
191		runtime(ex, TypeError, "Date.prototype." + f + " called on non-Date object");
192}
193
194cdateprototoString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
195{
196	datecheck(ex, this, f.val.str);
197	secs := 0;
198	t := this.val.num;
199	if(!math->isnan(t)){
200		secs = int(big t / msPerSec);
201		if(big t % msPerSec < big 0)
202			secs -= 1;
203	}
204	return strval(daytime->text(daytime->local(secs)));
205}
206
207cdateprototoDateString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
208{
209	datecheck(ex, this, f.val.str);
210	secs := 0;
211	t := this.val.num;
212	if(!math->isnan(t)){
213		secs = int(big t / msPerSec);
214		if(big t % msPerSec < big 0)
215			secs -= 1;
216	}
217	s := daytime->text(daytime->local(secs));
218	(n, ls) := sys->tokenize(s, " ");
219	if(n < 3)
220		return strval("");
221	return strval(hd ls + " " + hd tl ls + " " + hd tl tl ls);
222}
223
224cdateprototoTimeString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
225{
226	datecheck(ex, this, f.val.str);
227	secs := 0;
228	t := this.val.num;
229	if(!math->isnan(t)){
230		secs = int(big t / msPerSec);
231		if(big t % msPerSec < big 0)
232			secs -= 1;
233	}
234	s := daytime->text(daytime->local(secs));
235	(n, ls) := sys->tokenize(s, " ");
236	if(n < 4)
237		return strval("");
238	return strval(hd tl tl tl ls);
239}
240
241cdateprotovalueOf(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
242{
243	datecheck(ex, this, f.val.str);
244	return this.val;
245}
246
247cdateprotoget(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val, utc: int): ref Val
248{
249	datecheck(ex, this, f.val.str);
250	t := this.val.num;
251	if(!math->isnan(t)){
252		tm := time2Tm(t, utc);
253		case f.val.str{
254		"Date.prototype.getYear" =>
255			if (tm.year < 0 || tm.year > 99)
256				t = real(tm.year + 1900);
257			else
258				t = real tm.year;
259		"Date.prototype.getFullYear" or
260		"Date.prototype.getUTCFullYear" =>
261			t = real(tm.year + 1900);
262		"Date.prototype.getMonth" or
263		"Date.prototype.getUTCMonth" =>
264			t = real tm.mon;
265		"Date.prototype.getDate" or
266		"Date.prototype.getUTCDate" =>
267			t = real tm.mday;
268		"Date.prototype.getDay" or
269		"Date.prototype.getUTCDay" =>
270			t = real tm.wday;
271		"Date.prototype.getHours" or
272		"Date.prototype.getUTCHours" =>
273			t = real tm.hour;
274		"Date.prototype.getMinutes" or
275		"Date.prototype.getUTCMinutes" =>
276			t = real tm.min;
277		"Date.prototype.getSeconds" or
278		"Date.prototype.getUTCSeconds" =>
279			t = real tm.sec;
280		}
281	}
282	return numval(t);
283}
284
285cdateprotogetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
286{
287	datecheck(ex, this, f.val.str);
288	t := this.val.num;
289	if(!math->isnan(t)){
290		ms := big t % msPerSec;
291		if(ms < big 0)
292			ms += msPerSec;
293		t = real ms;
294	}
295	return numval(t);
296}
297
298cdateprotogetTimezoneOffset(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
299{
300	datecheck(ex, this, f.val.str);
301	t := this.val.num;
302	if(!math->isnan(t)){
303		tm := time2Tm(t, !UTC);
304		t = real(tm.tzoff / 60);
305	}
306	return numval(t);
307}
308
309# process arguments of Date.prototype.set*() functions
310dateprotosetargs(ex: ref Exec, this: ref Ecmascript->Obj, args: array of ref Val, n: int): (int, big, big, big, big)
311{
312	x := array[4] of { * => big 0 };
313	ok := 1;
314	if(this != nil && !isfinite(this.val.num))
315		ok = 0;
316	for(i := 0; i < n && i < len args; i++){
317		if(!isfinite(toNumber(ex, biarg(args, i))))
318			ok = 0;
319		else
320			x[i] = big toInteger(ex, biarg(args, i));
321	}
322	return (ok, x[0], x[1], x[2], x[3]);
323}
324
325cdateprotosetTime(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
326{
327	datecheck(ex, this, f.val.str);
328	r := Math->NaN;
329	(ok, t, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1);
330	if(ok){
331		secs := t / msPerSec;
332		if(big t % msPerSec < big 0)
333			secs -= big 1;
334		if(secs == big int secs)
335			r = real t;
336	}
337	this.val.num = r;
338	return numval(r);
339}
340
341cdateprotosetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
342{
343	datecheck(ex, this, f.val.str);
344	r := Math->NaN;
345	(ok, ms, nil, nil, nil) := dateprotosetargs(ex, this, args, 1);
346	if(ok){
347		btm := time2bigTm(this.val.num, utc);
348		btm.ms = ms;
349		r = bigTm2time(btm);
350	}
351	this.val.num = r;
352	return numval(r);
353}
354
355cdateprotosetSeconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
356{
357	datecheck(ex, this, f.val.str);
358	r := Math->NaN;
359	(ok, sec, ms, nil, nil) := dateprotosetargs(ex, this, args, 2);
360	if(ok){
361		btm := time2bigTm(this.val.num, utc);
362		btm.sec = sec;
363		if(len args > 1)
364			btm.ms = ms;
365		r = bigTm2time(btm);
366	}
367	this.val.num = r;
368	return numval(r);
369}
370
371cdateprotosetMinutes(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
372{
373	datecheck(ex, this, f.val.str);
374	r := Math->NaN;
375	(ok, min, sec, ms, nil) := dateprotosetargs(ex, this, args, 3);
376	if(ok){
377		btm := time2bigTm(this.val.num, utc);
378		btm.min = min;
379		if(len args > 1){
380			btm.sec = sec;
381			if(len args > 2)
382				btm.ms = ms;
383		}
384		r = bigTm2time(btm);
385	}
386	this.val.num = r;
387	return numval(r);
388}
389
390cdateprotosetHours(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
391{
392	datecheck(ex, this, f.val.str);
393	r := Math->NaN;
394	(ok, hour, min, sec, ms) := dateprotosetargs(ex, this, args, 4);
395	if(ok){
396		btm := time2bigTm(this.val.num, utc);
397		btm.hour = hour;
398		if(len args > 1){
399			btm.min = min;
400			if(len args > 2){
401				btm.sec = sec;
402				if(len args > 3)
403					btm.ms = ms;
404			}
405		}
406		r = bigTm2time(btm);
407	}
408	this.val.num = r;
409	return numval(r);
410}
411
412cdateprotosetDate(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
413{
414	datecheck(ex, this, f.val.str);
415	r := Math->NaN;
416	(ok, day, nil, nil, nil) := dateprotosetargs(ex, this, args, 1);
417	if(ok){
418		btm := time2bigTm(this.val.num, utc);
419		btm.mday = day;
420		r = bigTm2time(btm);
421	}
422	this.val.num = r;
423	return numval(r);
424}
425
426cdateprotosetMonth(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
427{
428	datecheck(ex, this, f.val.str);
429	r := Math->NaN;
430	(ok, mon, day, nil, nil) := dateprotosetargs(ex, this, args, 2);
431	if(ok){
432		btm := time2bigTm(this.val.num, utc);
433		btm.mon = mon;
434		if(len args > 1)
435			btm.mday = day;
436		r = bigTm2time(btm);
437	}
438	this.val.num = r;
439	return numval(r);
440}
441
442cdateprotosetFullYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val
443{
444	datecheck(ex, this, f.val.str);
445	r := Math->NaN;
446	(ok, year, mon, day, nil) := dateprotosetargs(ex, nil, args, 3);
447	if(ok){
448		t := this.val.num;
449		if(!isfinite(t))
450			t = 0.;
451		btm := time2bigTm(t, utc);
452		btm.year = (year - big 1900);
453		if(len args > 1){
454			btm.mon = mon;
455			if(len args > 2)
456				btm.mday = day;
457		}
458		r = bigTm2time(btm);
459	}
460	this.val.num = r;
461	return numval(r);
462}
463
464cdateprotosetYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val
465{
466	datecheck(ex, this, f.val.str);
467	r := Math->NaN;
468	(ok, year, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1);
469	if(ok){
470		t := this.val.num;
471		if(!isfinite(t))
472			t = 0.;
473		btm := time2bigTm(t, !UTC);
474		if(year >= big 0 && year <= big 99)
475			btm.year = year;
476		else
477			btm.year = year - big 1900;
478		r = bigTm2time(btm);
479	}
480	this.val.num = r;
481	return numval(r);
482}
483
484cdateprototoUTCString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val
485{
486	datecheck(ex, this, f.val.str);
487	secs := 0;
488	t := this.val.num;
489	if(!math->isnan(t)){
490		secs = int(big t / msPerSec);
491		if(big t % msPerSec < big 0)
492			secs -= 1;
493	}
494	return strval(daytime->text(daytime->gmt(secs)));
495}
496