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
|
!lib/*/temporal_capi.lib
|
||||||
|
|
||||||
tmp
|
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 {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
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 --- //
|
// --- Zig Module: temporalz --- //
|
||||||
const mod = b.addModule("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);
|
b.installArtifact(exe);
|
||||||
|
|
||||||
// --- Steps: Run --- //
|
// --- Steps: Run --- //
|
||||||
{
|
{
|
||||||
const run_step = b.step("run", "Run the app");
|
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" });
|
const run_cmd = b.addSystemCommand(&.{ "node", "example/src/main.mjs" });
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
run_step.dependOn(&run_cmd.step);
|
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);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
if (b.args) |args| run_cmd.addArgs(args);
|
if (b.args) |args| run_cmd.addArgs(args);
|
||||||
run_step.dependOn(&run_cmd.step);
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,22 +170,10 @@ pub fn build(b: *std.Build) !void {
|
||||||
// --- Steps: test-262 --- //
|
// --- Steps: test-262 --- //
|
||||||
{
|
{
|
||||||
const test262_step = b.step("test262", "Run test-262 tests");
|
const test262_step = b.step("test262", "Run test-262 tests");
|
||||||
const test262_tests = b.addTest(.{
|
const run_cmd = b.addSystemCommand(&.{ "node", "test/test262/runner.mjs" });
|
||||||
.root_module = b.createModule(.{
|
if (b.args) |args| run_cmd.addArgs(args);
|
||||||
.root_source_file = b.path("test/test262/root.zig"),
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
.target = target,
|
test262_step.dependOn(&run_cmd.step);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Steps: Build all platforms --- //
|
// --- Steps: Build all platforms --- //
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,837 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const program = @import("root.zig");
|
const program = @import("root.zig");
|
||||||
|
const Temporal = @import("temporalz");
|
||||||
|
|
||||||
export fn _start() void {
|
export fn _start() void {
|
||||||
const allocator = std.heap.wasm_allocator;
|
const allocator = std.heap.wasm_allocator;
|
||||||
program.run(allocator, null) catch {};
|
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;
|
extern fn console(ptr: [*]u8, len: u32) void;
|
||||||
|
|
||||||
fn logFn(comptime _: anytype, comptime _: anytype, comptime format: []const u8, args: anytype) 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