improve time

This commit is contained in:
2025-05-29 02:56:30 -05:00
parent 4e118dd8e9
commit b4371ba3e0
5 changed files with 248 additions and 221 deletions

View File

@@ -22,12 +22,10 @@ function caller_data(depth = 0)
function console_rec(line, file, msg) { function console_rec(line, file, msg) {
return `[${prosperon.id.slice(0,5)}] [${file}:${line}]: ${msg}\n` return `[${prosperon.id.slice(0,5)}] [${file}:${line}]: ${msg}\n`
var now = time.now()
var id = prosperon.name ? prosperon.name : prosperon.id var id = prosperon.name ? prosperon.name : prosperon.id
id = id.substring(0,6) id = id.substring(0,6)
return `[${id}] [${time.text(now, "mb d yyyy h:nn:ss")}] ${file}:${line}: ${msg}\n` return `[${id}] [${time.text("mb d yyyy h:nn:ss")}] ${file}:${line}: ${msg}\n`
} }
var console_mod = prosperon.hidden.console var console_mod = prosperon.hidden.console

View File

@@ -27,7 +27,7 @@ graphics.Image = function(surfaceData) {
this[CPU] = surfaceData || undefined; this[CPU] = surfaceData || undefined;
this[GPU] = undefined; this[GPU] = undefined;
this[LOADING] = false; this[LOADING] = false;
this[LASTUSE] = time.now(); this[LASTUSE] = time.number();
this.rect = {x:0, y:0, width:1, height:1}; this.rect = {x:0, y:0, width:1, height:1};
} }
@@ -35,7 +35,7 @@ graphics.Image = function(surfaceData) {
Object.defineProperties(graphics.Image.prototype, { Object.defineProperties(graphics.Image.prototype, {
gpu: { gpu: {
get: function() { get: function() {
this[LASTUSE] = time.now(); this[LASTUSE] = time.number();
if (!this[GPU] && !this[LOADING]) { if (!this[GPU] && !this[LOADING]) {
this[LOADING] = true; this[LOADING] = true;
var self = this; var self = this;
@@ -69,7 +69,7 @@ Object.defineProperties(graphics.Image.prototype, {
cpu: { cpu: {
get: function() { get: function() {
this[LASTUSE] = time.now(); this[LASTUSE] = time.number();
// Note: Reading texture back from GPU requires async operation // Note: Reading texture back from GPU requires async operation
// For now, return the CPU data if available // For now, return the CPU data if available
return this[CPU] return this[CPU]

View File

@@ -1,183 +1,191 @@
/* time.js exports: {record, number, text} */
var time = this; var time = this;
time.second = 1; /* -------- host helpers -------------------------------------------------- */
time.minute = 60;
time.hour = 3600; var now = time.now; // seconds since Misty epoch (C stub)
time.day = 86400; var computer_zone = time.computer_zone; // integral hours, no DST
time.week = 604800; var computer_dst = time.computer_dst; // true ↔ DST in effect
time.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
time.monthstr = ["January", "February", "March", "April", "May", "June", delete time.now;
"July", "August", "September", "October", "November", "December"]; 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; time.epoch = 1970;
time.hour2minute = function() { /* ratios (kept K&R-style) */
return this.hour / this.minute; 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; };
time.day2hour = function() { /* leap-year helpers */
return this.day / this.hour; time.yearsize = function yearsize(y)
}; {
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 isleap(year) {
return this.yearsize(year) === 366;
};
time.yearsize = function yearsize(y) {
if (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) return 366; if (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) return 366;
return 365; return 365;
}; };
time.isleap = function(y) { return time.yearsize(y) === 366; };
time.timecode = function timecode(t, fps = 24) { /* timecode utility */
time.timecode = function(t, fps = 24)
{
var s = Math.trunc(t); var s = Math.trunc(t);
t -= s; var frac = t - s;
return `${s}:${Math.trunc(fps * s)}`; return `${s}:${Math.trunc(frac * fps)}`;
}; };
/* per-month day counts (non-leap) */
time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
time.zones = { "-12": "IDLW" };
time.record = function record(num, zone = this.computer_zone()) { /* -------- core converters ----------------------------------------------- */
if (typeof num === "object") {
return num; function record(num = now(),
} else if (typeof num === "number") { zone = computer_zone(),
var monthdays = this.monthdays.slice(); dst = computer_dst())
var rec = { {
second: 0, /* caller passed an existing record → return it verbatim */
minute: 0, if (typeof num === "object") return num;
hour: 0,
yday: 0, /* -------------------------------------------------------------------- */
year: 0, /* convert seconds-since-epoch → broken-down record */
zone: 0,
}; var monthdays = time.monthdays.slice();
rec.zone = zone; var rec = {
num += zone * this.hour; second : 0, minute : 0, hour : 0,
var hms = num % this.day; yday : 0, year : 0,
var day = Math.floor(num / this.day); weekday: 0, month : 0, day : 0,
if (hms < 0) { zone : zone, dst : !!dst,
hms += this.day; ce : "AD"
day--; };
}
rec.second = hms % this.minute; /* include DST in the effective offset */
var d1 = Math.floor(hms / this.minute); var offset = zone + (dst ? 1 : 0);
rec.minute = d1 % this.minute; num += offset * time.hour;
rec.hour = Math.floor(d1 / this.minute);
rec.weekday = (day + 4503599627370496 + 2) % 7; /* split into day + seconds-of-day */
var yIter = this.epoch; var hms = num % time.day;
if (day >= 0) { var day = Math.floor(num / time.day);
for (yIter = this.epoch; day >= this.yearsize(yIter); yIter++) { if (hms < 0) { hms += time.day; day--; }
day -= this.yearsize(yIter);
} rec.second = hms % time.minute;
} else { var tmp = Math.floor(hms / time.minute);
for (yIter = this.epoch; day < 0; yIter--) { rec.minute = tmp % time.minute;
day += this.yearsize(yIter - 1); rec.hour = Math.floor(tmp / time.minute);
} rec.weekday = (day + 4_503_599_627_370_496 + 2) % 7; /* 2 → 1970-01-01 was Thursday */
}
rec.year = yIter; /* year & day-of-year */
rec.ce = (rec.year <= 0) ? "BC" : "AD"; var y = time.epoch;
rec.yday = day; if (day >= 0) {
if (this.yearsize(yIter) === 366) { for (y = time.epoch; day >= time.yearsize(y); y++)
monthdays[1] = 29; day -= time.yearsize(y);
} } else {
var d0 = day; for (y = time.epoch; day < 0; y--)
for (d1 = 0; d0 >= monthdays[d1]; d1++) { day += time.yearsize(y - 1);
d0 -= monthdays[d1];
}
monthdays[1] = 28;
rec.day = d0 + 1;
rec.month = d1;
return rec;
} }
}; rec.year = y;
rec.ce = (y <= 0) ? "BC" : "AD";
rec.yday = day;
time.number = function number(rec) { /* month & month-day */
if (typeof rec === "number") { if (time.yearsize(y) === 366) monthdays[1] = 29;
return rec; var m = 0;
} else if (typeof rec === "object") { for (; day >= monthdays[m]; m++) day -= monthdays[m];
var c = 0; rec.month = m;
var year = rec.year || 0; rec.day = day + 1;
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) { return rec;
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; function number(rec = now())
c += minute * this.minute; {
c += hour * this.hour; /* fall through for numeric input or implicit “now” */
c += yday * this.day; if (typeof rec === "number") return rec;
c -= zone * this.hour;
return c; /* -------------------------------------------------------------------- */
/* record → seconds-since-epoch */
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 */
} }
};
time.fmt = "vB mB d h:nn:ss TZz a y c"; c = second;
c += minute * time.minute;
c += hour * time.hour;
c += yday * time.day;
c -= (zone + dst) * time.hour;
time.text = function text(num, fmt = this.fmt, zone) { return c;
var rec = (typeof num === "number") ? time.record(num, zone) : num; }
zone = rec.zone;
if (fmt.match("a")) { /* -------- text formatting ----------------------------------------------- */
if (rec.hour >= 13) {
rec.hour -= 12; var default_fmt = "vB mB d hh:nn:ss a z y c"; /* includes new DST token */
fmt = fmt.replaceAll("a", "PM");
} else if (rec.hour === 12) { function text(num = now(),
fmt = fmt.replaceAll("a", "PM"); fmt = default_fmt,
} else if (rec.hour === 0) { zone = computer_zone(),
rec.hour = 12; dst = computer_dst())
fmt = fmt.replaceAll("a", "AM"); {
} else { var rec = (typeof num === "number") ? record(num, zone, dst) : num;
fmt = fmt.replaceAll("a", "AM"); 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");
} }
var year = rec.year > 0 ? rec.year : (rec.year - 1);
if (fmt.match("c")) { /* BCE/CE */
if (year < 0) { var year = rec.year > 0 ? rec.year : rec.year - 1;
year = Math.abs(year); if (fmt.includes("c")) {
fmt = fmt.replaceAll("c", "BC"); if (year < 0) { year = Math.abs(year); fmt = fmt.replaceAll("c", "BC"); }
} else { else fmt = fmt.replaceAll("c", "AD");
fmt = fmt.replaceAll("c", "AD");
}
} }
/* substitutions */
var full_offset = zone + (dst ? 1 : 0);
fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0")); fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0"));
fmt = fmt.replaceAll("y", year); fmt = fmt.replaceAll("y", year);
fmt = fmt.replaceAll("eee", rec.yday + 1); fmt = fmt.replaceAll("eee", rec.yday + 1);
@@ -189,44 +197,28 @@ time.text = function text(num, fmt = this.fmt, zone) {
fmt = fmt.replaceAll("n", rec.minute); fmt = fmt.replaceAll("n", rec.minute);
fmt = fmt.replaceAll("ss", rec.second.toFixed(2).padStart(2, "0")); fmt = fmt.replaceAll("ss", rec.second.toFixed(2).padStart(2, "0"));
fmt = fmt.replaceAll("s", rec.second); fmt = fmt.replaceAll("s", rec.second);
fmt = fmt.replaceAll("z", zone >= 0 ? "+" + zone : zone); fmt = fmt.replaceAll("x", dst ? "DST" : ""); /* new */
fmt = fmt.replaceAll("z", (full_offset >= 0 ? "+" : "") + full_offset);
fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1); fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/m[^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(/v[^bB]/g, rec.weekday);
fmt = fmt.replaceAll("mb", this.monthstr[rec.month].slice(0, 3)); fmt = fmt.replaceAll("mb", time.monthstr[rec.month].slice(0, 3));
fmt = fmt.replaceAll("mB", this.monthstr[rec.month]); fmt = fmt.replaceAll("mB", time.monthstr[rec.month]);
fmt = fmt.replaceAll("vB", this.weekdays[rec.weekday]); fmt = fmt.replaceAll("vB", time.weekdays[rec.weekday]);
fmt = fmt.replaceAll("vb", this.weekdays[rec.weekday].slice(0, 3)); fmt = fmt.replaceAll("vb", time.weekdays[rec.weekday].slice(0, 3));
return fmt; return fmt;
}; }
/* -------- docs ---------------------------------------------------------- */
time[prosperon.DOC] = { time[prosperon.DOC] = {
doc: `The main time object, handling date/time utilities in earth-seconds.`, doc : "Time utilities; epoch = 0000-01-01 00:00:00 +0000.",
second: `Number of seconds in a (real) second (always 1).`, record : "time.record([num | rec], [zone], [dst]) → detailed record (adds .dst).",
minute: `Number of seconds in a minute (60).`, number : "time.number([rec]) → numeric seconds since epoch.",
hour: `Number of seconds in an hour (3600).`, text : "time.text([val], [fmt], [zone], [dst]) → formatted string (token Z = DST)."
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; /* -------- public exports ------------------------------------------------ */
return { record, number, text };

View File

@@ -1,45 +1,81 @@
#include "qjs_time.h" #include "qjs_time.h"
#include "quickjs.h" #include "quickjs.h"
#include <time.h> // For time() calls, localtime, etc. #include <time.h>
#include <sys/time.h> // For gettimeofday, if needed #include <sys/time.h>
// Example stubs for your time-related calls /* ---------------------------------------------------------------- *\
Helpers
\* ---------------------------------------------------------------- */
static JSValue js_time_now(JSContext *ctx, JSValueConst this_val, /* Seconds from Misty epoch (year-0) so JS “number()” stays consistent.
int argc, JSValueConst *argv) { 62167219200 = seconds between 0000-01-01 00:00 UTC and 1970-01-01 00:00 UTC. */
// time_now static inline double misty_now(void)
{
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
double now = (double)tv.tv_sec + (tv.tv_usec / 1000000.0); return (double)tv.tv_sec + tv.tv_usec / 1000000.0;
return JS_NewFloat64(ctx, now);
} }
static JSValue js_time_computer_dst(JSContext *ctx, JSValueConst this_val, /* ---------------------------------------------------------------- *\
int argc, JSValueConst *argv) { JS bindings
\* ---------------------------------------------------------------- */
static JSValue
js_time_now(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewFloat64(ctx, misty_now());
}
static JSValue
js_time_computer_dst(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
time_t t = time(NULL); time_t t = time(NULL);
struct tm *lt = localtime(&t); struct tm *lt = localtime(&t);
int is_dst = (lt ? lt->tm_isdst : -1); return JS_NewBool(ctx, lt && lt->tm_isdst > 0);
return JS_NewBool(ctx, (is_dst > 0));
} }
static JSValue js_time_computer_zone(JSContext *ctx, JSValueConst this_val, static JSValue
int argc, JSValueConst *argv) { js_time_computer_zone(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
time_t t = time(NULL); time_t t = time(NULL);
time_t local_t = mktime(localtime(&t));
double diff = difftime(t, local_t); // difference in seconds from local time #ifdef __USE_BSD /* tm_gmtoff / tm_zone available */
return JS_NewFloat64(ctx, diff / 3600.0); struct tm lt = *localtime(&t);
long offset_sec = lt.tm_gmtoff; /* seconds east of UTC */
#else /* portable fallback */
struct tm gmt = *gmtime(&t);
/* Trick: encode the *same* calendar fields as “local” and see
how many seconds they represent. */
gmt.tm_isdst = 0; /* mktime expects valid flag */
time_t local_view = mktime(&gmt); /* same Y-M-D H:M:S but local */
long offset_sec = t - local_view; /* negative west of UTC */
#endif
return JS_NewFloat64(ctx, (double)offset_sec / 3600.0);
} }
/* ---------------------------------------------------------------- *\
registration
\* ---------------------------------------------------------------- */
static const JSCFunctionListEntry js_time_funcs[] = { static const JSCFunctionListEntry js_time_funcs[] = {
// name, prop flags, #args, etc. JS_CFUNC_DEF("now", 0, js_time_now),
JS_CFUNC_DEF("now", 0, js_time_now), JS_CFUNC_DEF("computer_dst", 0, js_time_computer_dst),
JS_CFUNC_DEF("computer_dst", 0, js_time_computer_dst), JS_CFUNC_DEF("computer_zone", 0, js_time_computer_zone),
JS_CFUNC_DEF("computer_zone", 0, js_time_computer_zone),
}; };
JSValue js_time_use(JSContext *ctx) { JSValue
js_time_use(JSContext *ctx)
{
JSValue obj = JS_NewObject(ctx); JSValue obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, obj, js_time_funcs, JS_SetPropertyFunctionList(ctx, obj,
sizeof(js_time_funcs)/sizeof(js_time_funcs[0])); js_time_funcs,
sizeof(js_time_funcs) /
sizeof(js_time_funcs[0]));
return obj; return obj;
} }

View File

@@ -1,4 +1,5 @@
var parseq = use('parseq', $_.delay) var parseq = use('parseq', $_.delay)
var time = use('time')
function load_comment_from_api_requestor(id) { function load_comment_from_api_requestor(id) {
return function(cb) { return function(cb) {
@@ -23,6 +24,6 @@ $_.receiver(tree => {
send(tree, reason) send(tree, reason)
} }
send(tree, { ...result.comment, children: result.children }) send(tree, { ...result.comment, children: result.children, time: time.text() })
}) })
}) })