# the Date object is founded on the Daytime module UTC: con 1; msPerSec: con big 1000; # based on Daytime->Tm with big fields bigTm: adt { ms: big; sec: big; min: big; hour: big; mday: big; mon: big; year: big; tzoff: int; }; isfinite(r: real): int { if(math->isnan(r) || r == +Infinity || r == -Infinity) return 0; return 1; } time2Tm(t: real, utc: int): ref Daytime->Tm { secs := int(big t / msPerSec); if(big t % msPerSec < big 0) # t<0? secs -= 1; if(utc) tm := daytime->gmt(secs); else tm = daytime->local(secs); return tm; } time2bigTm(t: real, utc: int): ref bigTm { tm := time2Tm(t, utc); btm := ref bigTm; btm.ms = big t % msPerSec; if(btm.ms < big 0) btm.ms += msPerSec; btm.sec = big tm.sec; btm.min = big tm.min; btm.hour = big tm.hour; btm.mday = big tm.mday; btm.mon = big tm.mon; btm.year = big tm.year; btm.tzoff = tm.tzoff; return btm; } bigTm2time(btm: ref bigTm): real { # normalize if(btm.mon / big 12 != big 0){ btm.year += btm.mon / big 12; btm.mon %= big 12; } if(btm.ms / msPerSec != big 0){ btm.sec += btm.ms / msPerSec; btm.ms %= msPerSec; } if(btm.sec / big 60 != big 0){ btm.min += btm.sec / big 60; btm.sec %= big 60; } if(btm.min / big 60 != big 0){ btm.hour += btm.hour / big 60; btm.min %= big 60; } if(btm.hour / big 24 != big 0){ btm.mday += btm.mday / big 24; btm.hour %= big 24; } tm := ref Tm; tm.sec = int btm.sec; tm.min = int btm.min; tm.hour = int btm.hour; tm.mday = int btm.mday; tm.mon = int btm.mon; tm.year = int btm.year; tm.tzoff = btm.tzoff; secs := daytime->tm2epoch(tm); # check for out-of-band times if(secs == daytime->tm2epoch(daytime->gmt(secs))) r := real(big secs * msPerSec + btm.ms); else r = Math->NaN; return r; } str2time(s: string): real { tm := daytime->string2tm(s); if(tm == nil) r := Math->NaN; else r = real (big daytime->tm2epoch(tm) * msPerSec); return r; } cdate(nil: ref Exec, nil, nil: ref Ecmascript->Obj, nil: array of ref Val): ref Val { return strval(daytime->time()); } # process arguments of Date() [called as constructor] and Date.UTC() datectorargs(ex: ref Exec, args: array of ref Val): (int, ref bigTm) { x := array[7] of { * => big 0 }; ok := 1; for(i := 0; i < 7 && i < len args; i++){ if(!isfinite(toNumber(ex, biarg(args, i)))) ok = 0; else x[i] = big toInteger(ex, biarg(args, i)); } btm := ref bigTm; yr := x[0]; if(yr >= big 0 && yr <= big 99) btm.year = yr; else btm.year = yr - big 1900; btm.mon = x[1]; btm.mday = x[2]; btm.hour = x[3]; btm.min = x[4]; btm.sec = x[5]; btm.ms = x[6]; return (ok, btm); } ndate(ex: ref Exec, nil: ref Ecmascript->Obj, args: array of ref Val): ref Ecmascript->Obj { o := mkobj(ex.dateproto, "Date"); r := Math->NaN; case len args{ 0 => r = real(big daytime->now() * msPerSec); 1 => v := toPrimitive(ex, biarg(args, 0), NoHint); if(isstr(v)) r = str2time(v.str); else if(isfinite(toNumber(ex, v))){ t := big toInteger(ex, v); secs := t / msPerSec; if(big t % msPerSec < big 0) secs -= big 1; if(secs == big int secs) r = real t; } * => (ok, btm) := datectorargs(ex, args); if(ok){ tm := daytime->local(daytime->now()); btm.tzoff = tm.tzoff; r = bigTm2time(btm); } } o.val = numval(r); return o; } cdateparse(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { s := toString(ex, biarg(args, 0)); return numval(str2time(s)); } cdateUTC(ex: ref Exec, nil, nil: ref Ecmascript->Obj, args: array of ref Val): ref Val { r := Math->NaN; if(len args == 0) r = real(big daytime->now() * msPerSec); else{ (ok, btm) := datectorargs(ex, args); if(ok){ btm.tzoff = 0; r = bigTm2time(btm); } } return numval(r); } datecheck(ex: ref Exec, o: ref Ecmascript->Obj, f: string) { if(!isdateobj(o)) runtime(ex, "Date.prototype." + f + " called on non-Date object"); } cdateprototoString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { datecheck(ex, this, f.val.str); secs := 0; t := this.val.num; if(!math->isnan(t)){ secs = int(big t / msPerSec); if(big t % msPerSec < big 0) secs -= 1; } return strval(daytime->text(daytime->local(secs))); } cdateprotovalueOf(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { datecheck(ex, this, f.val.str); return this.val; } cdateprotoget(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); t := this.val.num; if(!math->isnan(t)){ tm := time2Tm(t, utc); case f.val.str{ "Date.prototype.getYear" => t = real tm.year; "Date.prototype.getFullYear" or "Date.prototype.getUTCFullYear" => t = real(tm.year + 1900); "Date.prototype.getMonth" or "Date.prototype.getUTCMonth" => t = real tm.mon; "Date.prototype.getDate" or "Date.prototype.getUTCDate" => t = real tm.mday; "Date.prototype.getDay" or "Date.prototype.getUTCDay" => t = real tm.wday; "Date.prototype.getHours" or "Date.prototype.getUTCHours" => t = real tm.hour; "Date.prototype.getMinutes" or "Date.prototype.getUTCMinutes" => t = real tm.min; "Date.prototype.getSeconds" or "Date.prototype.getUTCSeconds" => t = real tm.sec; } } return numval(t); } cdateprotogetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { datecheck(ex, this, f.val.str); t := this.val.num; if(!math->isnan(t)){ ms := big t % msPerSec; if(ms < big 0) ms += msPerSec; t = real ms; } return numval(t); } cdateprotogetTimezoneOffset(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { datecheck(ex, this, f.val.str); t := this.val.num; if(!math->isnan(t)){ tm := time2Tm(t, !UTC); t = real(tm.tzoff / 60); } return numval(t); } # process arguments of Date.prototype.set*() functions dateprotosetargs(ex: ref Exec, this: ref Ecmascript->Obj, args: array of ref Val, n: int): (int, big, big, big, big) { x := array[4] of { * => big 0 }; ok := 1; if(this != nil && !isfinite(this.val.num)) ok = 0; for(i := 0; i < n && i < len args; i++){ if(!isfinite(toNumber(ex, biarg(args, i)))) ok = 0; else x[i] = big toInteger(ex, biarg(args, i)); } return (ok, x[0], x[1], x[2], x[3]); } cdateprotosetTime(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, t, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1); if(ok){ secs := t / msPerSec; if(big t % msPerSec < big 0) secs -= big 1; if(secs == big int secs) r = real t; } this.val.num = r; return numval(r); } cdateprotosetMilliseconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, ms, nil, nil, nil) := dateprotosetargs(ex, this, args, 1); if(ok){ btm := time2bigTm(this.val.num, utc); btm.ms = ms; r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetSeconds(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, sec, ms, nil, nil) := dateprotosetargs(ex, this, args, 2); if(ok){ btm := time2bigTm(this.val.num, utc); btm.sec = sec; if(len args > 1) btm.ms = ms; r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetMinutes(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, min, sec, ms, nil) := dateprotosetargs(ex, this, args, 3); if(ok){ btm := time2bigTm(this.val.num, utc); btm.min = min; if(len args > 1){ btm.sec = sec; if(len args > 2) btm.ms = ms; } r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetHours(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, hour, min, sec, ms) := dateprotosetargs(ex, this, args, 4); if(ok){ btm := time2bigTm(this.val.num, utc); btm.hour = hour; if(len args > 1){ btm.min = min; if(len args > 2){ btm.sec = sec; if(len args > 3) btm.ms = ms; } } r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetDate(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, day, nil, nil, nil) := dateprotosetargs(ex, this, args, 1); if(ok){ btm := time2bigTm(this.val.num, utc); btm.mday = day; r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetMonth(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, mon, day, nil, nil) := dateprotosetargs(ex, this, args, 2); if(ok){ btm := time2bigTm(this.val.num, utc); btm.mon = mon; if(len args > 1) btm.mday = day; r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetFullYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val, utc: int): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, year, mon, day, nil) := dateprotosetargs(ex, nil, args, 3); if(ok){ t := this.val.num; if(!isfinite(t)) t = 0.; btm := time2bigTm(t, utc); btm.year = (year - big 1900); if(len args > 1){ btm.mon = mon; if(len args > 2) btm.mday = day; } r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprotosetYear(ex: ref Exec, f, this: ref Ecmascript->Obj, args: array of ref Val): ref Val { datecheck(ex, this, f.val.str); r := Math->NaN; (ok, year, nil, nil, nil) := dateprotosetargs(ex, nil, args, 1); if(ok){ t := this.val.num; if(!isfinite(t)) t = 0.; btm := time2bigTm(t, !UTC); if(year >= big 0 && year <= big 99) btm.year = year; else btm.year = year - big 1900; r = bigTm2time(btm); } this.val.num = r; return numval(r); } cdateprototoUTCString(ex: ref Exec, f, this: ref Ecmascript->Obj, nil: array of ref Val): ref Val { datecheck(ex, this, f.val.str); secs := 0; t := this.val.num; if(!math->isnan(t)){ secs = int(big t / msPerSec); if(big t % msPerSec < big 0) secs -= 1; } return strval(daytime->text(daytime->gmt(secs))); }