// epoch = 0000-01-01 00:00:00 +0000 var time = this; /* -------- host helpers -------------------------------------------------- */ var now = time.now; // seconds since Misty epoch (C stub) var computer_zone = time.computer_zone; // integral hours, no DST var computer_dst = time.computer_dst; // true ↔ DST in effect delete time.now; delete time.computer_zone; delete time.computer_dst; /* -------- units & static tables ----------------------------------------- */ time.second = 1; time.minute = 60; time.hour = 3_600; time.day = 86_400; time.week = 604_800; time.weekdays = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; time.monthstr = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; time.epoch = 1970; /* ratios (kept K&R-style) */ time.hour2minute = function() { return time.hour / time.minute; }; time.day2hour = function() { return time.day / time.hour; }; time.minute2second = function() { return time.minute / time.second; }; time.week2day = function() { return time.week / time.day; }; /* leap-year helpers */ time.yearsize = function yearsize(y) { if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366; return 365; }; time.isleap = function(y) { return time.yearsize(y) == 366; }; /* timecode utility */ time.timecode = function(t, fps = 24) { var s = number.whole(t); var frac = t - s; return `${s}:${number.whole(frac * fps)}`; }; /* per-month day counts (non-leap) */ time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // convert seconds-since-epoch → broken-down record function time_record(num = now(), zone = computer_zone(), dst = computer_dst()) { if (is_object(num)) return num; var monthdays = time.monthdays.slice(); var rec = { second : 0, minute : 0, hour : 0, yday : 0, year : 0, weekday: 0, month : 0, day : 0, zone : zone, dst : !!dst, ce : "AD" }; /* include DST in the effective offset */ var offset = zone + (dst ? 1 : 0); num += offset * time.hour; /* split into day + seconds-of-day */ var hms = num % time.day; var day = number.floor(num / time.day); if (hms < 0) { hms += time.day; day--; } rec.second = hms % time.minute; var tmp = number.floor(hms / time.minute); rec.minute = tmp % time.minute; rec.hour = number.floor(tmp / time.minute); rec.weekday = (day + 4_503_599_627_370_496 + 2) % 7; /* 2 → 1970-01-01 was Thursday */ /* year & day-of-year */ var y = time.epoch; if (day >= 0) { for (y = time.epoch; day >= time.yearsize(y); y++) day -= time.yearsize(y); } else { for (y = time.epoch; day < 0; y--) day += time.yearsize(y - 1); } rec.year = y; rec.ce = (y <= 0) ? "BC" : "AD"; rec.yday = day; /* month & month-day */ if (time.yearsize(y) == 366) monthdays[1] = 29; var m = 0; for (; day >= monthdays[m]; m++) day -= monthdays[m]; rec.month = m; rec.day = day + 1; return rec; } function time_number(rec = now()) { if (is_number(rec)) return rec; log.console(rec) log.console(rec.minute) var c = 0; var year = rec.year || 0; var hour = rec.hour || 0; var minute = rec.minute || 0; var second = rec.second || 0; var zone = rec.zone || 0; var dst = rec.dst ? 1 : 0; var yday = rec.yday || 0; if (year > time.epoch) { for (var i = time.epoch; i < year; i++) c += time.day * time.yearsize(i); } else if (year < time.epoch) { for (var i = time.epoch - 1; i > year; i--) c += time.day * time.yearsize(i); c += (time.yearsize(year) - yday - 1) * time.day; c += (time.day2hour() - hour - 1) * time.hour; c += (time.hour2minute() - minute - 1) * time.minute; c += time.minute2second() - second; c += (zone + dst) * time.hour; return -c; /* BCE */ } c = second; c += minute * time.minute; c += hour * time.hour; c += yday * time.day; c -= (zone + dst) * time.hour; return c; } // text formatting var default_fmt = "vB mB d hh:nn:ss a z y c"; /* includes new DST token */ function time_text(num = now(), fmt = default_fmt, zone = computer_zone(), dst = computer_dst()) { var rec = is_number(num) ? time_record(num, zone, dst) : num; zone = rec.zone; dst = rec.dst; /* am/pm */ if (fmt.includes("a")) { if (rec.hour >= 13) { rec.hour -= 12; fmt = fmt.replaceAll("a", "PM"); } else if (rec.hour == 12) { fmt = fmt.replaceAll("a", "PM"); } else if (rec.hour == 0) { rec.hour = 12; fmt = fmt.replaceAll("a", "AM"); } else fmt = fmt.replaceAll("a", "AM"); } /* BCE/CE */ var year = rec.year > 0 ? rec.year : rec.year - 1; if (fmt.includes("c")) { if (year < 0) { year = number.abs(year); fmt = fmt.replaceAll("c", "BC"); } else fmt = fmt.replaceAll("c", "AD"); } /* substitutions */ var full_offset = zone + (dst ? 1 : 0); fmt = fmt.replaceAll("yyyy", text(year, "i4")) fmt = fmt.replaceAll("y", year); fmt = fmt.replaceAll("eee", rec.yday + 1); fmt = fmt.replaceAll("dd", text(rec.day, "i2")) fmt = fmt.replaceAll("d", rec.day); fmt = fmt.replaceAll("hh", text(rec.hour, "i2")); fmt = fmt.replaceAll("h", rec.hour); fmt = fmt.replaceAll("nn", text(rec.minute, "i2")); fmt = fmt.replaceAll("n", rec.minute); fmt = fmt.replaceAll("ss", text(rec.second, "i2")); fmt = fmt.replaceAll("s", rec.second); fmt = fmt.replaceAll("x", dst ? "DST" : ""); /* new */ fmt = fmt.replaceAll("z", (full_offset >= 0 ? "+" : "") + text(full_offset)); fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1); fmt = fmt.replaceAll(/m[^bB]/g, rec.month + 1); fmt = fmt.replaceAll(/v[^bB]/g, rec.weekday); fmt = fmt.replaceAll("mb", time.monthstr[rec.month].slice(0, 3)); fmt = fmt.replaceAll("mB", time.monthstr[rec.month]); fmt = fmt.replaceAll("vB", time.weekdays[rec.weekday]); fmt = fmt.replaceAll("vb", time.weekdays[rec.weekday].slice(0, 3)); return fmt; } return { record: time_record, number: time_number, text: time_text };