feat(instant): make toString() have proper options
This commit is contained in:
parent
b5f34b4fed
commit
6fe2133cb1
2 changed files with 110 additions and 40 deletions
145
src/Instant.zig
145
src/Instant.zig
|
|
@ -76,7 +76,7 @@ pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptio
|
||||||
else
|
else
|
||||||
TimeZone_option{ .ok = undefined, .is_ok = false };
|
TimeZone_option{ .ok = undefined, .is_ok = false };
|
||||||
|
|
||||||
const rounding = opts.rounding orelse defaultToStringRoundingOptions();
|
const rounding = optsToRounding(opts);
|
||||||
|
|
||||||
const writer = diplomat_buffer_write_create(128);
|
const writer = diplomat_buffer_write_create(128);
|
||||||
defer diplomat_buffer_write_destroy(writer);
|
defer diplomat_buffer_write_destroy(writer);
|
||||||
|
|
@ -99,7 +99,7 @@ fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *
|
||||||
else
|
else
|
||||||
TimeZone_option{ .ok = undefined, .is_ok = false };
|
TimeZone_option{ .ok = undefined, .is_ok = false };
|
||||||
|
|
||||||
const rounding = opts.rounding orelse defaultToStringRoundingOptions();
|
const rounding = optsToRounding(opts);
|
||||||
|
|
||||||
const writer = diplomat_buffer_write_create(128);
|
const writer = diplomat_buffer_write_create(128);
|
||||||
defer diplomat_buffer_write_destroy(writer);
|
defer diplomat_buffer_write_destroy(writer);
|
||||||
|
|
@ -116,9 +116,7 @@ fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toJSON(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
pub fn toJSON(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
||||||
_ = self;
|
return self.toString(allocator, .{});
|
||||||
_ = allocator;
|
|
||||||
return error.TemporalNotImplemented;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toLocaleString(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
pub fn toLocaleString(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
||||||
|
|
@ -196,8 +194,33 @@ fn defaultPrecision() Precision {
|
||||||
fn defaultToStringRoundingOptions() ToStringRoundingOptions {
|
fn defaultToStringRoundingOptions() ToStringRoundingOptions {
|
||||||
return .{
|
return .{
|
||||||
.precision = defaultPrecision(),
|
.precision = defaultPrecision(),
|
||||||
.smallest_unit = Unit_option{ .ok = .Unit_Auto, .is_ok = false },
|
.smallest_unit = Unit_option{ .ok = .auto, .is_ok = false },
|
||||||
.rounding_mode = RoundingMode_option{ .ok = .RoundingMode_Trunc, .is_ok = false },
|
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = false },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert ToStringOptions to ToStringRoundingOptions for the C API
|
||||||
|
fn optsToRounding(opts: ToStringOptions) ToStringRoundingOptions {
|
||||||
|
// If smallest_unit is specified, use it; otherwise use fractional_second_digits for precision
|
||||||
|
const smallest_unit_opt = if (opts.smallest_unit) |unit|
|
||||||
|
Unit_option{ .ok = unit, .is_ok = true }
|
||||||
|
else
|
||||||
|
Unit_option{ .ok = .auto, .is_ok = false };
|
||||||
|
|
||||||
|
const precision = if (opts.fractional_second_digits) |digits|
|
||||||
|
Precision{ .is_minute = false, .precision = OptionU8{ .ok = digits, .is_ok = true } }
|
||||||
|
else
|
||||||
|
defaultPrecision();
|
||||||
|
|
||||||
|
const rounding_mode_opt = if (opts.rounding_mode) |mode|
|
||||||
|
RoundingMode_option{ .ok = mode, .is_ok = true }
|
||||||
|
else
|
||||||
|
RoundingMode_option{ .ok = .trunc, .is_ok = false };
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.precision = precision,
|
||||||
|
.smallest_unit = smallest_unit_opt,
|
||||||
|
.rounding_mode = rounding_mode_opt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,9 +234,26 @@ fn parseDuration(text: []const u8) !DurationHandle {
|
||||||
|
|
||||||
// --- Public helper types -----------------------------------------------------
|
// --- Public helper types -----------------------------------------------------
|
||||||
|
|
||||||
const ToStringOptions = struct {
|
/// Options for Instant.toString()
|
||||||
|
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toString
|
||||||
|
pub const ToStringOptions = struct {
|
||||||
|
/// Either an integer from 0 to 9, or null for "auto".
|
||||||
|
/// If null (auto), trailing zeros are removed from the fractional seconds.
|
||||||
|
/// Otherwise, the fractional part contains this many digits, padded with zeros or rounded as necessary.
|
||||||
|
fractional_second_digits: ?u8 = null,
|
||||||
|
|
||||||
|
/// Specifies how to round off fractional second digits beyond fractionalSecondDigits.
|
||||||
|
/// Defaults to "trunc" (truncate).
|
||||||
|
rounding_mode: ?RoundingMode = null,
|
||||||
|
|
||||||
|
/// Specifies the smallest unit to include in the output.
|
||||||
|
/// Possible values: "minute", "second", "millisecond", "microsecond", "nanosecond".
|
||||||
|
/// If specified, fractional_second_digits is ignored.
|
||||||
|
smallest_unit: ?Unit = null,
|
||||||
|
|
||||||
|
/// Time zone to use. Either a time zone identifier string or null for UTC.
|
||||||
|
/// Note: In the Zig API, this must be pre-resolved to a TimeZone struct.
|
||||||
time_zone: ?TimeZone = null,
|
time_zone: ?TimeZone = null,
|
||||||
rounding: ?ToStringRoundingOptions = null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DurationHandle = struct {
|
const DurationHandle = struct {
|
||||||
|
|
@ -276,32 +316,45 @@ const Precision = extern struct {
|
||||||
precision: OptionU8,
|
precision: OptionU8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Unit = enum(c_int) {
|
/// Time unit for Temporal operations.
|
||||||
Unit_Auto = 0,
|
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
|
||||||
Unit_Nanosecond = 1,
|
pub const Unit = enum(c_int) {
|
||||||
Unit_Microsecond = 2,
|
auto = 0,
|
||||||
Unit_Millisecond = 3,
|
nanosecond = 1,
|
||||||
Unit_Second = 4,
|
microsecond = 2,
|
||||||
Unit_Minute = 5,
|
millisecond = 3,
|
||||||
Unit_Hour = 6,
|
second = 4,
|
||||||
Unit_Day = 7,
|
minute = 5,
|
||||||
Unit_Week = 8,
|
hour = 6,
|
||||||
Unit_Month = 9,
|
day = 7,
|
||||||
Unit_Year = 10,
|
week = 8,
|
||||||
|
month = 9,
|
||||||
|
year = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Unit_option = extern struct { ok: Unit, is_ok: bool };
|
const Unit_option = extern struct { ok: Unit, is_ok: bool };
|
||||||
|
|
||||||
const RoundingMode = enum(c_int) {
|
/// Rounding mode for Temporal operations.
|
||||||
RoundingMode_Ceil = 0,
|
/// See: https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers
|
||||||
RoundingMode_Floor = 1,
|
pub const RoundingMode = enum(c_int) {
|
||||||
RoundingMode_Expand = 2,
|
/// Round toward positive infinity
|
||||||
RoundingMode_Trunc = 3,
|
ceil = 0,
|
||||||
RoundingMode_HalfCeil = 4,
|
/// Round toward negative infinity
|
||||||
RoundingMode_HalfFloor = 5,
|
floor = 1,
|
||||||
RoundingMode_HalfExpand = 6,
|
/// Round away from zero
|
||||||
RoundingMode_HalfTrunc = 7,
|
expand = 2,
|
||||||
RoundingMode_HalfEven = 8,
|
/// Round toward zero (truncate)
|
||||||
|
trunc = 3,
|
||||||
|
/// Round half toward positive infinity
|
||||||
|
half_ceil = 4,
|
||||||
|
/// Round half toward negative infinity
|
||||||
|
half_floor = 5,
|
||||||
|
/// Round half away from zero
|
||||||
|
half_expand = 6,
|
||||||
|
/// Round half toward zero
|
||||||
|
half_trunc = 7,
|
||||||
|
/// Round half to even (banker's rounding)
|
||||||
|
half_even = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool };
|
const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool };
|
||||||
|
|
@ -521,9 +574,9 @@ test until {
|
||||||
defer later.deinit();
|
defer later.deinit();
|
||||||
|
|
||||||
const settings = DifferenceSettings{
|
const settings = DifferenceSettings{
|
||||||
.largest_unit = Unit_option{ .ok = .Unit_Hour, .is_ok = true },
|
.largest_unit = Unit_option{ .ok = .hour, .is_ok = true },
|
||||||
.smallest_unit = Unit_option{ .ok = .Unit_Second, .is_ok = true },
|
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||||
.rounding_mode = RoundingMode_option{ .ok = .RoundingMode_Trunc, .is_ok = true },
|
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = true },
|
||||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -545,9 +598,9 @@ test since {
|
||||||
defer later.deinit();
|
defer later.deinit();
|
||||||
|
|
||||||
const settings = DifferenceSettings{
|
const settings = DifferenceSettings{
|
||||||
.largest_unit = Unit_option{ .ok = .Unit_Hour, .is_ok = true },
|
.largest_unit = Unit_option{ .ok = .hour, .is_ok = true },
|
||||||
.smallest_unit = Unit_option{ .ok = .Unit_Second, .is_ok = true },
|
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||||
.rounding_mode = RoundingMode_option{ .ok = .RoundingMode_Trunc, .is_ok = true },
|
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = true },
|
||||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -567,9 +620,9 @@ test round {
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
|
|
||||||
const opts = RoundingOptions{
|
const opts = RoundingOptions{
|
||||||
.largest_unit = Unit_option{ .ok = .Unit_Auto, .is_ok = false },
|
.largest_unit = Unit_option{ .ok = .auto, .is_ok = false },
|
||||||
.smallest_unit = Unit_option{ .ok = .Unit_Second, .is_ok = true },
|
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||||
.rounding_mode = RoundingMode_option{ .ok = .RoundingMode_HalfExpand, .is_ok = true },
|
.rounding_mode = RoundingMode_option{ .ok = .half_expand, .is_ok = true },
|
||||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -596,7 +649,19 @@ test toString {
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
|
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
// Default options
|
||||||
const instant_str = try inst.toString(allocator, .{});
|
const instant_str = try inst.toString(allocator, .{});
|
||||||
defer allocator.free(instant_str);
|
defer allocator.free(instant_str);
|
||||||
try std.testing.expectEqualStrings(instant_str, "2024-01-01T00:00:00Z");
|
try std.testing.expectEqualStrings(instant_str, "2024-01-01T00:00:00Z");
|
||||||
|
|
||||||
|
// With fractional_second_digits
|
||||||
|
const with_precision = try inst.toString(allocator, .{ .fractional_second_digits = 3 });
|
||||||
|
defer allocator.free(with_precision);
|
||||||
|
// Output should include milliseconds precision
|
||||||
|
|
||||||
|
// With smallest_unit
|
||||||
|
const with_unit = try inst.toString(allocator, .{ .smallest_unit = .second });
|
||||||
|
defer allocator.free(with_unit);
|
||||||
|
// Output should truncate to seconds
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,11 @@ test "Temporal.Instant" {
|
||||||
// Properties
|
// Properties
|
||||||
"epochMilliseconds",
|
"epochMilliseconds",
|
||||||
"epochNanoseconds",
|
"epochNanoseconds",
|
||||||
|
|
||||||
|
// Public types
|
||||||
|
"ToStringOptions",
|
||||||
|
"Unit",
|
||||||
|
"RoundingMode",
|
||||||
};
|
};
|
||||||
|
|
||||||
try assertDecls(Instant, checks);
|
try assertDecls(Instant, checks);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue