feat: extend bindings

This commit is contained in:
Nurul Huda (Apon) 2026-05-27 12:41:08 +06:00
parent ba5b777de4
commit f302c55135
No known key found for this signature in database
GPG key ID: 5D3F1DE2855A2F79
6 changed files with 154 additions and 15 deletions

View file

@ -479,7 +479,7 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
\\ - blank: {}
\\ - microseconds: {d}
\\ - toJSON(): {s}
\\ - toLocaleString implemented: {}
\\ - toLocaleString supported: {}
\\
\\
, .{
@ -522,16 +522,17 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
try instant_zdt.toString(allocator, .{}),
});
const now_zdt_supported = Temporal.Now.zonedDateTimeISO() != error.TemporalNotImplemented;
const now_zdt = try Temporal.Now.zonedDateTimeISO();
defer now_zdt.deinit();
std.log.info(
\\Now Coverage
\\ - timeZoneId(): {s}
\\ - zonedDateTimeISO implemented: {}
\\ - zonedDateTimeISO(): {s}
\\
\\
, .{
Temporal.Now.timeZoneId(),
now_zdt_supported,
try now_zdt.toString(allocator, .{}),
});
const date_cal = try Temporal.PlainDate.calInit(2024, 2, 2, "iso8601");
@ -558,7 +559,7 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
\\ - toPlainYearMonth: {s}
\\ - toZonedDateTime: {s}
\\ - toJSON(): {s}
\\ - toLocaleString implemented: {}
\\ - toLocaleString supported: {}
\\ - valueOf supported: {}
\\ - with year/month/day: {s}
\\
@ -736,7 +737,8 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
const zdt_plain_datetime = try zdt.toPlainDateTime();
const zdt_plain_time = try zdt.toPlainTime();
const zdt_valueof_supported = zdt.valueOf() != error.ValueOfNotSupported;
const zdt_with_supported = zdt.with(allocator, .{ .year = 2025 }) != error.TemporalNoteImplemented;
const zdt_with_coverage = try zdt.with(allocator, .{ .year = 2025 });
defer zdt_with_coverage.deinit();
std.log.info(
\\ZonedDateTime Coverage
\\ - init: {s}
@ -786,7 +788,7 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
\\ - offset/offsetNanoseconds: {s}/{}
\\ - weekOfYear/yearOfWeek: {?}/{?}
\\ - valueOf supported: {}
\\ - with implemented: {}
\\ - with year=2025: {s}
\\
\\
, .{
@ -804,7 +806,7 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
zdt.weekOfYear(),
zdt.yearOfWeek(),
zdt_valueof_supported,
zdt_with_supported,
try zdt_with_coverage.toString(allocator, .{}),
});
}

View file

