var time = this; time.second = 1; time.minute = 60; time.hour = 3600; time.day = 86400; time.week = 604800; 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; time.hour2minute = function() { return this.hour / this.minute; }; time.day2hour = function() { return this.day / this.hour; }; time.minute2second = function() { return this.minute / this.second; }; time.week2day = function() { return this.week / this.day; }; time.strparse = { yyyy: "year", mm: "month", m: "month", eee: "yday", dd: "day", d: "day", v: "weekday", hh: "hour", h: "hour", nn: "minute", n: "minute", ss: "second", s: "second", }; time.isleap = function(year) { return this.yearsize(year) === 366; }; time.yearsize = function(y) { if (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) return 366; return 365; }; time.timecode = function(t, fps = 24) { var s = Math.trunc(t); t -= s; return `${s}:${Math.trunc(fps * s)}`; }; time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; time.zones = { "-12": "IDLW" }; time.record = function(num, zone = this.computer_zone()) { if (typeof num === "object") { return num; } else if (typeof num === "number") { var monthdays = this.monthdays.slice(); var rec = { second: 0, minute: 0, hour: 0, yday: 0, year: 0, zone: 0, }; rec.zone = zone; num += zone * this.hour; var hms = num % this.day; var day = Math.floor(num / this.day); if (hms < 0) { hms += this.day; day--; } rec.second = hms % this.minute; var d1 = Math.floor(hms / this.minute); rec.minute = d1 % this.minute; rec.hour = Math.floor(d1 / this.minute); rec.weekday = (day + 4503599627370496 + 2) % 7; var yIter = this.epoch; if (day >= 0) { for (yIter = this.epoch; day >= this.yearsize(yIter); yIter++) { day -= this.yearsize(yIter); } } else { for (yIter = this.epoch; day < 0; yIter--) { day += this.yearsize(yIter - 1); } } rec.year = yIter; rec.ce = (rec.year <= 0) ? "BC" : "AD"; rec.yday = day; if (this.yearsize(yIter) === 366) { monthdays[1] = 29; } var d0 = day; for (d1 = 0; d0 >= monthdays[d1]; d1++) { d0 -= monthdays[d1]; } monthdays[1] = 28; rec.day = d0 + 1; rec.month = d1; return rec; } }; time.number = function(rec) { if (typeof rec === "number") { return rec; } else if (typeof rec === "object") { 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 yday = rec.yday || 0; if (year > this.epoch) { for (var i = this.epoch; i < year; i++) { c += this.day * this.yearsize(i); } } else if (year < this.epoch) { for (var i = this.epoch - 1; i > year; i--) { c += this.day * this.yearsize(i); } c += (this.yearsize(year) - yday - 1) * this.day; c += (this.day2hour() - hour - 1) * this.hour; c += (this.hour2minute() - minute - 1) * this.minute; c += this.minute2second() - second; c += zone * this.hour; c *= -1; return c; } c += second; c += minute * this.minute; c += hour * this.hour; c += yday * this.day; c -= zone * this.hour; return c; } }; time.fmt = "vB mB d h:nn:ss TZz a y c"; time.text = function(num, fmt = this.fmt, zone) { var rec = (typeof num === "number") ? time.record(num, zone) : num; zone = rec.zone; if (fmt.match("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"); } } var year = rec.year > 0 ? rec.year : (rec.year - 1); if (fmt.match("c")) { if (year < 0) { year = Math.abs(year); fmt = fmt.replaceAll("c", "BC"); } else { fmt = fmt.replaceAll("c", "AD"); } } fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0")); fmt = fmt.replaceAll("y", year); fmt = fmt.replaceAll("eee", rec.yday + 1); fmt = fmt.replaceAll("dd", rec.day.toString().padStart(2, "0")); fmt = fmt.replaceAll("d", rec.day); fmt = fmt.replaceAll("hh", rec.hour.toString().padStart(2, "0")); fmt = fmt.replaceAll("h", rec.hour); fmt = fmt.replaceAll("nn", rec.minute.toString().padStart(2, "0")); fmt = fmt.replaceAll("n", rec.minute); fmt = fmt.replaceAll("ss", rec.second.toFixed(2).padStart(2, "0")); fmt = fmt.replaceAll("s", rec.second); fmt = fmt.replaceAll("z", zone >= 0 ? "+" + zone : zone); 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", this.monthstr[rec.month].slice(0, 3)); fmt = fmt.replaceAll("mB", this.monthstr[rec.month]); fmt = fmt.replaceAll("vB", this.weekdays[rec.weekday]); fmt = fmt.replaceAll("vb", this.weekdays[rec.weekday].slice(0, 3)); return fmt; }; time[prosperon.DOC] = { doc: `The main time object, handling date/time utilities in earth-seconds.`, second: `Number of seconds in a (real) second (always 1).`, minute: `Number of seconds in a minute (60).`, hour: `Number of seconds in an hour (3600).`, day: `Number of seconds in a day (86400).`, week: `Number of seconds in a week (604800).`, weekdays: `Names of the days of the week, Sunday through Saturday.`, monthstr: `Full names of the months of the year, January through December.`, epoch: `Base epoch year, from which day 0 is calculated (default 1970).`, hour2minute: `Return the ratio of hour to minute in seconds, e.g. 3600 / 60 => 60.`, day2hour: `Return the ratio of day to hour in seconds, e.g. 86400 / 3600 => 24.`, minute2second: `Return the ratio of minute to second in seconds, e.g. 60 / 1 => 60.`, week2day: `Return the ratio of week to day in seconds, e.g. 604800 / 86400 => 7.`, strparse: `Mapping of format tokens (yyyy, mm, dd, etc.) to time fields (year, month, day...).`, isleap: `Return true if a given year is leap, based on whether it has 366 days.`, yearsize: `Given a year, return 365 or 366 depending on leap-year rules.`, timecode: `Convert seconds into a "S:frames" timecode string, with optional FPS (default 24).`, monthdays: `An array of days in each month for a non-leap year.`, zones: `Table of recognized time zone abbreviations, with offsets (e.g., "-12" -> "IDLW").`, record: `Convert a timestamp (in seconds) into a record with fields like day, month, year, etc.`, number: `Convert a record back into a numeric timestamp (seconds).`, fmt: `Default format string for time.text(), containing tokens like 'yyyy', 'dd', 'hh', etc.`, text: `Format a numeric or record time into a string using a format pattern, e.g. 'hh:nn:ss'.`, now: `Return the current system time in seconds (implemented in C extension).`, computer_dst: `Return true if local system time is currently in DST (implemented in C extension).`, computer_zone: `Return local time zone offset from UTC in hours (implemented in C extension).` }; return time;