mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
Cache the zone offset at UTC midnights for local-time conversions
Local setters and the field constructor convert a local time value to UTC by bracketing it with the zone offset a day before and after, then checking the candidate instants: three to five localtime(3) calls per conversion. In glibc each call takes the zone lock and, for dates past the zone file's last transition, parses the zone's rule string again. The offsets now come from a per-thread cache of offsets at UTC midnights: a day whose two midnights agree has that offset throughout, given at most one transition per day, which the conversion already assumes. A day with a transition still asks localtime for the instant. A change of process.env.TZ clears the cache.
This commit is contained in:
@@ -11588,8 +11588,8 @@ double scr_date_get_timezone_offset(double ms) {
|
||||
* from local fields reads those fields back. */
|
||||
|
||||
/* The host zone's offset (local - UTC) in ms at UTC instant `ms` (an
|
||||
* integral time value within about ±8.65e15). */
|
||||
static bool scr_date_offset_at(double ms, double *out) {
|
||||
* integral time value within about ±8.65e15), from the host's localtime. */
|
||||
static bool scr_date_offset_at_uncached(double ms, double *out) {
|
||||
long long cms = (long long)ms;
|
||||
double secd = (double)(cms / 1000 - (cms % 1000 < 0));
|
||||
struct tm tmv;
|
||||
@@ -11611,6 +11611,60 @@ static bool scr_date_offset_at(double ms, double *out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Offsets at UTC midnights, for scr_date_offset_at. A day whose two
|
||||
* midnights have the same offset has that offset throughout, given at most
|
||||
* one transition per day (scr_date_local_to_utc already takes transitions
|
||||
* to be at least a day apart). Each local setter asks for three to five
|
||||
* offsets around one time value, and consecutive ones land on the same or
|
||||
* neighbouring days, so most answers come from here instead of a
|
||||
* localtime(3) call: glibc takes its zone lock in each and, for dates past
|
||||
* the zone file's last transition, parses the zone's rule string again. A
|
||||
* change of process.env.TZ starts a new generation. */
|
||||
#define SCR_DATE_MIDNIGHTS 64
|
||||
typedef struct {
|
||||
long long day;
|
||||
double offset;
|
||||
bool valid;
|
||||
} ScrDateMidnight;
|
||||
typedef struct {
|
||||
bool valid;
|
||||
unsigned generation;
|
||||
ScrDateMidnight entries[SCR_DATE_MIDNIGHTS];
|
||||
} ScrDateMidnightCache;
|
||||
static SCR_TL ScrDateMidnightCache scr_date_midnight_cache;
|
||||
|
||||
static bool scr_date_midnight_offset(ScrDateMidnightCache *c, long long day, double *out) {
|
||||
ScrDateMidnight *e = &c->entries[(unsigned long long)day % SCR_DATE_MIDNIGHTS];
|
||||
if (e->valid && e->day == day) {
|
||||
*out = e->offset;
|
||||
return true;
|
||||
}
|
||||
if (!scr_date_offset_at_uncached((double)day * 86400000.0, out)) return false;
|
||||
e->valid = true;
|
||||
e->day = day;
|
||||
e->offset = *out;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool scr_date_offset_at(double ms, double *out) {
|
||||
ScrDateMidnightCache *c = &scr_date_midnight_cache;
|
||||
unsigned generation = scr_date_tz_generation;
|
||||
if (!c->valid || c->generation != generation) {
|
||||
memset(c, 0, sizeof *c);
|
||||
c->valid = true;
|
||||
c->generation = generation;
|
||||
}
|
||||
long long cms = (long long)ms;
|
||||
long long day = cms / 86400000 - (cms % 86400000 < 0);
|
||||
double start, end;
|
||||
if (scr_date_midnight_offset(c, day, &start) && scr_date_midnight_offset(c, day + 1, &end) &&
|
||||
start == end) {
|
||||
*out = start;
|
||||
return true;
|
||||
}
|
||||
return scr_date_offset_at_uncached(ms, out);
|
||||
}
|
||||
|
||||
static volatile const double scr_date_day_ms = 86400000.0;
|
||||
|
||||
static double scr_date_local_to_utc(double t) {
|
||||
|
||||
Reference in New Issue
Block a user