@scriptc/runtime 0.0.21 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_assert.c +23 -0
- package/src/scr_async.c +5 -2
- package/src/scr_bytes.c +7 -0
- package/src/scr_cycle.c +403 -62
- package/src/scr_dyn_invoke.c +58 -6
- package/src/scr_fetch.c +6200 -124
- package/src/scr_fetch_curl.c +21 -9
- package/src/scr_http.c +13 -0
- package/src/scr_inspect.c +6 -0
- package/src/scr_island.c +19 -0
- package/src/scr_json.c +413 -9
- package/src/scr_lib.c +277 -18
- package/src/scr_runtime.h +211 -27
- package/src/scr_tls.c +15 -11
- package/src/scr_tls_ca.c +35 -8
- package/src/scr_url.c +103 -15
- package/src/scr_web.c +77 -1
- package/vendor/README.md +4 -4
package/src/scr_lib.c
CHANGED
|
@@ -1646,12 +1646,77 @@ double scr_fs_open(ScrStr *path, ScrStr *flags) {
|
|
|
1646
1646
|
|
|
1647
1647
|
/* fs.closeSync(fd) — close(2); failure throws Node's path-less fs error
|
|
1648
1648
|
* shape ("EBADF: bad file descriptor, close"). */
|
|
1649
|
-
|
|
1649
|
+
|
|
1650
|
+
/* Offset-preserving read for fs.readSync's numeric-position form. POSIX
|
|
1651
|
+
* supplies pread(2). Windows follows libuv's synchronous-handle recipe:
|
|
1652
|
+
* ReadFile with an OVERLAPPED byte offset, then restore the handle position
|
|
1653
|
+
* because synchronous ReadFile updates it even when OVERLAPPED is present. */
|
|
1654
|
+
static ssize_t scr_fs_pread(int fd, void *data, size_t length, double position) {
|
|
1655
|
+
#ifdef _WIN32
|
|
1656
|
+
HANDLE handle = (HANDLE)_get_osfhandle(fd);
|
|
1657
|
+
if (handle == INVALID_HANDLE_VALUE) {
|
|
1658
|
+
errno = EBADF;
|
|
1659
|
+
return -1;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
OVERLAPPED overlapped;
|
|
1663
|
+
memset(&overlapped, 0, sizeof overlapped);
|
|
1664
|
+
LARGE_INTEGER at;
|
|
1665
|
+
at.QuadPart = (LONGLONG)position;
|
|
1666
|
+
overlapped.Offset = at.LowPart;
|
|
1667
|
+
overlapped.OffsetHigh = at.HighPart;
|
|
1668
|
+
|
|
1669
|
+
LARGE_INTEGER zero;
|
|
1670
|
+
LARGE_INTEGER original;
|
|
1671
|
+
zero.QuadPart = 0;
|
|
1672
|
+
BOOL restore = SetFilePointerEx(handle, zero, &original, FILE_CURRENT);
|
|
1673
|
+
DWORD got = 0;
|
|
1674
|
+
DWORD want = length > (size_t)UINT32_MAX ? UINT32_MAX : (DWORD)length;
|
|
1675
|
+
BOOL ok = ReadFile(handle, data, want, &got, &overlapped);
|
|
1676
|
+
DWORD error = ok ? ERROR_SUCCESS : GetLastError();
|
|
1677
|
+
if (restore) (void)SetFilePointerEx(handle, original, NULL, FILE_BEGIN);
|
|
1678
|
+
/* ReadFile may report a terminal status after still filling part of the
|
|
1679
|
+
* caller's buffer (notably ERROR_MORE_DATA for a message-mode pipe).
|
|
1680
|
+
* libuv/Node return those bytes and surface the status on the next read. */
|
|
1681
|
+
if (ok || got > 0 || error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
|
|
1682
|
+
return (ssize_t)got;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/* The read-specific subset of libuv's Win32-to-errno translation. */
|
|
1686
|
+
switch (error) {
|
|
1687
|
+
case ERROR_INVALID_HANDLE:
|
|
1688
|
+
case ERROR_ACCESS_DENIED:
|
|
1689
|
+
errno = EBADF;
|
|
1690
|
+
break;
|
|
1691
|
+
case ERROR_INVALID_FUNCTION:
|
|
1692
|
+
case ERROR_INVALID_PARAMETER:
|
|
1693
|
+
errno = EINVAL;
|
|
1694
|
+
break;
|
|
1695
|
+
case ERROR_NOT_ENOUGH_MEMORY:
|
|
1696
|
+
case ERROR_OUTOFMEMORY:
|
|
1697
|
+
errno = ENOMEM;
|
|
1698
|
+
break;
|
|
1699
|
+
case ERROR_OPERATION_ABORTED:
|
|
1700
|
+
errno = EINTR;
|
|
1701
|
+
break;
|
|
1702
|
+
default:
|
|
1703
|
+
errno = EIO;
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1706
|
+
return -1;
|
|
1707
|
+
#else
|
|
1708
|
+
return pread(fd, data, length, (off_t)position);
|
|
1709
|
+
#endif
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
/* fs.readSync(fd, buffer, offset, length[, position]) — the buffer form.
|
|
1650
1713
|
* Node validates offset/length against the buffer before reading and
|
|
1651
1714
|
* throws ERR_OUT_OF_RANGE; here the checks clamp to the same contract and
|
|
1652
|
-
* throw the RangeError shape.
|
|
1653
|
-
*
|
|
1654
|
-
|
|
1715
|
+
* throw the RangeError shape. Position -1 means the fd's current offset;
|
|
1716
|
+
* nonnegative positions do not advance it. Returns the byte count the OS
|
|
1717
|
+
* reports; errors carry the errno name like the other fd operations. */
|
|
1718
|
+
double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length,
|
|
1719
|
+
double position) {
|
|
1655
1720
|
size_t bytelen = buf->len; /* u8 buffers: elem count == byte count */
|
|
1656
1721
|
char msg[160];
|
|
1657
1722
|
int mlen;
|
|
@@ -1659,7 +1724,28 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1659
1724
|
* form, length against the remaining window (validateOffset vs the
|
|
1660
1725
|
* buffer-bounds check in fs.readSync). */
|
|
1661
1726
|
char numbuf[40];
|
|
1662
|
-
if (offset
|
|
1727
|
+
if (!(isfinite(offset) && trunc(offset) == offset)) {
|
|
1728
|
+
char recv[48];
|
|
1729
|
+
scr_num_received(offset, recv);
|
|
1730
|
+
mlen = snprintf(msg, sizeof msg,
|
|
1731
|
+
"The value of \"offset\" is out of range. It must be an integer. Received %s",
|
|
1732
|
+
recv);
|
|
1733
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1734
|
+
return 0;
|
|
1735
|
+
}
|
|
1736
|
+
if (offset < 0 || offset > 9007199254740991.0) {
|
|
1737
|
+
numbuf[scr_f64_to_str(offset, numbuf)] = 0;
|
|
1738
|
+
mlen = snprintf(msg, sizeof msg,
|
|
1739
|
+
"The value of \"offset\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
|
|
1740
|
+
numbuf);
|
|
1741
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1742
|
+
return 0;
|
|
1743
|
+
}
|
|
1744
|
+
/* Node returns after offset's intrinsic validation when the requested
|
|
1745
|
+
* length normalizes to zero: buffer-window bounds, position, and fd are
|
|
1746
|
+
* not consulted. Preserve the existing size_t coercion's [0, 1) case. */
|
|
1747
|
+
if (length >= 0 && length < 1) return 0;
|
|
1748
|
+
if (offset > (double)bytelen) {
|
|
1663
1749
|
numbuf[scr_f64_to_str(offset, numbuf)] = 0;
|
|
1664
1750
|
mlen = snprintf(msg, sizeof msg,
|
|
1665
1751
|
"The value of \"offset\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
|
|
@@ -1677,7 +1763,27 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1677
1763
|
return 0;
|
|
1678
1764
|
}
|
|
1679
1765
|
size_t want = (size_t)length;
|
|
1680
|
-
|
|
1766
|
+
if (!(isfinite(position) && trunc(position) == position)) {
|
|
1767
|
+
char recv[48];
|
|
1768
|
+
scr_num_received(position, recv);
|
|
1769
|
+
mlen = snprintf(msg, sizeof msg,
|
|
1770
|
+
"The value of \"position\" is out of range. It must be an integer. Received %s",
|
|
1771
|
+
recv);
|
|
1772
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1773
|
+
return 0;
|
|
1774
|
+
}
|
|
1775
|
+
if (position < -1 || position > 9007199254740991.0) {
|
|
1776
|
+
char recv[48];
|
|
1777
|
+
scr_num_received(position, recv);
|
|
1778
|
+
mlen = snprintf(msg, sizeof msg,
|
|
1779
|
+
"The value of \"position\" is out of range. It must be >= -1 && <= 9007199254740991. Received %s",
|
|
1780
|
+
recv);
|
|
1781
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1782
|
+
return 0;
|
|
1783
|
+
}
|
|
1784
|
+
ssize_t n = position == -1
|
|
1785
|
+
? read((int)fd, buf->data + off, want)
|
|
1786
|
+
: scr_fs_pread((int)fd, buf->data + off, want, position);
|
|
1681
1787
|
if (n < 0) {
|
|
1682
1788
|
int e = errno;
|
|
1683
1789
|
char namebuf[16];
|
|
@@ -3214,9 +3320,10 @@ double scr_str_last_index_of(ScrStr *s, ScrStr *needle) {
|
|
|
3214
3320
|
return -1.0;
|
|
3215
3321
|
}
|
|
3216
3322
|
|
|
3217
|
-
/* ── Date, the
|
|
3218
|
-
*
|
|
3219
|
-
*
|
|
3323
|
+
/* ── Date, the read-only value slice ───────────────────────────────────
|
|
3324
|
+
* Values are TimeClip'd epoch-millisecond scalars. Identity/mutation are
|
|
3325
|
+
* frontend-fenced; construction, storage, getters, and ISO formatting
|
|
3326
|
+
* observe exactly this payload. */
|
|
3220
3327
|
|
|
3221
3328
|
double scr_date_now(void) {
|
|
3222
3329
|
struct timespec ts;
|
|
@@ -3225,6 +3332,16 @@ double scr_date_now(void) {
|
|
|
3225
3332
|
return floor((double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6);
|
|
3226
3333
|
}
|
|
3227
3334
|
|
|
3335
|
+
/* Date's TimeClip: non-finite/out-of-range values become Invalid Date,
|
|
3336
|
+
* finite values truncate toward zero, and -0 normalizes to +0. */
|
|
3337
|
+
double scr_date_new_ms(double ms) {
|
|
3338
|
+
if (!isfinite(ms) || fabs(ms) > 8640000000000000.0) return NAN;
|
|
3339
|
+
double clipped = trunc(ms);
|
|
3340
|
+
return clipped == 0 ? 0 : clipped;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
double scr_date_get_time(double ms) { return ms; }
|
|
3344
|
+
|
|
3228
3345
|
/* Node's Date.prototype.toISOString over a millisecond time value:
|
|
3229
3346
|
* TimeClip's ToInteger truncation, proleptic Gregorian civil-from-days
|
|
3230
3347
|
* (Howard Hinnant's algorithm), YYYY-MM-DDTHH:mm:ss.sssZ with expanded
|
|
@@ -3291,7 +3408,7 @@ static double scr_days_from_civil(long long y, int m, int d) {
|
|
|
3291
3408
|
return (double)(era * 146097 + (long long)doe - 719468);
|
|
3292
3409
|
}
|
|
3293
3410
|
|
|
3294
|
-
static double
|
|
3411
|
+
static double scr_date_make_ms(long long y, int mo, int d, int hh, int mi, int ss, int ms) {
|
|
3295
3412
|
/* V8 accepts days 1..31 in every month and ROLLS OVER past the month's
|
|
3296
3413
|
* end (Feb 30 → Mar 2) — days_from_civil extrapolates linearly, so the
|
|
3297
3414
|
* rollover falls out; day 0 and 32+ are NaN, like V8. */
|
|
@@ -3299,10 +3416,13 @@ static double scr_date_ms_of(long long y, int mo, int d, int hh, int mi, int ss,
|
|
|
3299
3416
|
if (hh > 24 || mi > 59 || ss > 59 || (hh == 24 && (mi || ss || ms))) return NAN;
|
|
3300
3417
|
double t = scr_days_from_civil(y, mo, d) * 86400000.0 +
|
|
3301
3418
|
hh * 3600000.0 + mi * 60000.0 + ss * 1000.0 + ms;
|
|
3302
|
-
if (fabs(t) > 8640000000000000.0) return NAN;
|
|
3303
3419
|
return t;
|
|
3304
3420
|
}
|
|
3305
3421
|
|
|
3422
|
+
static double scr_date_ms_of(long long y, int mo, int d, int hh, int mi, int ss, int ms) {
|
|
3423
|
+
return scr_date_new_ms(scr_date_make_ms(y, mo, d, hh, mi, ss, ms));
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3306
3426
|
static bool scr_date_digits(const char **p, const char *end, int n, int *out) {
|
|
3307
3427
|
int v = 0;
|
|
3308
3428
|
if (end - *p < n) return false;
|
|
@@ -3358,6 +3478,7 @@ double scr_date_parse_get_time(ScrStr *s) {
|
|
|
3358
3478
|
bool neg = *p == '-';
|
|
3359
3479
|
p++;
|
|
3360
3480
|
if (!scr_date_digits(&p, end, 6, &y6)) return NAN;
|
|
3481
|
+
if (neg && y6 == 0) return NAN; /* ECMA forbids expanded -000000 */
|
|
3361
3482
|
yy = neg ? -(long long)y6 : y6;
|
|
3362
3483
|
} else {
|
|
3363
3484
|
if (!scr_date_digits(&p, end, 4, &y)) return NAN;
|
|
@@ -3393,14 +3514,18 @@ double scr_date_parse_get_time(ScrStr *s) {
|
|
|
3393
3514
|
p++;
|
|
3394
3515
|
if (!scr_date_digits(&p, end, 2, &oh) || p >= end || *p++ != ':') return NAN;
|
|
3395
3516
|
if (!scr_date_digits(&p, end, 2, &om)) return NAN;
|
|
3517
|
+
if (oh > 23 || om > 59) return NAN;
|
|
3396
3518
|
off = (oh * 60 + om) * 60000.0;
|
|
3397
3519
|
if (neg) off = -off;
|
|
3398
3520
|
} else {
|
|
3399
3521
|
return NAN;
|
|
3400
3522
|
}
|
|
3401
3523
|
if (p != end) return NAN;
|
|
3402
|
-
|
|
3403
|
-
|
|
3524
|
+
/* MakeDate can lie just beyond the TimeClip boundary while the
|
|
3525
|
+
* explicit offset brings the final UTC instant back into range. The
|
|
3526
|
+
* spec clips only after that offset has been applied. */
|
|
3527
|
+
double t = scr_date_make_ms(yy, mo, d, hh, mi, ss, ms);
|
|
3528
|
+
return scr_date_new_ms(t - off);
|
|
3404
3529
|
}
|
|
3405
3530
|
}
|
|
3406
3531
|
|
|
@@ -3412,10 +3537,10 @@ double scr_date_parse_get_time(ScrStr *s) {
|
|
|
3412
3537
|
* 1900+year (the spec's MakeFullYear), out-of-range months ROLL into the
|
|
3413
3538
|
* year (Date.UTC(2017, 13) is Feb 2018) and any integer date offsets from
|
|
3414
3539
|
* day 1 of that month — days_from_civil extrapolates linearly, so both
|
|
3415
|
-
* rollovers fall out. V8 bounds MakeDay's year to ±1e6
|
|
3416
|
-
*
|
|
3417
|
-
*
|
|
3418
|
-
* Never throws. */
|
|
3540
|
+
* rollovers fall out. V8 bounds MakeDay's input year to ±1e6 and input
|
|
3541
|
+
* month to ±1e7 before normalizing the month (kMaxYear/kMinYear and
|
|
3542
|
+
* kMaxMonth/kMinMonth, date.h); Node answers NaN past either bound even
|
|
3543
|
+
* when the two inputs would normalize back into range. Never throws. */
|
|
3419
3544
|
double scr_date_utc(double y, double mo, double d,
|
|
3420
3545
|
double h, double mi, double s, double ms) {
|
|
3421
3546
|
if (!isfinite(y) || !isfinite(mo) || !isfinite(d) || !isfinite(h) ||
|
|
@@ -3429,16 +3554,150 @@ double scr_date_utc(double y, double mo, double d,
|
|
|
3429
3554
|
mi = trunc(mi);
|
|
3430
3555
|
s = trunc(s);
|
|
3431
3556
|
ms = trunc(ms);
|
|
3557
|
+
if (fabs(y) > 1000000.0 || fabs(mo) > 10000000.0) return NAN;
|
|
3432
3558
|
if (y >= 0 && y <= 99) y += 1900;
|
|
3433
3559
|
double ym = y + floor(mo / 12.0);
|
|
3434
3560
|
int mn = (int)(mo - floor(mo / 12.0) * 12.0); /* 0..11 */
|
|
3435
|
-
if (fabs(ym) > 1000000.0) return NAN; /* V8's MakeDay year bound */
|
|
3436
3561
|
double days = scr_days_from_civil((long long)ym, mn + 1, 1) + (d - 1.0);
|
|
3437
3562
|
double t = days * 86400000.0 + h * 3600000.0 + mi * 60000.0 + s * 1000.0 + ms;
|
|
3438
3563
|
if (fabs(t) > 8640000000000000.0) return NAN; /* TimeClip */
|
|
3439
3564
|
return t == 0 ? 0 : t; /* normalize -0 (TimeClip's +0) */
|
|
3440
3565
|
}
|
|
3441
3566
|
|
|
3567
|
+
/* ── Date calendar getters ────────────────────────────────────────────
|
|
3568
|
+
* UTC fields use the same proleptic-Gregorian walk as toISOString, so the
|
|
3569
|
+
* whole Date range is portable. Local fields use the host timezone via
|
|
3570
|
+
* localtime, exactly the environment the sibling Node process observes;
|
|
3571
|
+
* a libc that cannot represent an extreme instant answers NaN rather than
|
|
3572
|
+
* inventing a zone. */
|
|
3573
|
+
|
|
3574
|
+
typedef struct ScrDateParts {
|
|
3575
|
+
long long year;
|
|
3576
|
+
int month, date, day, hours, minutes, seconds, milliseconds;
|
|
3577
|
+
double timezone_offset;
|
|
3578
|
+
} ScrDateParts;
|
|
3579
|
+
|
|
3580
|
+
/* Calendar decomposition itself also serves LocalTime(t), which can lie
|
|
3581
|
+
* just outside TimeClip's UTC interval after applying a zone offset at an
|
|
3582
|
+
* endpoint. The checked wrapper is the public UTC-getter gate; the inner
|
|
3583
|
+
* walk accepts those bounded local-time values too. */
|
|
3584
|
+
static void scr_date_utc_parts_unchecked(double t, ScrDateParts *out) {
|
|
3585
|
+
double dayd = floor(t / 86400000.0);
|
|
3586
|
+
long long msday = (long long)(t - dayd * 86400000.0);
|
|
3587
|
+
long long z = (long long)dayd + 719468;
|
|
3588
|
+
long long era = (z >= 0 ? z : z - 146096) / 146097;
|
|
3589
|
+
unsigned long long doe = (unsigned long long)(z - era * 146097);
|
|
3590
|
+
unsigned long long yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
3591
|
+
long long y = (long long)yoe + era * 400;
|
|
3592
|
+
unsigned long long doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
3593
|
+
unsigned long long mp = (5 * doy + 2) / 153;
|
|
3594
|
+
unsigned long long d = doy - (153 * mp + 2) / 5 + 1;
|
|
3595
|
+
unsigned long long m = mp < 10 ? mp + 3 : mp - 9;
|
|
3596
|
+
if (m <= 2) y++;
|
|
3597
|
+
long long wday = ((long long)dayd + 4) % 7;
|
|
3598
|
+
if (wday < 0) wday += 7;
|
|
3599
|
+
out->year = y;
|
|
3600
|
+
out->month = (int)m - 1;
|
|
3601
|
+
out->date = (int)d;
|
|
3602
|
+
out->day = (int)wday;
|
|
3603
|
+
out->hours = (int)(msday / 3600000);
|
|
3604
|
+
out->minutes = (int)(msday / 60000 % 60);
|
|
3605
|
+
out->seconds = (int)(msday / 1000 % 60);
|
|
3606
|
+
out->milliseconds = (int)(msday % 1000);
|
|
3607
|
+
out->timezone_offset = 0;
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
static bool scr_date_utc_parts(double ms, ScrDateParts *out) {
|
|
3611
|
+
if (!isfinite(ms) || fabs(ms) > 8640000000000000.0) return false;
|
|
3612
|
+
scr_date_utc_parts_unchecked(trunc(ms), out);
|
|
3613
|
+
return true;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
static bool scr_date_localtime(double secd, struct tm *out) {
|
|
3617
|
+
time_t sec = (time_t)secd;
|
|
3618
|
+
if ((double)sec != secd) return false;
|
|
3619
|
+
#ifdef _WIN32
|
|
3620
|
+
return localtime_s(out, &sec) == 0;
|
|
3621
|
+
#else
|
|
3622
|
+
return localtime_r(&sec, out) != NULL;
|
|
3623
|
+
#endif
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
static bool scr_date_local_parts(double ms, ScrDateParts *out) {
|
|
3627
|
+
if (!isfinite(ms) || fabs(ms) > 8640000000000000.0) return false;
|
|
3628
|
+
double clipped = trunc(ms);
|
|
3629
|
+
double secd = floor(clipped / 1000.0);
|
|
3630
|
+
struct tm tmv;
|
|
3631
|
+
double basis_secd = secd;
|
|
3632
|
+
if (!scr_date_localtime(basis_secd, &tmv)) {
|
|
3633
|
+
/* Windows' _localtime64_s rejects pre-epoch instants and years after
|
|
3634
|
+
* 3001 even though both are valid ECMAScript Dates. Query the host's
|
|
3635
|
+
* zone rule at a calendar-equivalent surrogate year in 2000..2399:
|
|
3636
|
+
* Gregorian weekdays/leap years repeat every 400 years. This keeps
|
|
3637
|
+
* every valid Date finite; the OS-vs-Node historical-rule difference
|
|
3638
|
+
* remains the documented timezone-data divergence. */
|
|
3639
|
+
ScrDateParts utc;
|
|
3640
|
+
scr_date_utc_parts_unchecked(clipped, &utc);
|
|
3641
|
+
long long cycle_year = (utc.year - 2000) % 400;
|
|
3642
|
+
if (cycle_year < 0) cycle_year += 400;
|
|
3643
|
+
long long surrogate_year = 2000 + cycle_year;
|
|
3644
|
+
basis_secd =
|
|
3645
|
+
scr_days_from_civil(surrogate_year, utc.month + 1, utc.date) * 86400.0 +
|
|
3646
|
+
utc.hours * 3600.0 + utc.minutes * 60.0 + utc.seconds;
|
|
3647
|
+
if (!scr_date_localtime(basis_secd, &tmv)) return false;
|
|
3648
|
+
}
|
|
3649
|
+
/* Treat the local broken-down fields as UTC. Its distance from the real
|
|
3650
|
+
* (or surrogate) epoch second is the zone offset. Apply that offset to
|
|
3651
|
+
* the original instant and use the portable Gregorian walk for its
|
|
3652
|
+
* fields; Date#getTimezoneOffset uses the
|
|
3653
|
+
* opposite sign (UTC - local), in whole minutes. Historical local-mean
|
|
3654
|
+
* offsets can contain seconds, which JavaScript truncates toward zero. */
|
|
3655
|
+
double local_as_utc =
|
|
3656
|
+
scr_days_from_civil((long long)tmv.tm_year + 1900, tmv.tm_mon + 1, tmv.tm_mday) * 86400.0 +
|
|
3657
|
+
tmv.tm_hour * 3600.0 + tmv.tm_min * 60.0 + tmv.tm_sec;
|
|
3658
|
+
double local_offset = local_as_utc - basis_secd;
|
|
3659
|
+
scr_date_utc_parts_unchecked(clipped + local_offset * 1000.0, out);
|
|
3660
|
+
double timezone_offset = trunc(-local_offset / 60.0);
|
|
3661
|
+
out->timezone_offset = timezone_offset == 0 ? 0 : timezone_offset;
|
|
3662
|
+
return true;
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
static bool scr_date_parts(double ms, bool utc, ScrDateParts *out) {
|
|
3666
|
+
return utc ? scr_date_utc_parts(ms, out) : scr_date_local_parts(ms, out);
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
#define SCR_DATE_PART_GETTER(name, field) \
|
|
3670
|
+
double scr_date_get_##name(double ms, bool utc) { \
|
|
3671
|
+
ScrDateParts p; \
|
|
3672
|
+
return scr_date_parts(ms, utc, &p) ? (double)p.field : NAN; \
|
|
3673
|
+
} \
|
|
3674
|
+
double scr_date_get_##name##_local(double ms) { \
|
|
3675
|
+
return scr_date_get_##name(ms, false); \
|
|
3676
|
+
} \
|
|
3677
|
+
double scr_date_get_##name##_utc(double ms) { \
|
|
3678
|
+
return scr_date_get_##name(ms, true); \
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
SCR_DATE_PART_GETTER(full_year, year)
|
|
3682
|
+
SCR_DATE_PART_GETTER(month, month)
|
|
3683
|
+
SCR_DATE_PART_GETTER(date, date)
|
|
3684
|
+
SCR_DATE_PART_GETTER(day, day)
|
|
3685
|
+
SCR_DATE_PART_GETTER(hours, hours)
|
|
3686
|
+
SCR_DATE_PART_GETTER(minutes, minutes)
|
|
3687
|
+
SCR_DATE_PART_GETTER(seconds, seconds)
|
|
3688
|
+
|
|
3689
|
+
#undef SCR_DATE_PART_GETTER
|
|
3690
|
+
|
|
3691
|
+
double scr_date_get_milliseconds(double ms) {
|
|
3692
|
+
ScrDateParts p;
|
|
3693
|
+
return scr_date_utc_parts(ms, &p) ? (double)p.milliseconds : NAN;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
double scr_date_get_timezone_offset(double ms) {
|
|
3697
|
+
ScrDateParts p;
|
|
3698
|
+
return scr_date_local_parts(ms, &p) ? p.timezone_offset : NAN;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3442
3701
|
/* ── Number statics ────────────────────────────────────────────────────
|
|
3443
3702
|
* JS-exact: the ES2015 Number statics never coerce (unlike the global
|
|
3444
3703
|
* isNaN/isFinite), and the compiler routes only number-typed arguments
|