feat: add wasi target
This commit is contained in:
parent
7a30f17d43
commit
7abcd0b2ed
8 changed files with 1575 additions and 854 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -8,3 +8,5 @@ lib
|
|||
!lib/*/temporal_capi.lib
|
||||
|
||||
tmp
|
||||
|
||||
test/test262/polyfill-injected.js
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "test/temporal-test262-runner"]
|
||||
path = test/temporal-test262-runner
|
||||
url = https://github.com/js-temporal/temporal-test262-runner.git
|
||||
37
build.zig
37
build.zig
|
|
@ -4,7 +4,7 @@ const build_crab = @import("build_crab");
|
|||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const is_wasm_freestanding = target.result.cpu.arch.isWasm();
|
||||
const is_wasm_freestanding = target.result.cpu.arch.isWasm() and target.result.os.tag == .freestanding;
|
||||
|
||||
// --- Zig Module: temporalz --- //
|
||||
const mod = b.addModule("temporalz", .{
|
||||
|
|
@ -108,20 +108,31 @@ pub fn build(b: *std.Build) !void {
|
|||
},
|
||||
}),
|
||||
});
|
||||
exe.root_module.link_libc = !is_wasm_freestanding;
|
||||
exe.rdynamic = is_wasm_freestanding;
|
||||
b.installArtifact(exe);
|
||||
|
||||
// --- Steps: Run --- //
|
||||
{
|
||||
const run_step = b.step("run", "Run the app");
|
||||
if (is_wasm_freestanding) {
|
||||
switch (target.result.os.tag) {
|
||||
.freestanding => {
|
||||
const run_cmd = b.addSystemCommand(&.{ "node", "example/src/main.mjs" });
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
} else {
|
||||
},
|
||||
.wasi => {
|
||||
const run_cmd = b.addSystemCommand(&.{"wasmtime"});
|
||||
run_cmd.addFileArg(exe.getEmittedBin());
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
},
|
||||
else => {
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,22 +170,10 @@ pub fn build(b: *std.Build) !void {
|
|||
// --- Steps: test-262 --- //
|
||||
{
|
||||
const test262_step = b.step("test262", "Run test-262 tests");
|
||||
const test262_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("test/test262/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "temporalz", .module = mod },
|
||||
},
|
||||
}),
|
||||
.test_runner = .{
|
||||
.path = b.path("test/runner.zig"),
|
||||
.mode = .simple,
|
||||
},
|
||||
});
|
||||
test262_tests.root_module.link_libc = !is_wasm_freestanding;
|
||||
test262_step.dependOn(&b.addRunArtifact(test262_tests).step);
|
||||
const run_cmd = b.addSystemCommand(&.{ "node", "test/test262/runner.mjs" });
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
test262_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// --- Steps: Build all platforms --- //
|
||||
|
|
|
|||
|
|
@ -1,11 +1,837 @@
|
|||
const std = @import("std");
|
||||
const program = @import("root.zig");
|
||||
const Temporal = @import("temporalz");
|
||||
|
||||
export fn _start() void {
|
||||
const allocator = std.heap.wasm_allocator;
|
||||
program.run(allocator, null) catch {};
|
||||
}
|
||||
|
||||
const wasm_allocator = std.heap.wasm_allocator;
|
||||
const PolyfillError = error{InvalidHandle};
|
||||
|
||||
var instants_init = false;
|
||||
var durations_init = false;
|
||||
var instants: std.ArrayList(?Temporal.Instant) = .empty;
|
||||
var durations: std.ArrayList(?Temporal.Duration) = .empty;
|
||||
var plain_dates_init = false;
|
||||
var plain_dates: std.ArrayList(?Temporal.PlainDate) = .empty;
|
||||
|
||||
var last_error: ?[]u8 = null;
|
||||
|
||||
fn ensureInstants() void {
|
||||
if (!instants_init) {
|
||||
instants = .empty;
|
||||
instants_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn ensureDurations() void {
|
||||
if (!durations_init) {
|
||||
durations = .empty;
|
||||
durations_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn ensurePlainDates() void {
|
||||
if (!plain_dates_init) {
|
||||
plain_dates = .empty;
|
||||
plain_dates_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn addInstant(inst: Temporal.Instant) u32 {
|
||||
ensureInstants();
|
||||
instants.append(wasm_allocator, inst) catch return 0;
|
||||
return @intCast(instants.items.len);
|
||||
}
|
||||
|
||||
fn getInstant(handle: u32) !Temporal.Instant {
|
||||
ensureInstants();
|
||||
if (handle == 0 or handle > instants.items.len) return PolyfillError.InvalidHandle;
|
||||
return instants.items[handle - 1] orelse PolyfillError.InvalidHandle;
|
||||
}
|
||||
|
||||
fn removeInstant(handle: u32) void {
|
||||
if (!instants_init or handle == 0 or handle > instants.items.len) return;
|
||||
if (instants.items[handle - 1]) |inst| {
|
||||
inst.deinit();
|
||||
instants.items[handle - 1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn addDuration(dur: Temporal.Duration) u32 {
|
||||
ensureDurations();
|
||||
durations.append(wasm_allocator, dur) catch return 0;
|
||||
return @intCast(durations.items.len);
|
||||
}
|
||||
|
||||
fn getDuration(handle: u32) !Temporal.Duration {
|
||||
ensureDurations();
|
||||
if (handle == 0 or handle > durations.items.len) return PolyfillError.InvalidHandle;
|
||||
return durations.items[handle - 1] orelse PolyfillError.InvalidHandle;
|
||||
}
|
||||
|
||||
fn removeDuration(handle: u32) void {
|
||||
if (!durations_init or handle == 0 or handle > durations.items.len) return;
|
||||
if (durations.items[handle - 1]) |dur| {
|
||||
dur.deinit();
|
||||
durations.items[handle - 1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn addPlainDate(date: Temporal.PlainDate) u32 {
|
||||
ensurePlainDates();
|
||||
plain_dates.append(wasm_allocator, date) catch return 0;
|
||||
return @intCast(plain_dates.items.len);
|
||||
}
|
||||
|
||||
fn getPlainDate(handle: u32) !Temporal.PlainDate {
|
||||
ensurePlainDates();
|
||||
if (handle == 0 or handle > plain_dates.items.len) return PolyfillError.InvalidHandle;
|
||||
return plain_dates.items[handle - 1] orelse PolyfillError.InvalidHandle;
|
||||
}
|
||||
|
||||
fn removePlainDate(handle: u32) void {
|
||||
if (!plain_dates_init or handle == 0 or handle > plain_dates.items.len) return;
|
||||
if (plain_dates.items[handle - 1]) |date| {
|
||||
date.deinit();
|
||||
plain_dates.items[handle - 1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn clearLastError() void {
|
||||
if (last_error) |msg| {
|
||||
wasm_allocator.free(msg);
|
||||
last_error = null;
|
||||
}
|
||||
}
|
||||
|
||||
fn setLastError(err: anyerror) void {
|
||||
clearLastError();
|
||||
const msg = std.fmt.allocPrint(wasm_allocator, "{s}", .{@errorName(err)}) catch return;
|
||||
last_error = msg;
|
||||
}
|
||||
|
||||
fn setLastErrorMessage(msg: []const u8) void {
|
||||
clearLastError();
|
||||
const owned = wasm_allocator.alloc(u8, msg.len) catch return;
|
||||
std.mem.copyForwards(u8, owned, msg);
|
||||
last_error = owned;
|
||||
}
|
||||
|
||||
fn packPtrLen(ptr: [*]u8, len: usize) u64 {
|
||||
const ptr_u32: u32 = @intCast(@intFromPtr(ptr));
|
||||
const len_u32: u32 = @intCast(len);
|
||||
return (@as(u64, ptr_u32) << 32) | @as(u64, len_u32);
|
||||
}
|
||||
|
||||
fn unpackI128Hi(value: i128) i64 {
|
||||
const bits: u128 = @bitCast(value);
|
||||
const hi_bits: u64 = @intCast(bits >> 64);
|
||||
return @bitCast(hi_bits);
|
||||
}
|
||||
|
||||
fn unpackI128Lo(value: i128) u64 {
|
||||
const bits: u128 = @bitCast(value);
|
||||
return @intCast(bits & 0xFFFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
fn joinI128(hi: i64, lo: u64) i128 {
|
||||
const hi_bits: u64 = @bitCast(hi);
|
||||
const value: u128 = (@as(u128, hi_bits) << 64) | @as(u128, lo);
|
||||
return @bitCast(value);
|
||||
}
|
||||
|
||||
fn unitFromCode(code: u8) ?Temporal.Duration.Unit {
|
||||
return switch (code) {
|
||||
1 => .nanosecond,
|
||||
2 => .microsecond,
|
||||
3 => .millisecond,
|
||||
4 => .second,
|
||||
5 => .minute,
|
||||
6 => .hour,
|
||||
7 => .day,
|
||||
8 => .week,
|
||||
9 => .month,
|
||||
10 => .year,
|
||||
11 => .auto,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn roundingModeFromCode(code: u8) ?Temporal.Duration.RoundingMode {
|
||||
return switch (code) {
|
||||
1 => .ceil,
|
||||
2 => .floor,
|
||||
3 => .expand,
|
||||
4 => .trunc,
|
||||
5 => .half_ceil,
|
||||
6 => .half_floor,
|
||||
7 => .half_expand,
|
||||
8 => .half_trunc,
|
||||
9 => .half_even,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
export fn temporalz_last_error_ptr() usize {
|
||||
return if (last_error) |msg| @intFromPtr(msg.ptr) else 0;
|
||||
}
|
||||
|
||||
export fn temporalz_last_error_len() usize {
|
||||
return if (last_error) |msg| msg.len else 0;
|
||||
}
|
||||
|
||||
export fn temporalz_last_error_clear() void {
|
||||
clearLastError();
|
||||
}
|
||||
|
||||
export fn temporalz_alloc(len: usize) usize {
|
||||
clearLastError();
|
||||
const buf = wasm_allocator.alloc(u8, len) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return @intFromPtr(buf.ptr);
|
||||
}
|
||||
|
||||
export fn temporalz_free(ptr: usize, len: usize) void {
|
||||
if (ptr == 0 or len == 0) return;
|
||||
const slice = @as([*]u8, @ptrFromInt(ptr))[0..len];
|
||||
wasm_allocator.free(slice);
|
||||
}
|
||||
|
||||
export fn temporalz_string_free(ptr: usize, len: usize) void {
|
||||
temporalz_free(ptr, len);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_from_utf8(ptr: [*]const u8, len: usize) u32 {
|
||||
clearLastError();
|
||||
const text = ptr[0..len];
|
||||
const inst = Temporal.Instant.from(text) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(inst);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_from_epoch_milliseconds(epoch_ms: i64) u32 {
|
||||
clearLastError();
|
||||
const inst = Temporal.Instant.fromEpochMilliseconds(epoch_ms) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(inst);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_from_epoch_nanoseconds_parts(hi: i64, lo: u64) u32 {
|
||||
clearLastError();
|
||||
const epoch_ns = joinI128(hi, lo);
|
||||
const inst = Temporal.Instant.fromEpochNanoseconds(epoch_ns) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(inst);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_epoch_milliseconds(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return inst.epochMilliseconds();
|
||||
}
|
||||
|
||||
export fn temporalz_instant_epoch_nanoseconds_hi(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return unpackI128Hi(inst.epochNanoseconds());
|
||||
}
|
||||
|
||||
export fn temporalz_instant_epoch_nanoseconds_lo(handle: u32) u64 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return unpackI128Lo(inst.epochNanoseconds());
|
||||
}
|
||||
|
||||
export fn temporalz_instant_to_string(handle: u32) u64 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const text = inst.toString(wasm_allocator, .{}) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return packPtrLen(text.ptr, text.len);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_add(handle: u32, duration_handle: u32) u32 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
var dur = getDuration(duration_handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = inst.add(&dur) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(res);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_subtract(handle: u32, duration_handle: u32) u32 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
var dur = getDuration(duration_handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = inst.subtract(&dur) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(res);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_compare(handle_a: u32, handle_b: u32) i32 {
|
||||
clearLastError();
|
||||
const a = getInstant(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getInstant(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return @intCast(Temporal.Instant.compare(a, b));
|
||||
}
|
||||
|
||||
export fn temporalz_instant_equals(handle_a: u32, handle_b: u32) u8 {
|
||||
clearLastError();
|
||||
const a = getInstant(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getInstant(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return if (Temporal.Instant.equals(a, b)) 1 else 0;
|
||||
}
|
||||
|
||||
export fn temporalz_instant_round(
|
||||
handle: u32,
|
||||
smallest_unit: u8,
|
||||
rounding_mode: u8,
|
||||
rounding_increment: u32,
|
||||
) u32 {
|
||||
clearLastError();
|
||||
const inst = getInstant(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
|
||||
var opts = Temporal.Instant.RoundingOptions{};
|
||||
if (smallest_unit != 255) {
|
||||
opts.smallest_unit = unitFromCode(smallest_unit) orelse {
|
||||
setLastErrorMessage("Invalid smallestUnit");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_mode != 255) {
|
||||
opts.rounding_mode = roundingModeFromCode(rounding_mode) orelse {
|
||||
setLastErrorMessage("Invalid roundingMode");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_increment != 0) {
|
||||
opts.rounding_increment = rounding_increment;
|
||||
}
|
||||
|
||||
const res = inst.round(opts) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addInstant(res);
|
||||
}
|
||||
|
||||
export fn temporalz_instant_destroy(handle: u32) void {
|
||||
removeInstant(handle);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_from_utf8(ptr: [*]const u8, len: usize) u32 {
|
||||
clearLastError();
|
||||
const text = ptr[0..len];
|
||||
const dur = Temporal.Duration.from(text) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(dur);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_from_parts(
|
||||
mask: u32,
|
||||
years: i64,
|
||||
months: i64,
|
||||
weeks: i64,
|
||||
days: i64,
|
||||
hours: i64,
|
||||
minutes: i64,
|
||||
seconds: i64,
|
||||
milliseconds: i64,
|
||||
microseconds: f64,
|
||||
nanoseconds: f64,
|
||||
) u32 {
|
||||
clearLastError();
|
||||
var partial = Temporal.Duration.PartialDuration{};
|
||||
if ((mask & 0x1) != 0) partial.years = years;
|
||||
if ((mask & 0x2) != 0) partial.months = months;
|
||||
if ((mask & 0x4) != 0) partial.weeks = weeks;
|
||||
if ((mask & 0x8) != 0) partial.days = days;
|
||||
if ((mask & 0x10) != 0) partial.hours = hours;
|
||||
if ((mask & 0x20) != 0) partial.minutes = minutes;
|
||||
if ((mask & 0x40) != 0) partial.seconds = seconds;
|
||||
if ((mask & 0x80) != 0) partial.milliseconds = milliseconds;
|
||||
if ((mask & 0x100) != 0) partial.microseconds = microseconds;
|
||||
if ((mask & 0x200) != 0) partial.nanoseconds = nanoseconds;
|
||||
|
||||
const dur = Temporal.Duration.from(partial) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(dur);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_init(
|
||||
years: i64,
|
||||
months: i64,
|
||||
weeks: i64,
|
||||
days: i64,
|
||||
hours: i64,
|
||||
minutes: i64,
|
||||
seconds: i64,
|
||||
milliseconds: i64,
|
||||
microseconds: f64,
|
||||
nanoseconds: f64,
|
||||
) u32 {
|
||||
clearLastError();
|
||||
const dur = Temporal.Duration.init(
|
||||
years,
|
||||
months,
|
||||
weeks,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
milliseconds,
|
||||
microseconds,
|
||||
nanoseconds,
|
||||
) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(dur);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_to_string(handle: u32) u64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const text = dur.toString(wasm_allocator, .{}) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return packPtrLen(text.ptr, text.len);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_years(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.years();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_months(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.months();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_weeks(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.weeks();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_days(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.days();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_hours(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.hours();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_minutes(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.minutes();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_seconds(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.seconds();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_milliseconds(handle: u32) i64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.milliseconds();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_microseconds(handle: u32) f64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.microseconds();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_nanoseconds(handle: u32) f64 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.nanoseconds();
|
||||
}
|
||||
|
||||
export fn temporalz_duration_sign(handle: u32) i32 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return switch (dur.sign()) {
|
||||
.positive => 1,
|
||||
.zero => 0,
|
||||
.negative => -1,
|
||||
};
|
||||
}
|
||||
|
||||
export fn temporalz_duration_blank(handle: u32) u8 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return if (dur.blank()) 1 else 0;
|
||||
}
|
||||
|
||||
export fn temporalz_duration_abs(handle: u32) u32 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(dur.abs());
|
||||
}
|
||||
|
||||
export fn temporalz_duration_negated(handle: u32) u32 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(dur.negated());
|
||||
}
|
||||
|
||||
export fn temporalz_duration_add(handle_a: u32, handle_b: u32) u32 {
|
||||
clearLastError();
|
||||
const a = getDuration(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getDuration(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = a.add(b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(res);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_subtract(handle_a: u32, handle_b: u32) u32 {
|
||||
clearLastError();
|
||||
const a = getDuration(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getDuration(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = a.subtract(b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(res);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_compare(handle_a: u32, handle_b: u32) i32 {
|
||||
clearLastError();
|
||||
const a = getDuration(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getDuration(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = a.compare(b, .{}) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return @intCast(res);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_total(handle: u32, unit_code: u8) f64 {
|
||||
clearLastError();
|
||||
const unit = unitFromCode(unit_code) orelse {
|
||||
setLastErrorMessage("Invalid unit");
|
||||
return 0;
|
||||
};
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.total(.{ .unit = unit }) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
export fn temporalz_duration_round(
|
||||
handle: u32,
|
||||
smallest_unit: u8,
|
||||
largest_unit: u8,
|
||||
rounding_mode: u8,
|
||||
rounding_increment: u32,
|
||||
) u32 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
|
||||
var opts = Temporal.Duration.RoundingOptions{};
|
||||
if (smallest_unit != 255) {
|
||||
opts.smallest_unit = unitFromCode(smallest_unit) orelse {
|
||||
setLastErrorMessage("Invalid smallestUnit");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (largest_unit != 255) {
|
||||
opts.largest_unit = unitFromCode(largest_unit) orelse {
|
||||
setLastErrorMessage("Invalid largestUnit");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_mode != 255) {
|
||||
opts.rounding_mode = roundingModeFromCode(rounding_mode) orelse {
|
||||
setLastErrorMessage("Invalid roundingMode");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_increment != 0) {
|
||||
opts.rounding_increment = rounding_increment;
|
||||
}
|
||||
|
||||
const res = dur.round(opts) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(res);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_compare_plain_date(handle_a: u32, handle_b: u32, date_handle: u32) i32 {
|
||||
clearLastError();
|
||||
const a = getDuration(handle_a) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const b = getDuration(handle_b) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const date = getPlainDate(date_handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const res = a.compare(b, .{ .relative_to = .{ .plain_date = date } }) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return @intCast(res);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_total_plain_date(handle: u32, unit_code: u8, date_handle: u32) f64 {
|
||||
clearLastError();
|
||||
const unit = unitFromCode(unit_code) orelse {
|
||||
setLastErrorMessage("Invalid unit");
|
||||
return 0;
|
||||
};
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const date = getPlainDate(date_handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return dur.total(.{ .unit = unit, .relative_to = .{ .plain_date = date } }) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
export fn temporalz_duration_round_plain_date(
|
||||
handle: u32,
|
||||
smallest_unit: u8,
|
||||
largest_unit: u8,
|
||||
rounding_mode: u8,
|
||||
rounding_increment: u32,
|
||||
date_handle: u32,
|
||||
) u32 {
|
||||
clearLastError();
|
||||
const dur = getDuration(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const date = getPlainDate(date_handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
|
||||
var opts = Temporal.Duration.RoundingOptions{};
|
||||
if (smallest_unit != 255) {
|
||||
opts.smallest_unit = unitFromCode(smallest_unit) orelse {
|
||||
setLastErrorMessage("Invalid smallestUnit");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (largest_unit != 255) {
|
||||
opts.largest_unit = unitFromCode(largest_unit) orelse {
|
||||
setLastErrorMessage("Invalid largestUnit");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_mode != 255) {
|
||||
opts.rounding_mode = roundingModeFromCode(rounding_mode) orelse {
|
||||
setLastErrorMessage("Invalid roundingMode");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
if (rounding_increment != 0) {
|
||||
opts.rounding_increment = rounding_increment;
|
||||
}
|
||||
opts.relative_to = .{ .plain_date = date };
|
||||
|
||||
const res = dur.round(opts) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addDuration(res);
|
||||
}
|
||||
|
||||
export fn temporalz_plain_date_from_utf8(ptr: [*]const u8, len: usize) u32 {
|
||||
clearLastError();
|
||||
const text = ptr[0..len];
|
||||
const date = Temporal.PlainDate.from(text) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addPlainDate(date);
|
||||
}
|
||||
|
||||
export fn temporalz_plain_date_init(year: i32, month: u8, day: u8) u32 {
|
||||
clearLastError();
|
||||
const date = Temporal.PlainDate.init(year, month, day) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return addPlainDate(date);
|
||||
}
|
||||
|
||||
export fn temporalz_plain_date_to_string(handle: u32) u64 {
|
||||
clearLastError();
|
||||
const date = getPlainDate(handle) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
const text = date.toString(wasm_allocator, .{}) catch |err| {
|
||||
setLastError(err);
|
||||
return 0;
|
||||
};
|
||||
return packPtrLen(text.ptr, text.len);
|
||||
}
|
||||
|
||||
export fn temporalz_plain_date_destroy(handle: u32) void {
|
||||
removePlainDate(handle);
|
||||
}
|
||||
|
||||
export fn temporalz_duration_destroy(handle: u32) void {
|
||||
removeDuration(handle);
|
||||
}
|
||||
|
||||
extern fn console(ptr: [*]u8, len: u32) void;
|
||||
|
||||
fn logFn(comptime _: anytype, comptime _: anytype, comptime format: []const u8, args: anytype) void {
|
||||
|
|
|
|||
|
|
@ -1,824 +0,0 @@
|
|||
const std = @import("std");
|
||||
const Temporal = @import("temporalz");
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/basic.js (constructor fields and sign)
|
||||
test "Temporal.Duration constructor components" {
|
||||
const pos = try Temporal.Duration.init(5, 5, 5, 5, 5, 5, 5, 5, 5, 0);
|
||||
defer pos.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.years());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.months());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.days());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.hours());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 5), pos.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 5), pos.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 0), pos.nanoseconds());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.positive, pos.sign());
|
||||
|
||||
const neg = try Temporal.Duration.init(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0);
|
||||
defer neg.deinit();
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.years());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.months());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.weeks());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.days());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.hours());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.minutes());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.seconds());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, -5), neg.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 0), neg.nanoseconds());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.negative, neg.sign());
|
||||
|
||||
const neg_zero = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0);
|
||||
defer neg_zero.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.days());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.hours());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 0), neg_zero.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 0), neg_zero.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 0), neg_zero.nanoseconds());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.zero, neg_zero.sign());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/compare/basic.js
|
||||
test "Temporal.Duration compare time-only" {
|
||||
const td1 = try Temporal.Duration.init(0, 0, 0, 0, 5, 5, 5, 5, 5, 5);
|
||||
defer td1.deinit();
|
||||
const td2 = try Temporal.Duration.init(0, 0, 0, 0, 5, 4, 5, 5, 5, 5);
|
||||
defer td2.deinit();
|
||||
const td1_neg = try Temporal.Duration.init(0, 0, 0, 0, -5, -5, -5, -5, -5, -5);
|
||||
defer td1_neg.deinit();
|
||||
const td2_neg = try Temporal.Duration.init(0, 0, 0, 0, -5, -4, -5, -5, -5, -5);
|
||||
defer td2_neg.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i8, 0), try td1.compare(td1, .{}));
|
||||
try std.testing.expectEqual(@as(i8, -1), try td2.compare(td1, .{}));
|
||||
try std.testing.expectEqual(@as(i8, 1), try td1.compare(td2, .{}));
|
||||
try std.testing.expectEqual(@as(i8, 0), try td1_neg.compare(td1_neg, .{}));
|
||||
try std.testing.expectEqual(@as(i8, 1), try td2_neg.compare(td1_neg, .{}));
|
||||
try std.testing.expectEqual(@as(i8, -1), try td1_neg.compare(td2_neg, .{}));
|
||||
try std.testing.expectEqual(@as(i8, -1), try td1_neg.compare(td2, .{}));
|
||||
try std.testing.expectEqual(@as(i8, 1), try td1.compare(td2_neg, .{}));
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/argument-string.js
|
||||
test "Temporal.Duration from string parsing" {
|
||||
// P1D
|
||||
const d1 = try Temporal.Duration.from("P1D");
|
||||
defer d1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 1), d1.days());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.hours());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.milliseconds());
|
||||
|
||||
// p1y1m1dt1h1m1s
|
||||
const d2 = try Temporal.Duration.from("p1y1m1dt1h1m1s");
|
||||
defer d2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.years());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d2.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.days());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.hours());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.seconds());
|
||||
|
||||
// P1Y1M1W1DT1H1M1.123456789S
|
||||
const d3 = try Temporal.Duration.from("P1Y1M1W1DT1H1M1.123456789S");
|
||||
defer d3.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.years());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.months());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.days());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.hours());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 1), d3.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 123), d3.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 456), d3.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 789), d3.nanoseconds());
|
||||
|
||||
// P1DT0.5M (0.5 minutes)
|
||||
const d4 = try Temporal.Duration.from("P1DT0.5M");
|
||||
defer d4.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d4.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), d4.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d4.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 1), d4.days());
|
||||
try std.testing.expectEqual(@as(i64, 0), d4.hours());
|
||||
try std.testing.expectEqual(@as(i64, 0), d4.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 30), d4.seconds());
|
||||
|
||||
// -P1Y1M1W1DT1H1M1.123456789S (negative)
|
||||
const d5 = try Temporal.Duration.from("-P1Y1M1W1DT1H1M1.123456789S");
|
||||
defer d5.deinit();
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.years());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.months());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.weeks());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.days());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.hours());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.minutes());
|
||||
try std.testing.expectEqual(@as(i64, -1), d5.seconds());
|
||||
|
||||
// PT100M
|
||||
const d6 = try Temporal.Duration.from("PT100M");
|
||||
defer d6.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d6.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), d6.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d6.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), d6.days());
|
||||
try std.testing.expectEqual(@as(i64, 0), d6.hours());
|
||||
try std.testing.expectEqual(@as(i64, 100), d6.minutes());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/abs/basic.js
|
||||
test "Temporal.Duration abs" {
|
||||
// blank duration
|
||||
const d1 = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d1.deinit();
|
||||
const abs1 = d1.abs();
|
||||
defer abs1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), abs1.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), abs1.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), abs1.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), abs1.days());
|
||||
|
||||
// positive values
|
||||
const d2 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer d2.deinit();
|
||||
const abs2 = d2.abs();
|
||||
defer abs2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), abs2.years());
|
||||
try std.testing.expectEqual(@as(i64, 2), abs2.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), abs2.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 4), abs2.days());
|
||||
try std.testing.expectEqual(@as(i64, 5), abs2.hours());
|
||||
try std.testing.expectEqual(@as(i64, 6), abs2.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 7), abs2.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 8), abs2.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 9), abs2.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 10), abs2.nanoseconds());
|
||||
|
||||
// negative values
|
||||
const d3 = try Temporal.Duration.init(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
|
||||
defer d3.deinit();
|
||||
const abs3 = d3.abs();
|
||||
defer abs3.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), abs3.years());
|
||||
try std.testing.expectEqual(@as(i64, 2), abs3.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), abs3.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 4), abs3.days());
|
||||
try std.testing.expectEqual(@as(i64, 5), abs3.hours());
|
||||
try std.testing.expectEqual(@as(i64, 6), abs3.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 7), abs3.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 8), abs3.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 9), abs3.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 10), abs3.nanoseconds());
|
||||
|
||||
// some zeros
|
||||
const d4 = try Temporal.Duration.init(1, 0, 3, 0, 5, 0, 7, 0, 9, 0);
|
||||
defer d4.deinit();
|
||||
const abs4 = d4.abs();
|
||||
defer abs4.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), abs4.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), abs4.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), abs4.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), abs4.days());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/negated/basic.js
|
||||
test "Temporal.Duration negated" {
|
||||
// blank duration
|
||||
const d1 = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d1.deinit();
|
||||
const neg1 = d1.negated();
|
||||
defer neg1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), neg1.years());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.zero, neg1.sign());
|
||||
|
||||
// positive values
|
||||
const d2 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer d2.deinit();
|
||||
const neg2 = d2.negated();
|
||||
defer neg2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, -1), neg2.years());
|
||||
try std.testing.expectEqual(@as(i64, -2), neg2.months());
|
||||
try std.testing.expectEqual(@as(i64, -3), neg2.weeks());
|
||||
try std.testing.expectEqual(@as(i64, -4), neg2.days());
|
||||
try std.testing.expectEqual(@as(i64, -5), neg2.hours());
|
||||
try std.testing.expectEqual(@as(i64, -6), neg2.minutes());
|
||||
try std.testing.expectEqual(@as(i64, -7), neg2.seconds());
|
||||
try std.testing.expectEqual(@as(i64, -8), neg2.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, -9), neg2.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, -10), neg2.nanoseconds());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.negative, neg2.sign());
|
||||
|
||||
// negative values
|
||||
const d3 = try Temporal.Duration.init(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10);
|
||||
defer d3.deinit();
|
||||
const neg3 = d3.negated();
|
||||
defer neg3.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), neg3.years());
|
||||
try std.testing.expectEqual(@as(i64, 2), neg3.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), neg3.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 4), neg3.days());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.positive, neg3.sign());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/add/basic.js
|
||||
test "Temporal.Duration add time-only" {
|
||||
// Simple time additions
|
||||
const td1 = try Temporal.Duration.init(0, 0, 0, 0, 1, 2, 3, 4, 5, 6);
|
||||
defer td1.deinit();
|
||||
const td2 = try Temporal.Duration.init(0, 0, 0, 0, 1, 2, 3, 4, 5, 6);
|
||||
defer td2.deinit();
|
||||
const result = try td1.add(td2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), result.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), result.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), result.days());
|
||||
try std.testing.expectEqual(@as(i64, 2), result.hours());
|
||||
try std.testing.expectEqual(@as(i64, 4), result.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 6), result.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 8), result.milliseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/subtract/basic.js
|
||||
test "Temporal.Duration subtract time-only" {
|
||||
// Simple time subtraction
|
||||
const td1 = try Temporal.Duration.init(0, 0, 0, 0, 5, 5, 5, 5, 5, 5);
|
||||
defer td1.deinit();
|
||||
const td2 = try Temporal.Duration.init(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
|
||||
defer td2.deinit();
|
||||
const result = try td1.subtract(td2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 4), result.hours());
|
||||
try std.testing.expectEqual(@as(i64, 4), result.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 4), result.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 4), result.milliseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/blank/basic.js
|
||||
test "Temporal.Duration blank" {
|
||||
// Blank duration
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
try std.testing.expectEqual(true, blank.blank());
|
||||
|
||||
// Non-blank durations
|
||||
const d1 = try Temporal.Duration.init(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d1.deinit();
|
||||
try std.testing.expectEqual(false, d1.blank());
|
||||
|
||||
const d2 = try Temporal.Duration.init(0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d2.deinit();
|
||||
try std.testing.expectEqual(false, d2.blank());
|
||||
|
||||
const d3 = try Temporal.Duration.init(0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d3.deinit();
|
||||
try std.testing.expectEqual(false, d3.blank());
|
||||
|
||||
const d4 = try Temporal.Duration.init(0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
|
||||
defer d4.deinit();
|
||||
try std.testing.expectEqual(false, d4.blank());
|
||||
|
||||
const d5 = try Temporal.Duration.init(0, 0, 0, 0, 1, 0, 0, 0, 0, 0);
|
||||
defer d5.deinit();
|
||||
try std.testing.expectEqual(false, d5.blank());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/toString/basic.js
|
||||
test "Temporal.Duration toString" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// Blank duration
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
const str1 = try blank.toString(allocator, .{});
|
||||
defer allocator.free(str1);
|
||||
try std.testing.expectEqualSlices(u8, "PT0S", str1);
|
||||
|
||||
// Hours and minutes
|
||||
const td1 = try Temporal.Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
|
||||
defer td1.deinit();
|
||||
const str2 = try td1.toString(allocator, .{});
|
||||
defer allocator.free(str2);
|
||||
try std.testing.expectEqualSlices(u8, "PT1H30M", str2);
|
||||
|
||||
// Full duration
|
||||
const td2 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer td2.deinit();
|
||||
const str3 = try td2.toString(allocator, .{});
|
||||
defer allocator.free(str3);
|
||||
// Result should contain Y, M, W, D, H, M, S, etc.
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, str3, 1, "Y"));
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, str3, 1, "W"));
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, str3, 1, "D"));
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/toJSON/basic.js
|
||||
test "Temporal.Duration toJSON" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
const json = try blank.toJSON(allocator);
|
||||
defer allocator.free(json);
|
||||
try std.testing.expectEqualSlices(u8, "PT0S", json);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/total/basic.js
|
||||
test "Temporal.Duration total" {
|
||||
const td = try Temporal.Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
|
||||
defer td.deinit();
|
||||
|
||||
// total hours
|
||||
const hours = try td.total(.{ .unit = Temporal.Duration.Unit.hour });
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 1.5), hours, 0.0001);
|
||||
|
||||
// total minutes
|
||||
const minutes = try td.total(.{ .unit = Temporal.Duration.Unit.minute });
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 90), minutes, 0.0001);
|
||||
|
||||
// total seconds
|
||||
const seconds = try td.total(.{ .unit = Temporal.Duration.Unit.second });
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 5400), seconds, 0.0001);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/constructor.js
|
||||
test "Temporal.Duration cannot be called without new" {
|
||||
// Note: In Zig we use a function, not a constructor, so this test is about ensuring proper init
|
||||
// Testing that we can successfully create a duration
|
||||
const dur = try Temporal.Duration.init(1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
|
||||
defer dur.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), dur.years());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/days-undefined.js
|
||||
test "Temporal.Duration with specific fields from ISO string" {
|
||||
const d1 = try Temporal.Duration.from("P1Y");
|
||||
defer d1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d1.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.days());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.hours());
|
||||
|
||||
const d2 = try Temporal.Duration.from("PT1H");
|
||||
defer d2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d2.years());
|
||||
try std.testing.expectEqual(@as(i64, 0), d2.days());
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.hours());
|
||||
try std.testing.expectEqual(@as(i64, 0), d2.minutes());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/mixed.js
|
||||
test "Temporal.Duration mixed components" {
|
||||
const d1 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer d1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d1.years());
|
||||
try std.testing.expectEqual(@as(i64, 2), d1.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), d1.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 4), d1.days());
|
||||
try std.testing.expectEqual(@as(i64, 5), d1.hours());
|
||||
try std.testing.expectEqual(@as(i64, 6), d1.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 7), d1.seconds());
|
||||
try std.testing.expectEqual(@as(i64, 8), d1.milliseconds());
|
||||
try std.testing.expectEqual(@as(f64, 9), d1.microseconds());
|
||||
try std.testing.expectEqual(@as(f64, 10), d1.nanoseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/sign/basic.js
|
||||
test "Temporal.Duration sign property" {
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.zero, blank.sign());
|
||||
|
||||
const pos = try Temporal.Duration.init(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer pos.deinit();
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.positive, pos.sign());
|
||||
|
||||
const neg = try Temporal.Duration.init(-1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer neg.deinit();
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.negative, neg.sign());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/large-number.js
|
||||
test "Temporal.Duration with large numbers" {
|
||||
const d = try Temporal.Duration.init(100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000, 100000);
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 100000), d.years());
|
||||
try std.testing.expectEqual(@as(i64, 100000), d.months());
|
||||
try std.testing.expectEqual(@as(i64, 100000), d.weeks());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/blank-duration.js
|
||||
test "Temporal.Duration blank from string" {
|
||||
const blank1 = try Temporal.Duration.from("PT0S");
|
||||
defer blank1.deinit();
|
||||
try std.testing.expectEqual(true, blank1.blank());
|
||||
|
||||
const blank2 = try Temporal.Duration.from("P0D");
|
||||
defer blank2.deinit();
|
||||
try std.testing.expectEqual(true, blank2.blank());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/length.js
|
||||
test "Temporal.Duration constructor has correct arity" {
|
||||
// In Zig, we test that init can be called with the expected parameters
|
||||
const d = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer d.deinit();
|
||||
try std.testing.expect(true); // If we get here, init works
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/length.js
|
||||
test "Temporal.Duration from has correct arity" {
|
||||
const d = try Temporal.Duration.from("P1D");
|
||||
defer d.deinit();
|
||||
try std.testing.expect(true); // If we get here, from works
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/add/basic.js with normal values
|
||||
test "Temporal.Duration add with time fields" {
|
||||
const d1 = try Temporal.Duration.init(0, 0, 0, 0, 1, 1, 1, 1, 0, 0);
|
||||
defer d1.deinit();
|
||||
const d2 = try Temporal.Duration.init(0, 0, 0, 0, 2, 2, 2, 2, 0, 0);
|
||||
defer d2.deinit();
|
||||
|
||||
const result = try d1.add(d2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 3), result.hours());
|
||||
try std.testing.expectEqual(@as(i64, 3), result.minutes());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/add/basic.js with date and time
|
||||
test "Temporal.Duration add with date and time components" {
|
||||
// Note: Adding durations with date components together requires a RelativeTo context
|
||||
// which is not always available, causing a RangeError
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const d1 = try Temporal.Duration.init(100, 100, 100, 100, 100, 100, 100, 100, 100, 100);
|
||||
defer d1.deinit();
|
||||
const d2 = try Temporal.Duration.init(100, 100, 100, 100, 100, 100, 100, 100, 100, 100);
|
||||
defer d2.deinit();
|
||||
|
||||
const result = try d1.add(d2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 200), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 200), result.months());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/subtract/basic.js with normal values
|
||||
test "Temporal.Duration subtract with time fields" {
|
||||
const d1 = try Temporal.Duration.init(0, 0, 0, 0, 5, 5, 5, 5, 0, 0);
|
||||
defer d1.deinit();
|
||||
const d2 = try Temporal.Duration.init(0, 0, 0, 0, 2, 2, 2, 2, 0, 0);
|
||||
defer d2.deinit();
|
||||
|
||||
const result = try d1.subtract(d2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 3), result.hours());
|
||||
try std.testing.expectEqual(@as(i64, 3), result.minutes());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/subtract/basic.js with date and time
|
||||
test "Temporal.Duration subtract with date and time components" {
|
||||
// Note: Subtracting durations with date components together requires a RelativeTo context
|
||||
// which is not always available, causing a RangeError
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const d1 = try Temporal.Duration.init(200, 200, 200, 200, 200, 200, 200, 200, 200, 200);
|
||||
defer d1.deinit();
|
||||
const d2 = try Temporal.Duration.init(100, 100, 100, 100, 100, 100, 100, 100, 100, 100);
|
||||
defer d2.deinit();
|
||||
|
||||
const result = try d1.subtract(d2);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 100), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 100), result.months());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/name.js
|
||||
test "Temporal.Duration constructor name" {
|
||||
// In Zig we can't directly test function name, but we test that the constructor exists and works
|
||||
const d = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d.deinit();
|
||||
try std.testing.expect(true);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/argument-duration.js
|
||||
test "Temporal.Duration from duration object" {
|
||||
const d1 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
defer d1.deinit();
|
||||
|
||||
// In Zig binding, we'd typically test string parsing instead
|
||||
const d2 = try Temporal.Duration.from("P1Y2M3W4DT5H6M7.008009010S");
|
||||
defer d2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d2.years());
|
||||
try std.testing.expectEqual(@as(i64, 2), d2.months());
|
||||
try std.testing.expectEqual(@as(i64, 3), d2.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 4), d2.days());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/with/basic.js (approximation)
|
||||
test "Temporal.Duration with method basic" {
|
||||
// Skip - requires with() method which may not be available
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/with/all-positive.js
|
||||
test "Temporal.Duration with positive components" {
|
||||
// Skip - requires with() method
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/round/basic.js
|
||||
test "Temporal.Duration round method" {
|
||||
// Skip - requires round() method with RoundingOptions
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/round/largest-unit.js
|
||||
test "Temporal.Duration round with largest unit" {
|
||||
// Skip - requires round() method
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/round/smallest-unit.js
|
||||
test "Temporal.Duration round with smallest unit" {
|
||||
// Skip - requires round() method
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/years-undefined.js
|
||||
test "Temporal.Duration years property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("PT1H");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.years());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/months-undefined.js
|
||||
test "Temporal.Duration months property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("PT1H");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.months());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/weeks-undefined.js
|
||||
test "Temporal.Duration weeks property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("PT1H");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.weeks());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/days-undefined.js (property access)
|
||||
test "Temporal.Duration days property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("PT1H");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.days());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/hours-undefined.js
|
||||
test "Temporal.Duration hours property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.hours());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/minutes-undefined.js
|
||||
test "Temporal.Duration minutes property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.minutes());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/seconds-undefined.js
|
||||
test "Temporal.Duration seconds property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.seconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/milliseconds-undefined.js
|
||||
test "Temporal.Duration milliseconds property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 0), d.milliseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/microseconds-undefined.js
|
||||
test "Temporal.Duration microseconds property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(f64, 0), d.microseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/nanoseconds-undefined.js
|
||||
test "Temporal.Duration nanoseconds property undefined behavior" {
|
||||
const d = try Temporal.Duration.from("P1Y");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(f64, 0), d.nanoseconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/max.js
|
||||
test "Temporal.Duration max value handling" {
|
||||
// Test that very large durations can be created
|
||||
const d = try Temporal.Duration.init(999999999, 999999999, 999999999, 999999999, 999999999, 999999999, 999999999, 999999999, 999999999, 999999999);
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 999999999), d.years());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/lower-limit.js
|
||||
test "Temporal.Duration lower limit handling" {
|
||||
// Test negative duration handling
|
||||
const d = try Temporal.Duration.init(-999999999, -999999999, -999999999, -999999999, -999999999, -999999999, -999999999, -999999999, -999999999, -999999999);
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, -999999999), d.years());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/out-of-range.js
|
||||
test "Temporal.Duration out of range behavior" {
|
||||
// Skip - would require error handling for invalid ranges
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/invalid-type.js
|
||||
test "Temporal.Duration invalid type handling" {
|
||||
// Skip - type validation would be at binding level
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/fractional-throws-rangeerror.js
|
||||
test "Temporal.Duration fractional range error" {
|
||||
// Skip - fractional components validation
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/infinity-throws-rangeerror.js
|
||||
test "Temporal.Duration infinity range error" {
|
||||
// Skip - infinity handling at binding level
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js
|
||||
test "Temporal.Duration negative infinity range error" {
|
||||
// Skip - infinity handling at binding level
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/builtin.js
|
||||
test "Temporal.Duration builtin verification" {
|
||||
const d = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d.deinit();
|
||||
try std.testing.expect(true);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/call-builtin.js
|
||||
test "Temporal.Duration call as builtin" {
|
||||
// In Zig, Duration is created via init function, not called as builtin
|
||||
const d = try Temporal.Duration.init(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d.years());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/valueOf/basic.js
|
||||
test "Temporal.Duration valueOf method" {
|
||||
// Skip - valueOf may not be directly comparable in Zig
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/compare/blank-duration.js
|
||||
test "Temporal.Duration compare blank durations" {
|
||||
const blank1 = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank1.deinit();
|
||||
const blank2 = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank2.deinit();
|
||||
|
||||
const result = try blank1.compare(blank2, .{});
|
||||
try std.testing.expectEqual(@as(i8, 0), result);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/blank-duration.js (property test)
|
||||
test "Temporal.Duration blank property from string" {
|
||||
const d = try Temporal.Duration.from("PT0S");
|
||||
defer d.deinit();
|
||||
try std.testing.expectEqual(true, d.blank());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/abs/new-object.js
|
||||
test "Temporal.Duration abs creates new object" {
|
||||
const d1 = try Temporal.Duration.init(-5, -5, -5, -5, -5, -5, -5, -5, -5, -5);
|
||||
defer d1.deinit();
|
||||
const d2 = d1.abs();
|
||||
defer d2.deinit();
|
||||
|
||||
// Verify they have different signs
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.negative, d1.sign());
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.positive, d2.sign());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/negated/basic.js (zero handling)
|
||||
test "Temporal.Duration negated zero duration" {
|
||||
const zero = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer zero.deinit();
|
||||
const negated = zero.negated();
|
||||
defer negated.deinit();
|
||||
|
||||
try std.testing.expectEqual(Temporal.Duration.Sign.zero, negated.sign());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/from/string-with-skipped-units.js
|
||||
test "Temporal.Duration from string with skipped units" {
|
||||
const d1 = try Temporal.Duration.from("P1Y3M");
|
||||
defer d1.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), d1.years());
|
||||
try std.testing.expectEqual(@as(i64, 3), d1.months());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.weeks());
|
||||
try std.testing.expectEqual(@as(i64, 0), d1.days());
|
||||
|
||||
const d2 = try Temporal.Duration.from("PT2H30M");
|
||||
defer d2.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 2), d2.hours());
|
||||
try std.testing.expectEqual(@as(i64, 30), d2.minutes());
|
||||
try std.testing.expectEqual(@as(i64, 0), d2.seconds());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/add/blank-duration.js
|
||||
test "Temporal.Duration add blank duration" {
|
||||
if (true) return error.SkipZigTest;
|
||||
const d1 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 0, 0);
|
||||
defer d1.deinit();
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
|
||||
const result = try d1.add(blank);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 5), result.hours());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/subtract/blank-duration.js
|
||||
test "Temporal.Duration subtract blank duration" {
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const d1 = try Temporal.Duration.init(1, 2, 3, 4, 5, 6, 7, 8, 0, 0);
|
||||
defer d1.deinit();
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
|
||||
const result = try d1.subtract(blank);
|
||||
defer result.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 1), result.years());
|
||||
try std.testing.expectEqual(@as(i64, 5), result.hours());
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js
|
||||
test "Temporal.Duration toString blank with precision" {
|
||||
const allocator = std.testing.allocator;
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
const str = try blank.toString(allocator, .{});
|
||||
defer allocator.free(str);
|
||||
try std.testing.expectEqualSlices(u8, "PT0S", str);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prototype/toJSON/blank-duration.js (approx)
|
||||
test "Temporal.Duration toJSON blank" {
|
||||
const allocator = std.testing.allocator;
|
||||
const blank = try Temporal.Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
defer blank.deinit();
|
||||
const json = try blank.toJSON(allocator);
|
||||
defer allocator.free(json);
|
||||
try std.testing.expectEqualSlices(u8, "PT0S", json);
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/prop-desc.js
|
||||
test "Temporal.Duration property descriptor" {
|
||||
// JS-specific test for property descriptors (writable, enumerable, configurable)
|
||||
// Not directly applicable to Zig bindings
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/subclass.js
|
||||
test "Temporal.Duration subclassing" {
|
||||
// JS-specific test for subclassing Temporal.Duration
|
||||
// Zig does not support class inheritance in the same way
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
|
||||
// Mirrors test262 test/built-ins/Temporal/Duration/get-prototype-from-constructor-throws.js
|
||||
test "Temporal.Duration get prototype from constructor throws" {
|
||||
// JS-specific test for OrdinaryCreateFromConstructor prototype handling
|
||||
// Not applicable to Zig bindings
|
||||
if (true) return error.SkipZigTest;
|
||||
}
|
||||
683
test/test262/polyfill.js
Normal file
683
test/test262/polyfill.js
Normal file
|
|
@ -0,0 +1,683 @@
|
|||
// This polyfill is executed in a vm.Script context by test262-runner,
|
||||
// so it must be synchronous and self-contained.
|
||||
// The wasm bytes are injected as global.__TEMPORALZ_WASM_BYTES__ by the runner.
|
||||
(function () {
|
||||
if (!globalThis.__TEMPORALZ_WASM_BYTES__) {
|
||||
throw new Error("WASM bytes not injected into test context");
|
||||
}
|
||||
|
||||
const wasmBinary = globalThis.__TEMPORALZ_WASM_BYTES__;
|
||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, {
|
||||
env: {
|
||||
console(ptr, len) {
|
||||
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
||||
globalThis.console.log(decoder.decode(bytes));
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const exports = wasmInstance.exports;
|
||||
const memory = exports.memory;
|
||||
|
||||
const encoder = {
|
||||
encode(str) {
|
||||
const buf = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const code = str.charCodeAt(i);
|
||||
if (code < 0x80) {
|
||||
buf.push(code);
|
||||
} else if (code < 0x800) {
|
||||
buf.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
buf.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
|
||||
} else {
|
||||
const code2 = str.charCodeAt(++i);
|
||||
const codePoint = 0x10000 + (((code & 0x3ff) << 10) | (code2 & 0x3ff));
|
||||
buf.push(
|
||||
0xf0 | (codePoint >> 18),
|
||||
0x80 | ((codePoint >> 12) & 0x3f),
|
||||
0x80 | ((codePoint >> 6) & 0x3f),
|
||||
0x80 | (codePoint & 0x3f)
|
||||
);
|
||||
}
|
||||
}
|
||||
return new Uint8Array(buf);
|
||||
},
|
||||
};
|
||||
|
||||
const decoder = {
|
||||
decode(bytes) {
|
||||
let str = "";
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
const byte = bytes[i];
|
||||
if (byte < 0x80) {
|
||||
str += String.fromCharCode(byte);
|
||||
} else if ((byte & 0xe0) === 0xc0) {
|
||||
str += String.fromCharCode(((byte & 0x1f) << 6) | (bytes[++i] & 0x3f));
|
||||
} else if ((byte & 0xf0) === 0xe0) {
|
||||
str += String.fromCharCode(
|
||||
((byte & 0x0f) << 12) | ((bytes[++i] & 0x3f) << 6) | (bytes[++i] & 0x3f)
|
||||
);
|
||||
} else if ((byte & 0xf8) === 0xf0) {
|
||||
const codePoint =
|
||||
((byte & 0x07) << 18) |
|
||||
((bytes[++i] & 0x3f) << 12) |
|
||||
((bytes[++i] & 0x3f) << 6) |
|
||||
(bytes[++i] & 0x3f);
|
||||
const high = ((codePoint - 0x10000) >> 10) + 0xd800;
|
||||
const low = ((codePoint - 0x10000) & 0x3ff) + 0xdc00;
|
||||
str += String.fromCharCode(high, low);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
},
|
||||
};
|
||||
|
||||
function readString(ptr, len) {
|
||||
return decoder.decode(new Uint8Array(memory.buffer, ptr, len));
|
||||
}
|
||||
|
||||
function lastError() {
|
||||
const ptr = exports.temporalz_last_error_ptr();
|
||||
const len = exports.temporalz_last_error_len();
|
||||
if (!ptr || !len) return new Error("temporalz error");
|
||||
const msg = readString(ptr, len);
|
||||
exports.temporalz_last_error_clear();
|
||||
if (msg.includes("Range")) return new RangeError(msg);
|
||||
if (msg.includes("Type")) return new TypeError(msg);
|
||||
return new Error(msg);
|
||||
}
|
||||
|
||||
function requireHandle(handle) {
|
||||
if (!handle) throw lastError();
|
||||
return handle;
|
||||
}
|
||||
|
||||
function allocString(text) {
|
||||
const bytes = encoder.encode(text);
|
||||
const ptr = exports.temporalz_alloc(bytes.length);
|
||||
if (!ptr) throw lastError();
|
||||
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
|
||||
return { ptr, len: bytes.length };
|
||||
}
|
||||
|
||||
function takeString(packed) {
|
||||
if (!packed) throw lastError();
|
||||
const value = BigInt(packed);
|
||||
const ptr = Number(value >> 32n);
|
||||
const len = Number(value & 0xffffffffn);
|
||||
const text = readString(ptr, len);
|
||||
exports.temporalz_string_free(ptr, len);
|
||||
return text;
|
||||
}
|
||||
|
||||
function splitI128(value) {
|
||||
const v = typeof value === "bigint" ? value : BigInt(value);
|
||||
const mask = (1n << 64n) - 1n;
|
||||
return { hi: v >> 64n, lo: v & mask };
|
||||
}
|
||||
|
||||
function joinI128(hi, lo) {
|
||||
const mask = (1n << 64n) - 1n;
|
||||
return (BigInt(hi) << 64n) | (BigInt(lo) & mask);
|
||||
}
|
||||
|
||||
const unitCodes = {
|
||||
nanosecond: 1,
|
||||
microsecond: 2,
|
||||
millisecond: 3,
|
||||
second: 4,
|
||||
minute: 5,
|
||||
hour: 6,
|
||||
day: 7,
|
||||
week: 8,
|
||||
month: 9,
|
||||
year: 10,
|
||||
auto: 11,
|
||||
};
|
||||
|
||||
const roundingModeCodes = {
|
||||
ceil: 1,
|
||||
floor: 2,
|
||||
expand: 3,
|
||||
trunc: 4,
|
||||
halfCeil: 5,
|
||||
halfFloor: 6,
|
||||
halfExpand: 7,
|
||||
halfTrunc: 8,
|
||||
halfEven: 9,
|
||||
};
|
||||
|
||||
function toUnitCode(value, name) {
|
||||
if (value === undefined || value === null) return 255;
|
||||
const code = unitCodes[value];
|
||||
if (!code) throw new RangeError(`Invalid ${name}`);
|
||||
return code;
|
||||
}
|
||||
|
||||
function toRoundingModeCode(value) {
|
||||
if (value === undefined || value === null) return 255;
|
||||
const code = roundingModeCodes[value];
|
||||
if (!code) throw new RangeError("Invalid roundingMode");
|
||||
return code;
|
||||
}
|
||||
|
||||
function toIntegerBigInt(value, name) {
|
||||
if (typeof value === "bigint") return value;
|
||||
const number = Number(value);
|
||||
if (!isFiniteNumber(number)) throw new RangeError(`${name} must be finite`);
|
||||
if (!Number.isInteger(number)) throw new RangeError(`${name} must be an integer`);
|
||||
return BigInt(number);
|
||||
}
|
||||
|
||||
function toNumber(value, name) {
|
||||
const number = Number(value);
|
||||
if (!isFiniteNumber(number)) throw new RangeError(`${name} must be finite`);
|
||||
return number;
|
||||
}
|
||||
|
||||
function toInteger(value, name) {
|
||||
const number = Number(value);
|
||||
if (!isFiniteNumber(number)) throw new RangeError(`${name} must be finite`);
|
||||
if (!Number.isInteger(number)) throw new RangeError(`${name} must be an integer`);
|
||||
return number;
|
||||
}
|
||||
|
||||
function isFiniteNumber(value) {
|
||||
return value === value && value !== Infinity && value !== -Infinity;
|
||||
}
|
||||
|
||||
function unimplemented(name) {
|
||||
return function () {
|
||||
throw new Error(`${name} is not implemented yet`);
|
||||
};
|
||||
}
|
||||
|
||||
class Instant {
|
||||
constructor(handle) {
|
||||
this._handle = handle;
|
||||
}
|
||||
|
||||
static _fromHandle(handle) {
|
||||
return new Instant(handle);
|
||||
}
|
||||
|
||||
static fromEpochMilliseconds(epochMs) {
|
||||
const number = Number(epochMs);
|
||||
if (!isFiniteNumber(number)) throw new RangeError("epochMilliseconds must be finite");
|
||||
const handle = exports.temporalz_instant_from_epoch_milliseconds(number);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
static fromEpochNanoseconds(epochNs) {
|
||||
const parts = splitI128(epochNs);
|
||||
const handle = exports.temporalz_instant_from_epoch_nanoseconds_parts(
|
||||
parts.hi,
|
||||
parts.lo
|
||||
);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
static from(value) {
|
||||
if (value instanceof Instant) return value;
|
||||
if (typeof value === "string") {
|
||||
const text = allocString(value);
|
||||
const handle = exports.temporalz_instant_from_utf8(text.ptr, text.len);
|
||||
exports.temporalz_free(text.ptr, text.len);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
if (value.epochNanoseconds !== undefined) {
|
||||
return Instant.fromEpochNanoseconds(value.epochNanoseconds);
|
||||
}
|
||||
if (value.epochMilliseconds !== undefined) {
|
||||
return Instant.fromEpochMilliseconds(value.epochMilliseconds);
|
||||
}
|
||||
}
|
||||
throw new TypeError("Instant.from expects a string or object");
|
||||
}
|
||||
|
||||
get epochMilliseconds() {
|
||||
return exports.temporalz_instant_epoch_milliseconds(this._handle);
|
||||
}
|
||||
|
||||
get epochNanoseconds() {
|
||||
const hi = exports.temporalz_instant_epoch_nanoseconds_hi(this._handle);
|
||||
const lo = exports.temporalz_instant_epoch_nanoseconds_lo(this._handle);
|
||||
return joinI128(hi, lo);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return takeString(exports.temporalz_instant_to_string(this._handle));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
add(durationLike) {
|
||||
const dur = Duration.from(durationLike);
|
||||
const handle = exports.temporalz_instant_add(this._handle, dur._handle);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
subtract(durationLike) {
|
||||
const dur = Duration.from(durationLike);
|
||||
const handle = exports.temporalz_instant_subtract(this._handle, dur._handle);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
round(options) {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new TypeError("round options must be an object");
|
||||
}
|
||||
const smallestUnit = toUnitCode(options.smallestUnit, "smallestUnit");
|
||||
if (smallestUnit === 255) throw new RangeError("smallestUnit is required");
|
||||
const roundingMode = toRoundingModeCode(options.roundingMode);
|
||||
|
||||
let roundingIncrement = 0;
|
||||
if (options.roundingIncrement !== undefined) {
|
||||
const inc = Number(options.roundingIncrement);
|
||||
if (!isFiniteNumber(inc) || !Number.isInteger(inc) || inc <= 0) {
|
||||
throw new RangeError("Invalid roundingIncrement");
|
||||
}
|
||||
roundingIncrement = inc;
|
||||
}
|
||||
|
||||
const handle = exports.temporalz_instant_round(
|
||||
this._handle,
|
||||
smallestUnit,
|
||||
roundingMode,
|
||||
roundingIncrement
|
||||
);
|
||||
return Instant._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
const rhs = Instant.from(other);
|
||||
return exports.temporalz_instant_equals(this._handle, rhs._handle) === 1;
|
||||
}
|
||||
|
||||
toLocaleString() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
throw new TypeError("Cannot convert Temporal.Instant to a number");
|
||||
}
|
||||
|
||||
static compare(a, b) {
|
||||
const left = Instant.from(a);
|
||||
const right = Instant.from(b);
|
||||
return Number(exports.temporalz_instant_compare(left._handle, right._handle));
|
||||
}
|
||||
}
|
||||
|
||||
const plainDateHandleToken = Symbol("PlainDateHandle");
|
||||
|
||||
class PlainDate {
|
||||
constructor(year, month, day) {
|
||||
if (year === plainDateHandleToken) {
|
||||
this._handle = month;
|
||||
return;
|
||||
}
|
||||
|
||||
const yearValue = toInteger(year ?? 0, "year");
|
||||
const monthValue = toInteger(month ?? 0, "month");
|
||||
const dayValue = toInteger(day ?? 0, "day");
|
||||
const handle = exports.temporalz_plain_date_init(yearValue, monthValue, dayValue);
|
||||
this._handle = requireHandle(handle);
|
||||
}
|
||||
|
||||
static _fromHandle(handle) {
|
||||
return new PlainDate(plainDateHandleToken, handle);
|
||||
}
|
||||
|
||||
static from(value) {
|
||||
if (value instanceof PlainDate) return value;
|
||||
if (typeof value === "string") {
|
||||
const text = allocString(value);
|
||||
const handle = exports.temporalz_plain_date_from_utf8(text.ptr, text.len);
|
||||
exports.temporalz_free(text.ptr, text.len);
|
||||
return PlainDate._fromHandle(requireHandle(handle));
|
||||
}
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new TypeError("PlainDate.from expects a string or object");
|
||||
}
|
||||
|
||||
const year = toInteger(value.year, "year");
|
||||
const month = toInteger(value.month, "month");
|
||||
const day = toInteger(value.day, "day");
|
||||
const handle = exports.temporalz_plain_date_init(year, month, day);
|
||||
return PlainDate._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
toString() {
|
||||
return takeString(exports.temporalz_plain_date_to_string(this._handle));
|
||||
}
|
||||
}
|
||||
|
||||
const durationHandleToken = Symbol("DurationHandle");
|
||||
|
||||
class Duration {
|
||||
constructor(
|
||||
years,
|
||||
months,
|
||||
weeks,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
milliseconds,
|
||||
microseconds,
|
||||
nanoseconds
|
||||
) {
|
||||
if (years === durationHandleToken) {
|
||||
this._handle = months;
|
||||
return;
|
||||
}
|
||||
|
||||
const yearsValue = toIntegerBigInt(years ?? 0, "years");
|
||||
const monthsValue = toIntegerBigInt(months ?? 0, "months");
|
||||
const weeksValue = toIntegerBigInt(weeks ?? 0, "weeks");
|
||||
const daysValue = toIntegerBigInt(days ?? 0, "days");
|
||||
const hoursValue = toIntegerBigInt(hours ?? 0, "hours");
|
||||
const minutesValue = toIntegerBigInt(minutes ?? 0, "minutes");
|
||||
const secondsValue = toIntegerBigInt(seconds ?? 0, "seconds");
|
||||
const millisecondsValue = toIntegerBigInt(milliseconds ?? 0, "milliseconds");
|
||||
const microsecondsValue = toNumber(microseconds ?? 0, "microseconds");
|
||||
const nanosecondsValue = toNumber(nanoseconds ?? 0, "nanoseconds");
|
||||
|
||||
const created = exports.temporalz_duration_init(
|
||||
yearsValue,
|
||||
monthsValue,
|
||||
weeksValue,
|
||||
daysValue,
|
||||
hoursValue,
|
||||
minutesValue,
|
||||
secondsValue,
|
||||
millisecondsValue,
|
||||
microsecondsValue,
|
||||
nanosecondsValue
|
||||
);
|
||||
this._handle = requireHandle(created);
|
||||
}
|
||||
|
||||
static _fromHandle(handle) {
|
||||
return new Duration(durationHandleToken, handle);
|
||||
}
|
||||
|
||||
static from(value) {
|
||||
if (value instanceof Duration) return value;
|
||||
if (typeof value === "string") {
|
||||
const text = allocString(value);
|
||||
const handle = exports.temporalz_duration_from_utf8(text.ptr, text.len);
|
||||
exports.temporalz_free(text.ptr, text.len);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new TypeError("Duration.from expects a string or object");
|
||||
}
|
||||
|
||||
const fields = [
|
||||
"years",
|
||||
"months",
|
||||
"weeks",
|
||||
"days",
|
||||
"hours",
|
||||
"minutes",
|
||||
"seconds",
|
||||
"milliseconds",
|
||||
"microseconds",
|
||||
"nanoseconds",
|
||||
];
|
||||
|
||||
let mask = 0;
|
||||
const values = [0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0, 0];
|
||||
|
||||
fields.forEach((field, index) => {
|
||||
if (value[field] !== undefined) {
|
||||
mask |= 1 << index;
|
||||
if (field === "microseconds" || field === "nanoseconds") {
|
||||
values[index] = toNumber(value[field], field);
|
||||
} else {
|
||||
values[index] = toIntegerBigInt(value[field], field);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handle = exports.temporalz_duration_from_parts(
|
||||
mask,
|
||||
values[0],
|
||||
values[1],
|
||||
values[2],
|
||||
values[3],
|
||||
values[4],
|
||||
values[5],
|
||||
values[6],
|
||||
values[7],
|
||||
values[8],
|
||||
values[9]
|
||||
);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
static compare(a, b, options) {
|
||||
const left = Duration.from(a);
|
||||
const right = Duration.from(b);
|
||||
return Duration.compareWithOptions(left, right, options);
|
||||
}
|
||||
|
||||
static compareWithOptions(left, right, options) {
|
||||
if (options !== undefined && (options === null || typeof options !== "object")) {
|
||||
throw new TypeError("options must be an object");
|
||||
}
|
||||
const relativeTo = toRelativeTo(options);
|
||||
if (!relativeTo && (hasYearMonthWeek(left) || hasYearMonthWeek(right))) {
|
||||
throw new RangeError("relativeTo is required for calendar units");
|
||||
}
|
||||
if (relativeTo) {
|
||||
return Number(
|
||||
exports.temporalz_duration_compare_plain_date(
|
||||
left._handle,
|
||||
right._handle,
|
||||
relativeTo._handle
|
||||
)
|
||||
);
|
||||
}
|
||||
return Number(exports.temporalz_duration_compare(left._handle, right._handle));
|
||||
}
|
||||
|
||||
add(other) {
|
||||
const rhs = Duration.from(other);
|
||||
const handle = exports.temporalz_duration_add(this._handle, rhs._handle);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
subtract(other) {
|
||||
const rhs = Duration.from(other);
|
||||
const handle = exports.temporalz_duration_subtract(this._handle, rhs._handle);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
abs() {
|
||||
const handle = exports.temporalz_duration_abs(this._handle);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
negated() {
|
||||
const handle = exports.temporalz_duration_negated(this._handle);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
round(options = {}) {
|
||||
if (options === undefined) options = {};
|
||||
if (options === null || typeof options !== "object") {
|
||||
throw new TypeError("round options must be an object");
|
||||
}
|
||||
const relativeTo = toRelativeTo(options);
|
||||
if (!relativeTo && hasCalendarUnits(this)) {
|
||||
throw new RangeError("relativeTo is required for calendar units");
|
||||
}
|
||||
const smallestUnit = toUnitCode(options.smallestUnit, "smallestUnit");
|
||||
const largestUnit = toUnitCode(options.largestUnit, "largestUnit");
|
||||
const roundingMode = toRoundingModeCode(options.roundingMode);
|
||||
|
||||
let roundingIncrement = 0;
|
||||
if (options.roundingIncrement !== undefined) {
|
||||
const inc = Number(options.roundingIncrement);
|
||||
if (!Number.isFinite(inc) || !Number.isInteger(inc) || inc <= 0) {
|
||||
throw new RangeError("Invalid roundingIncrement");
|
||||
}
|
||||
roundingIncrement = inc;
|
||||
}
|
||||
|
||||
const handle = relativeTo
|
||||
? exports.temporalz_duration_round_plain_date(
|
||||
this._handle,
|
||||
smallestUnit,
|
||||
largestUnit,
|
||||
roundingMode,
|
||||
roundingIncrement,
|
||||
relativeTo._handle
|
||||
)
|
||||
: exports.temporalz_duration_round(
|
||||
this._handle,
|
||||
smallestUnit,
|
||||
largestUnit,
|
||||
roundingMode,
|
||||
roundingIncrement
|
||||
);
|
||||
return Duration._fromHandle(requireHandle(handle));
|
||||
}
|
||||
|
||||
total(options) {
|
||||
if (!options || typeof options !== "object") {
|
||||
throw new TypeError("total options must be an object");
|
||||
}
|
||||
const relativeTo = toRelativeTo(options);
|
||||
if (!relativeTo && hasCalendarUnits(this)) {
|
||||
throw new RangeError("relativeTo is required for calendar units");
|
||||
}
|
||||
const unit = toUnitCode(options.unit, "unit");
|
||||
if (unit === 255) throw new RangeError("Invalid unit");
|
||||
const result = relativeTo
|
||||
? exports.temporalz_duration_total_plain_date(
|
||||
this._handle,
|
||||
unit,
|
||||
relativeTo._handle
|
||||
)
|
||||
: exports.temporalz_duration_total(this._handle, unit);
|
||||
if (!Number.isFinite(result)) throw lastError();
|
||||
return result;
|
||||
}
|
||||
|
||||
get sign() {
|
||||
return Number(exports.temporalz_duration_sign(this._handle));
|
||||
}
|
||||
|
||||
get blank() {
|
||||
return exports.temporalz_duration_blank(this._handle) === 1;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return takeString(exports.temporalz_duration_to_string(this._handle));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
toLocaleString() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
throw new TypeError("Cannot convert Temporal.Duration to a number");
|
||||
}
|
||||
|
||||
get years() {
|
||||
return Number(exports.temporalz_duration_years(this._handle));
|
||||
}
|
||||
|
||||
get months() {
|
||||
return Number(exports.temporalz_duration_months(this._handle));
|
||||
}
|
||||
|
||||
get weeks() {
|
||||
return Number(exports.temporalz_duration_weeks(this._handle));
|
||||
}
|
||||
|
||||
get days() {
|
||||
return Number(exports.temporalz_duration_days(this._handle));
|
||||
}
|
||||
|
||||
get hours() {
|
||||
return Number(exports.temporalz_duration_hours(this._handle));
|
||||
}
|
||||
|
||||
get minutes() {
|
||||
return Number(exports.temporalz_duration_minutes(this._handle));
|
||||
}
|
||||
|
||||
get seconds() {
|
||||
return Number(exports.temporalz_duration_seconds(this._handle));
|
||||
}
|
||||
|
||||
get milliseconds() {
|
||||
return Number(exports.temporalz_duration_milliseconds(this._handle));
|
||||
}
|
||||
|
||||
get microseconds() {
|
||||
return Number(exports.temporalz_duration_microseconds(this._handle));
|
||||
}
|
||||
|
||||
get nanoseconds() {
|
||||
return Number(exports.temporalz_duration_nanoseconds(this._handle));
|
||||
}
|
||||
}
|
||||
|
||||
function hasCalendarUnits(duration) {
|
||||
return (
|
||||
duration.years !== 0 ||
|
||||
duration.months !== 0 ||
|
||||
duration.weeks !== 0 ||
|
||||
duration.days !== 0
|
||||
);
|
||||
}
|
||||
|
||||
function hasYearMonthWeek(duration) {
|
||||
return duration.years !== 0 || duration.months !== 0 || duration.weeks !== 0;
|
||||
}
|
||||
|
||||
function toRelativeTo(options) {
|
||||
if (!options || options.relativeTo === undefined) return null;
|
||||
const rel = options.relativeTo;
|
||||
if (rel instanceof PlainDate) return rel;
|
||||
if (typeof rel === "string") return PlainDate.from(rel);
|
||||
if (rel && typeof rel === "object") return PlainDate.from(rel);
|
||||
throw new TypeError("Invalid relativeTo");
|
||||
}
|
||||
|
||||
const Temporal = {
|
||||
Instant,
|
||||
Duration,
|
||||
Now: {
|
||||
instant: unimplemented("Temporal.Now.instant"),
|
||||
plainDateISO: unimplemented("Temporal.Now.plainDateISO"),
|
||||
plainDateTimeISO: unimplemented("Temporal.Now.plainDateTimeISO"),
|
||||
plainTimeISO: unimplemented("Temporal.Now.plainTimeISO"),
|
||||
timeZoneId: unimplemented("Temporal.Now.timeZoneId"),
|
||||
zonedDateTimeISO: unimplemented("Temporal.Now.zonedDateTimeISO"),
|
||||
},
|
||||
PlainDate,
|
||||
PlainTime: unimplemented("Temporal.PlainTime"),
|
||||
PlainDateTime: unimplemented("Temporal.PlainDateTime"),
|
||||
PlainYearMonth: unimplemented("Temporal.PlainYearMonth"),
|
||||
PlainMonthDay: unimplemented("Temporal.PlainMonthDay"),
|
||||
ZonedDateTime: unimplemented("Temporal.ZonedDateTime"),
|
||||
};
|
||||
|
||||
globalThis.Temporal = Temporal;
|
||||
})();
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
test {
|
||||
_ = @import("duration.zig");
|
||||
}
|
||||
35
test/test262/runner.mjs
Normal file
35
test/test262/runner.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import runTest262 from "../temporal-test262-runner/index.mjs";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const wasmPath =
|
||||
process.env.TEMPORALZ_WASM ||
|
||||
path.resolve(__dirname, "../../zig-out/bin/temporalz.wasm");
|
||||
const wasmBytes = fs.readFileSync(wasmPath);
|
||||
|
||||
const polyfillPath = path.resolve(__dirname, "../test262/polyfill.js");
|
||||
let polyfillCode = fs.readFileSync(polyfillPath, "utf-8");
|
||||
|
||||
const bytesArray = JSON.stringify(Array.from(wasmBytes));
|
||||
// TextEncoder/TextDecoder need to be manually constructed and passed via context
|
||||
// Since we can't serialize them, we inject polyfill-compatible shim versions
|
||||
const injection = `globalThis.__TEMPORALZ_WASM_BYTES__ = new Uint8Array(${bytesArray});
|
||||
`;
|
||||
polyfillCode = injection + polyfillCode;
|
||||
|
||||
const tempPolyfillPath = path.resolve(__dirname, "../test262/polyfill-injected.js");
|
||||
fs.writeFileSync(tempPolyfillPath, polyfillCode);
|
||||
|
||||
const result = runTest262({
|
||||
test262Dir: "test/temporal-test262-runner/test262",
|
||||
polyfillCodeFile: tempPolyfillPath,
|
||||
testGlobs: process.argv.slice(2),
|
||||
});
|
||||
|
||||
fs.unlinkSync(tempPolyfillPath);
|
||||
|
||||
process.exit(result ? 0 : 1);
|
||||
Loading…
Add table
Reference in a new issue