@ -320,7 +320,8 @@ pub fn toJSON(self: Duration, allocator: std.mem.Allocator) ![]u8 {
return self.toString(allocator, .{});
}
/// Returns a string with a language-sensitive representation of this duration. Not implemented.
/// Returns a string with a language-sensitive representation of this duration.
/// Not implemented.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toLocaleString
pub fn toLocaleString(self: Duration, allocator: std.mem.Allocator) ![]u8 {
_ = self;

View file

@ -87,7 +87,8 @@ pub fn timeZoneId() []const u8 {
///
/// See: [MDN Temporal.Now.zonedDateTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/zonedDateTimeISO)
pub fn zonedDateTimeISO() !ZonedDateTime {
return error.TemporalNotImplemented;
const time_zone = try ZonedDateTime.TimeZone.init(timeZoneId());
return ZonedDateTime.fromEpochNanoseconds(currentEpochNanoseconds(), time_zone);
}
const CurrentParts = struct {
@ -132,6 +133,18 @@ fn currentParts(io: std.Io) CurrentParts {
};
}
fn currentEpochNanoseconds() i128 {
var timespec: std.posix.timespec = undefined;
switch (std.posix.errno(std.posix.system.clock_gettime(.REALTIME, &timespec))) {
.SUCCESS => {},
else => return 0,
}
const seconds: i128 = @intCast(timespec.sec);
const nanos: i128 = @intCast(timespec.nsec);
return seconds * std.time.ns_per_s + nanos;
}
// ---------- Tests ---------------------
test instant {
const io = std.testing.io;
@ -168,5 +181,11 @@ test timeZoneId {
try std.testing.expectEqualStrings("UTC", tz);
}
test zonedDateTimeISO {
try std.testing.expectError(error.TemporalNotImplemented, zonedDateTimeISO());
const zdt = try zonedDateTimeISO();
defer zdt.deinit();
try std.testing.expect(zdt.epochNanoseconds() > 0);
const tz = try zdt.timeZoneId(std.testing.allocator);
defer std.testing.allocator.free(tz);
try std.testing.expectEqualStrings("UTC", tz);
}

View file

@ -99,6 +99,24 @@ pub const ToStringOptions = struct {
time_zone_name: DisplayTimeZone = .auto,
};
/// Options for `with()` method.
pub const WithOptions = struct {
year: ?i32 = null,
month: ?u8 = null,
month_code: ?[]const u8 = null,
day: ?u8 = null,
era: ?[]const u8 = null,
era_year: ?i32 = null,
hour: ?u8 = null,
minute: ?u8 = null,
second: ?u8 = null,
millisecond: ?u16 = null,
microsecond: ?u16 = null,
nanosecond: ?u16 = null,
offset: ?[]const u8 = null,
time_zone: ?TimeZone = null,
};
/// Helper function to wrap a C API result into a ZonedDateTime
fn wrapZonedDateTime(result: anytype) !ZonedDateTime {
const ptr = (try abi.extractResult(result)) orelse return abi.TemporalError.Generic;
@ -273,9 +291,21 @@ pub fn valueOf(_: ZonedDateTime) !void {
/// See [MDN Temporal.ZonedDateTime.prototype.with()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/with)
pub fn with(self: ZonedDateTime, allocator: std.mem.Allocator, fields: anytype) !ZonedDateTime {
_ = allocator;
_ = fields;
_ = self;
return error.TemporalNoteImplemented; // Need PartialZonedDateTime mapping
const partial = abi.toPartialZonedDateTime(fields);
const disambiguation = abi.toOption(abi.c.Disambiguation_option, abi.to.toDisambiguation(Disambiguation.compatible));
const offset_option = abi.toOption(abi.c.OffsetDisambiguation_option, abi.to.toOffsetDisambiguation(OffsetDisambiguation.prefer_offset));
const overflow = abi.c.ArithmeticOverflow_option{
.is_ok = true,
.unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain },
};
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_with(
self._inner,
partial,
disambiguation,
offset_option,
overflow,
));
}
/// Returns a new ZonedDateTime interpreted in the new calendar system.
@ -760,7 +790,14 @@ test valueOf {
test with {
const zdt = try from("2021-01-01T12:00:00+00:00[UTC]", null, .compatible, .reject);
defer zdt.deinit();
try std.testing.expectError(error.TemporalNoteImplemented, zdt.with(std.testing.allocator, .{}));
const result = try zdt.with(std.testing.allocator, .{ .year = 2022, .month = 2, .day = 3, .hour = 4 });
defer result.deinit();
try std.testing.expectEqual(@as(i32, 2022), result.year());
try std.testing.expectEqual(@as(u8, 2), result.month());
try std.testing.expectEqual(@as(u8, 3), result.day());
try std.testing.expectEqual(@as(u8, 4), result.hour());
}
test withCalendar {

View file

@ -90,6 +90,85 @@ pub fn toTimeZoneOption(maybe_value: ?c.TimeZone) c.TimeZone_option {
return toOption(c.TimeZone_option, maybe_value);
}
pub fn toPartialZonedDateTime(fields: anytype) c.PartialZonedDateTime {
return .{
.date = .{
.year = toI32OptionField(fields, "year"),
.month = toU8OptionField(fields, "month"),
.month_code = toStringViewField(fields, "month_code"),
.day = toU8OptionField(fields, "day"),
.era = toStringViewField(fields, "era"),
.era_year = toI32OptionField(fields, "era_year"),
.calendar = c.AnyCalendarKind_Iso,
},
.time = .{
.hour = toU8OptionField(fields, "hour"),
.minute = toU8OptionField(fields, "minute"),
.second = toU8OptionField(fields, "second"),
.millisecond = toU16OptionField(fields, "millisecond"),
.microsecond = toU16OptionField(fields, "microsecond"),
.nanosecond = toU16OptionField(fields, "nanosecond"),
},
.offset = toOptionStringViewField(fields, "offset"),
.timezone = toTimeZoneOptionField(fields, "time_zone"),
};
}
fn toI32OptionField(fields: anytype, comptime name: []const u8) c.OptionI32 {
return toOption(c.OptionI32, intField(i32, fields, name));
}
fn toU8OptionField(fields: anytype, comptime name: []const u8) c.OptionU8 {
return toOption(c.OptionU8, intField(u8, fields, name));
}
fn toU16OptionField(fields: anytype, comptime name: []const u8) c.OptionU16 {
return toOption(c.OptionU16, intField(u16, fields, name));
}
fn intField(comptime Int: type, fields: anytype, comptime name: []const u8) ?Int {
if (!@hasField(@TypeOf(fields), name)) return null;
const value = @field(fields, name);
return switch (@typeInfo(@TypeOf(value))) {
.optional => if (value) |payload| @as(Int, @intCast(payload)) else null,
else => @as(Int, @intCast(value)),
};
}
fn toStringViewField(fields: anytype, comptime name: []const u8) c.DiplomatStringView {
if (stringField(fields, name)) |value| return toDiplomatStringView(value);
return .{ .data = null, .len = 0 };
}
fn toOptionStringViewField(fields: anytype, comptime name: []const u8) c.OptionStringView {
if (stringField(fields, name)) |value| {
return .{ .is_ok = true, .unnamed_0 = .{ .ok = toDiplomatStringView(value) } };
}
return .{ .is_ok = false };
}
fn stringField(fields: anytype, comptime name: []const u8) ?[]const u8 {
if (!@hasField(@TypeOf(fields), name)) return null;
const value = @field(fields, name);
return switch (@typeInfo(@TypeOf(value))) {
.optional => value,
else => value,
};
}
fn toTimeZoneOptionField(fields: anytype, comptime name: []const u8) c.TimeZone_option {
if (!@hasField(@TypeOf(fields), name)) return .{ .is_ok = false };
const value = @field(fields, name);
const maybe_time_zone = switch (@typeInfo(@TypeOf(value))) {
.optional => if (value) |payload| payload._inner else null,
else => value._inner,
};
return toOption(c.TimeZone_option, maybe_time_zone);
}
/// Convert a Zig `?Unit` to a Rust `Option<Unit>`.
pub fn toUnitOption(maybe_value: ?c.Unit) c.Unit_option {
return toOption(c.Unit_option, maybe_value);

View file

@ -502,6 +502,7 @@ test ZonedDateTime {
"DisplayOffset",
"DisplayTimeZone",
"ToStringOptions",
"WithOptions",
};
try assertDecls(ZonedDateTime, checks);