fix: instant/duration complete tc39 alignment
This commit is contained in:
parent
7c4cb5a3ca
commit
6ff4ef1298
8 changed files with 245 additions and 70 deletions
221
src/Duration.zig
221
src/Duration.zig
|
|
@ -2,15 +2,35 @@ const std = @import("std");
|
||||||
const abi = @import("abi.zig");
|
const abi = @import("abi.zig");
|
||||||
const temporal = @import("temporal.zig");
|
const temporal = @import("temporal.zig");
|
||||||
|
|
||||||
|
const PlainDate = @import("PlainDate.zig");
|
||||||
|
const PlainDateTime = @import("PlainDateTime.zig");
|
||||||
|
const ZonedDateTime = @import("ZonedDateTime.zig");
|
||||||
|
|
||||||
const Duration = @This();
|
const Duration = @This();
|
||||||
|
|
||||||
pub const RoundingOptions = temporal.RoundingOptions;
|
|
||||||
pub const ToStringOptions = temporal.ToStringRoundingOptions;
|
pub const ToStringOptions = temporal.ToStringRoundingOptions;
|
||||||
pub const ToStringRoundingOptions = temporal.ToStringRoundingOptions;
|
pub const ToStringRoundingOptions = temporal.ToStringRoundingOptions;
|
||||||
pub const Unit = temporal.Unit;
|
pub const Unit = temporal.Unit;
|
||||||
pub const RoundingMode = temporal.RoundingMode;
|
pub const RoundingMode = temporal.RoundingMode;
|
||||||
pub const Sign = temporal.Sign;
|
pub const Sign = temporal.Sign;
|
||||||
|
|
||||||
|
pub const RoundingOptions = struct {
|
||||||
|
largest_unit: ?Unit = null,
|
||||||
|
smallest_unit: ?Unit = null,
|
||||||
|
rounding_mode: ?RoundingMode = null,
|
||||||
|
rounding_increment: ?u32 = null,
|
||||||
|
relative_to: ?RelativeTo = null,
|
||||||
|
|
||||||
|
pub fn toCApi(self: RoundingOptions) abi.c.RoundingOptions {
|
||||||
|
return .{
|
||||||
|
.largest_unit = abi.toUnitOption(temporal.toCUnit(self.largest_unit)),
|
||||||
|
.smallest_unit = abi.toUnitOption(temporal.toCUnit(self.smallest_unit)),
|
||||||
|
.rounding_mode = abi.toRoundingModeOption(temporal.toCRoundingMode(self.rounding_mode)),
|
||||||
|
.increment = abi.toOption(abi.c.OptionU32, self.rounding_increment),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Partial duration specification for creating Duration objects.
|
/// Partial duration specification for creating Duration objects.
|
||||||
/// This is a wrapper around the C API type to avoid exposing C types directly.
|
/// This is a wrapper around the C API type to avoid exposing C types directly.
|
||||||
pub const PartialDuration = struct {
|
pub const PartialDuration = struct {
|
||||||
|
|
@ -52,23 +72,30 @@ const ZonedDateTimeRef = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Relative-to context for duration operations.
|
/// Relative-to context for duration operations.
|
||||||
pub const RelativeTo = struct {
|
pub const RelativeTo = union(enum) {
|
||||||
plain_date: ?PlainDateRef = null,
|
plain_date: PlainDate,
|
||||||
zoned_date_time: ?ZonedDateTimeRef = null,
|
plain_date_time: PlainDateTime,
|
||||||
|
zoned_date_time: ZonedDateTime,
|
||||||
|
|
||||||
fn toCApi(self: RelativeTo) abi.c.RelativeTo {
|
fn toCApi(self: RelativeTo) abi.c.RelativeTo {
|
||||||
return .{
|
switch (self) {
|
||||||
.date = if (self.plain_date) |pd| pd._inner else null,
|
.plain_date => |pd| return .{ .date = pd._inner },
|
||||||
.zoned = if (self.zoned_date_time) |zdt| zdt._inner else null,
|
.zoned_date_time => |zdt| return .{ .zoned = zdt._inner },
|
||||||
};
|
.plain_date_time => |pdt| return .{ .date = (pdt.toPlainDate() catch unreachable)._inner },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Options for Duration.total() providing unit and relative-to context.
|
/// Options for Duration.total() providing unit and relative-to context.
|
||||||
pub const TotalOptions = struct {
|
pub const TotalOptions = struct {
|
||||||
unit: Unit,
|
unit: Unit,
|
||||||
relative_to: ?RelativeTo = null,
|
relative_to: ?RelativeTo = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const CompareOptions = struct {
|
||||||
|
relative_to: ?RelativeTo = null,
|
||||||
|
};
|
||||||
|
|
||||||
_inner: *abi.c.Duration,
|
_inner: *abi.c.Duration,
|
||||||
|
|
||||||
/// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds.
|
/// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds.
|
||||||
|
|
@ -99,18 +126,54 @@ pub fn init(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an ISO 8601 duration string (Temporal.Duration.from).
|
/// The Temporal.Duration.from() static method creates a new Temporal.Duration object from one of the following:
|
||||||
pub fn from(text: []const u8) !Duration {
|
/// - A Temporal.Duration instance, which creates a copy of the instance.
|
||||||
const view = abi.toDiplomatStringView(text);
|
/// - An ISO 8601 string representing a duration.
|
||||||
return wrapDuration(abi.c.temporal_rs_Duration_from_utf8(view));
|
/// - A @Temporal.Duration.PartialDuration struct containing at least one of the following properties:
|
||||||
|
/// - days
|
||||||
|
/// - hours
|
||||||
|
/// - microseconds
|
||||||
|
/// - milliseconds
|
||||||
|
/// - minutes
|
||||||
|
/// - months
|
||||||
|
/// - nanoseconds
|
||||||
|
/// - seconds
|
||||||
|
/// - weeks
|
||||||
|
/// - years
|
||||||
|
/// The resulting duration must not have mixed signs, so all of these properties must have the same sign (or zero). Missing properties are treated as zero.
|
||||||
|
pub fn from(info: anytype) !Duration {
|
||||||
|
const T = @TypeOf(info);
|
||||||
|
|
||||||
|
if (T == Duration) return info.clone();
|
||||||
|
if (T == PartialDuration) return fromPartialDuration(info);
|
||||||
|
|
||||||
|
// Handle string types (both literals and slices)
|
||||||
|
const type_info = @typeInfo(T);
|
||||||
|
switch (type_info) {
|
||||||
|
.pointer => {
|
||||||
|
const ptr = type_info.pointer;
|
||||||
|
const ChildType = switch (@typeInfo(ptr.child)) {
|
||||||
|
.array => |arr| arr.child,
|
||||||
|
else => ptr.child,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ChildType == u8) return fromUtf8(info);
|
||||||
|
if (ChildType == u16) return fromUtf16(info);
|
||||||
|
},
|
||||||
|
else => @compileError("from() expects a Duration, []const u8, or []const u16, or Temporal.Duration.PartialDuration"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an ISO 8601 UTF-16 duration string.
|
inline fn fromUtf16(text: []const u16) !Duration {
|
||||||
fn fromUtf16(text: []const u16) !Duration {
|
|
||||||
const view = abi.toDiplomatString16View(text);
|
const view = abi.toDiplomatString16View(text);
|
||||||
return wrapDuration(abi.c.temporal_rs_Duration_from_utf16(view));
|
return wrapDuration(abi.c.temporal_rs_Duration_from_utf16(view));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fn fromUtf8(text: []const u8) !Duration {
|
||||||
|
const view = abi.toDiplomatStringView(text);
|
||||||
|
return wrapDuration(abi.c.temporal_rs_Duration_from_utf8(view));
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a Duration from a partial duration (where some fields may be omitted).
|
/// Create a Duration from a partial duration (where some fields may be omitted).
|
||||||
fn fromPartialDuration(partial: PartialDuration) !Duration {
|
fn fromPartialDuration(partial: PartialDuration) !Duration {
|
||||||
return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(partial.toCApi()));
|
return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(partial.toCApi()));
|
||||||
|
|
@ -204,8 +267,9 @@ pub fn subtract(self: Duration, other: Duration) !Duration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Round the duration according to the specified options (Temporal.Duration.prototype.round).
|
/// Round the duration according to the specified options (Temporal.Duration.prototype.round).
|
||||||
pub fn round(self: Duration, options: RoundingOptions, relative_to: RelativeTo) !Duration {
|
pub fn round(self: Duration, options: RoundingOptions) !Duration {
|
||||||
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options.toCApi(), relative_to.toCApi()));
|
const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null };
|
||||||
|
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options.toCApi(), rel));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Round the duration with an explicit provider.
|
/// Round the duration with an explicit provider.
|
||||||
|
|
@ -214,8 +278,9 @@ fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: Rela
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare two durations (Temporal.Duration.compare).
|
/// Compare two durations (Temporal.Duration.compare).
|
||||||
pub fn compare(self: Duration, other: Duration, relative_to: RelativeTo) !i8 {
|
pub fn compare(self: Duration, other: Duration, options: CompareOptions) !i8 {
|
||||||
const res = abi.c.temporal_rs_Duration_compare(self._inner, other._inner, relative_to.toCApi());
|
const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null };
|
||||||
|
const res = abi.c.temporal_rs_Duration_compare(self._inner, other._inner, rel);
|
||||||
return try abi.extractResult(res);
|
return try abi.extractResult(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,32 +369,54 @@ test init {
|
||||||
}
|
}
|
||||||
|
|
||||||
test from {
|
test from {
|
||||||
// P1Y2M3DT4H5M6.789S = 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6.789 seconds
|
{ // P1Y2M3DT4H5M6.789S = 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6.789 seconds
|
||||||
const dur = try Duration.from("P1Y2M3DT4H5M6.789S");
|
const dur = try Duration.from("P1Y2M3DT4H5M6.789S");
|
||||||
defer dur.deinit();
|
defer dur.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(i64, 1), dur.years());
|
try std.testing.expectEqual(@as(i64, 1), dur.years());
|
||||||
try std.testing.expectEqual(@as(i64, 2), dur.months());
|
try std.testing.expectEqual(@as(i64, 2), dur.months());
|
||||||
try std.testing.expectEqual(@as(i64, 3), dur.days());
|
try std.testing.expectEqual(@as(i64, 3), dur.days());
|
||||||
try std.testing.expectEqual(@as(i64, 4), dur.hours());
|
try std.testing.expectEqual(@as(i64, 4), dur.hours());
|
||||||
try std.testing.expectEqual(@as(i64, 5), dur.minutes());
|
try std.testing.expectEqual(@as(i64, 5), dur.minutes());
|
||||||
try std.testing.expectEqual(@as(i64, 6), dur.seconds());
|
try std.testing.expectEqual(@as(i64, 6), dur.seconds());
|
||||||
|
|
||||||
// from-time
|
// from-time
|
||||||
// PT2H30M = 2 hours, 30 minutes
|
// PT2H30M = 2 hours, 30 minutes
|
||||||
const dur_time = try Duration.from("PT2H30M");
|
const dur_time = try Duration.from("PT2H30M");
|
||||||
defer dur_time.deinit();
|
defer dur_time.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(i64, 0), dur_time.years());
|
try std.testing.expectEqual(@as(i64, 0), dur_time.years());
|
||||||
try std.testing.expectEqual(@as(i64, 2), dur_time.hours());
|
try std.testing.expectEqual(@as(i64, 2), dur_time.hours());
|
||||||
try std.testing.expectEqual(@as(i64, 30), dur_time.minutes());
|
try std.testing.expectEqual(@as(i64, 30), dur_time.minutes());
|
||||||
|
|
||||||
// from-negative
|
// from-negative
|
||||||
const dur_negative = try Duration.from("-P1D");
|
const dur_negative = try Duration.from("-P1D");
|
||||||
defer dur_negative.deinit();
|
defer dur_negative.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(i64, -1), dur_negative.days());
|
try std.testing.expectEqual(@as(i64, -1), dur_negative.days());
|
||||||
try std.testing.expectEqual(Sign.negative, dur_negative.sign());
|
try std.testing.expectEqual(Sign.negative, dur_negative.sign());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const partial = PartialDuration{
|
||||||
|
.hours = 3,
|
||||||
|
.minutes = 45,
|
||||||
|
};
|
||||||
|
|
||||||
|
const dur = try Duration.from(partial);
|
||||||
|
defer dur.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i64, 3), dur.hours());
|
||||||
|
try std.testing.expectEqual(@as(i64, 45), dur.minutes());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const dur1 = try Duration.from("PT1H");
|
||||||
|
defer dur1.deinit();
|
||||||
|
const dur2 = try Duration.from(dur1);
|
||||||
|
defer dur2.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i64, 1), dur2.hours());
|
||||||
|
try std.testing.expectEqual(@as(i64, 0), dur2.minutes());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test blank {
|
test blank {
|
||||||
|
|
@ -353,6 +440,17 @@ test add {
|
||||||
try std.testing.expectEqual(@as(i64, 30), result.minutes());
|
try std.testing.expectEqual(@as(i64, 30), result.minutes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test compare {
|
||||||
|
const dur1 = try Duration.from("PT1H");
|
||||||
|
defer dur1.deinit();
|
||||||
|
const dur2 = try Duration.from("PT30M");
|
||||||
|
defer dur2.deinit();
|
||||||
|
|
||||||
|
const result = try dur1.compare(dur2, .{});
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i8, 1), result);
|
||||||
|
}
|
||||||
|
|
||||||
test subtract {
|
test subtract {
|
||||||
const dur1 = try Duration.from("PT2H");
|
const dur1 = try Duration.from("PT2H");
|
||||||
defer dur1.deinit();
|
defer dur1.deinit();
|
||||||
|
|
@ -432,6 +530,51 @@ test clone {
|
||||||
try std.testing.expectEqual(dur.hours(), cloned.hours());
|
try std.testing.expectEqual(dur.hours(), cloned.hours());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test round {
|
||||||
|
{
|
||||||
|
const dur = try Duration.from("PT1H30M");
|
||||||
|
defer dur.deinit();
|
||||||
|
|
||||||
|
const rounded = try dur.round(.{
|
||||||
|
.smallest_unit = .hour,
|
||||||
|
.relative_to = .{
|
||||||
|
.plain_date_time = try PlainDateTime.init(2024, 1, 1, 12, 0, 0, 0, 0, 0),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defer rounded.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
|
||||||
|
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const dur = try Duration.from("PT1H30M");
|
||||||
|
defer dur.deinit();
|
||||||
|
|
||||||
|
const rounded = try dur.round(.{
|
||||||
|
.smallest_unit = .hour,
|
||||||
|
.relative_to = .{
|
||||||
|
.plain_date = try PlainDate.init(2024, 1, 1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defer rounded.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
|
||||||
|
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const dur = try Duration.from("PT1H30M");
|
||||||
|
defer dur.deinit();
|
||||||
|
|
||||||
|
const rounded = try dur.round(.{ .smallest_unit = .hour });
|
||||||
|
defer rounded.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
|
||||||
|
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test total {
|
test total {
|
||||||
{
|
{
|
||||||
const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
|
const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,6 @@ pub const ToStringOptions = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
_inner: *abi.c.Instant,
|
_inner: *abi.c.Instant,
|
||||||
epoch_milliseconds: i64,
|
|
||||||
epoch_nanoseconds: i128,
|
|
||||||
|
|
||||||
/// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds).
|
/// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds).
|
||||||
pub fn init(epoch_ns: i128) !Instant {
|
pub fn init(epoch_ns: i128) !Instant {
|
||||||
|
|
@ -193,7 +191,15 @@ fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *cons
|
||||||
/// Clone the underlying instant.
|
/// Clone the underlying instant.
|
||||||
fn clone(self: Instant) Instant {
|
fn clone(self: Instant) Instant {
|
||||||
const ptr = abi.c.temporal_rs_Instant_clone(self._inner) orelse unreachable;
|
const ptr = abi.c.temporal_rs_Instant_clone(self._inner) orelse unreachable;
|
||||||
return .{ ._inner = ptr, .epoch_milliseconds = abi.c.temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(ptr)) };
|
return .{ ._inner = ptr };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn epochMilliseconds(self: Instant) i64 {
|
||||||
|
return abi.c.temporal_rs_Instant_epoch_milliseconds(self._inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn epochNanoseconds(self: Instant) i128 {
|
||||||
|
return abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(self._inner));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Instant) void {
|
pub fn deinit(self: Instant) void {
|
||||||
|
|
@ -206,8 +212,6 @@ fn wrapInstant(res: anytype) !Instant {
|
||||||
const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic;
|
const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic;
|
||||||
return .{
|
return .{
|
||||||
._inner = ptr,
|
._inner = ptr,
|
||||||
.epoch_milliseconds = abi.c.temporal_rs_Instant_epoch_milliseconds(ptr),
|
|
||||||
.epoch_nanoseconds = abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(ptr)),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,7 +262,7 @@ test init {
|
||||||
const inst = try Instant.init(epoch_ns);
|
const inst = try Instant.init(epoch_ns);
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(epoch_ns, inst.epoch_nanoseconds);
|
try std.testing.expectEqual(epoch_ns, inst.epochNanoseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
test fromEpochNanoseconds {
|
test fromEpochNanoseconds {
|
||||||
|
|
@ -271,8 +275,8 @@ test fromEpochNanoseconds {
|
||||||
const min_inst = try Instant.fromEpochNanoseconds(min_ns);
|
const min_inst = try Instant.fromEpochNanoseconds(min_ns);
|
||||||
defer min_inst.deinit();
|
defer min_inst.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(max_ns, max_inst.epoch_nanoseconds);
|
try std.testing.expectEqual(max_ns, max_inst.epochNanoseconds());
|
||||||
try std.testing.expectEqual(min_ns, min_inst.epoch_nanoseconds);
|
try std.testing.expectEqual(min_ns, min_inst.epochNanoseconds());
|
||||||
|
|
||||||
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(max_ns + 1));
|
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(max_ns + 1));
|
||||||
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(min_ns - 1));
|
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(min_ns - 1));
|
||||||
|
|
@ -282,13 +286,13 @@ test from {
|
||||||
// Test parsing from UTF-8 string
|
// Test parsing from UTF-8 string
|
||||||
const inst = try Instant.from("2024-03-15T14:30:45.123Z");
|
const inst = try Instant.from("2024-03-15T14:30:45.123Z");
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epoch_milliseconds);
|
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epochMilliseconds());
|
||||||
|
|
||||||
// Test creating from another Instant
|
// Test creating from another Instant
|
||||||
const inst2 = try Instant.from(inst);
|
const inst2 = try Instant.from(inst);
|
||||||
defer inst2.deinit();
|
defer inst2.deinit();
|
||||||
try std.testing.expectEqual(inst.epoch_milliseconds, inst2.epoch_milliseconds);
|
try std.testing.expectEqual(inst.epochMilliseconds(), inst2.epochMilliseconds());
|
||||||
try std.testing.expectEqual(inst.epoch_nanoseconds, inst2.epoch_nanoseconds);
|
try std.testing.expectEqual(inst.epochNanoseconds(), inst2.epochNanoseconds());
|
||||||
try std.testing.expect(Instant.equals(inst, inst2));
|
try std.testing.expect(Instant.equals(inst, inst2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +305,7 @@ test fromUtf16 {
|
||||||
const inst = try Instant.fromUtf16(utf16);
|
const inst = try Instant.fromUtf16(utf16);
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epoch_milliseconds);
|
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epochMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
test subtract {
|
test subtract {
|
||||||
|
|
@ -313,11 +317,11 @@ test subtract {
|
||||||
|
|
||||||
const added = try base.add(&dur);
|
const added = try base.add(&dur);
|
||||||
defer added.deinit();
|
defer added.deinit();
|
||||||
try std.testing.expectEqual(@as(i64, 5_400_000), added.epoch_milliseconds);
|
try std.testing.expectEqual(@as(i64, 5_400_000), added.epochMilliseconds());
|
||||||
|
|
||||||
const subbed = try added.subtract(&dur);
|
const subbed = try added.subtract(&dur);
|
||||||
defer subbed.deinit();
|
defer subbed.deinit();
|
||||||
try std.testing.expectEqual(@as(i64, 0), subbed.epoch_milliseconds);
|
try std.testing.expectEqual(@as(i64, 0), subbed.epochMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
test compare {
|
test compare {
|
||||||
|
|
@ -410,7 +414,7 @@ test round {
|
||||||
const rounded = try inst.round(opts);
|
const rounded = try inst.round(opts);
|
||||||
defer rounded.deinit();
|
defer rounded.deinit();
|
||||||
|
|
||||||
const ns = rounded.epoch_nanoseconds;
|
const ns = rounded.epochNanoseconds();
|
||||||
try std.testing.expectEqual(@as(i128, 1_609_459_245_000_000_000), ns);
|
try std.testing.expectEqual(@as(i128, 1_609_459_245_000_000_000), ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,7 +425,7 @@ test clone {
|
||||||
const cloned = inst.clone();
|
const cloned = inst.clone();
|
||||||
defer cloned.deinit();
|
defer cloned.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(inst.epoch_milliseconds, cloned.epoch_milliseconds);
|
try std.testing.expectEqual(inst.epochMilliseconds(), cloned.epochMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
test toString {
|
test toString {
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ fn currentParts() CurrentParts {
|
||||||
test instant {
|
test instant {
|
||||||
const inst = try instant();
|
const inst = try instant();
|
||||||
defer inst.deinit();
|
defer inst.deinit();
|
||||||
try std.testing.expect(inst.epoch_nanoseconds > 0);
|
try std.testing.expect(inst.epochNanoseconds() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
test plainDateISO {
|
test plainDateISO {
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,10 @@ pub const ToZonedDateTimeOptions = struct {
|
||||||
_inner: *abi.c.PlainDate,
|
_inner: *abi.c.PlainDate,
|
||||||
|
|
||||||
pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate {
|
pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate {
|
||||||
return initWithCalendar(year_val, month_val, day_val, "iso8601");
|
return calInit(year_val, month_val, day_val, "iso8601");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initWithCalendar(year_val: i32, month_val: u8, day_val: u8, calendar: []const u8) !PlainDate {
|
pub fn calInit(year_val: i32, month_val: u8, day_val: u8, calendar: []const u8) !PlainDate {
|
||||||
const cal_view = abi.toDiplomatStringView(calendar);
|
const cal_view = abi.toDiplomatStringView(calendar);
|
||||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||||
const cal_kind = try abi.extractResult(cal_result);
|
const cal_kind = try abi.extractResult(cal_result);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ pub const WithOptions = struct {
|
||||||
microsecond: ?u16 = null,
|
microsecond: ?u16 = null,
|
||||||
nanosecond: ?u16 = null,
|
nanosecond: ?u16 = null,
|
||||||
};
|
};
|
||||||
|
const PartialDateTime = struct {};
|
||||||
|
|
||||||
// Constructor - creates a PlainDateTime with all parameters
|
// Constructor - creates a PlainDateTime with all parameters
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
|
@ -75,7 +76,7 @@ pub fn init(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initWithCalendar(
|
pub fn calInit(
|
||||||
year_val: i32,
|
year_val: i32,
|
||||||
month_val: u8,
|
month_val: u8,
|
||||||
day_val: u8,
|
day_val: u8,
|
||||||
|
|
@ -105,8 +106,37 @@ pub fn initWithCalendar(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse from string
|
// Parse from string
|
||||||
pub fn from(s: []const u8) !PlainDateTime {
|
pub fn from(info: anytype) !PlainDateTime {
|
||||||
return fromUtf8(s);
|
const T = @TypeOf(info);
|
||||||
|
|
||||||
|
if (T == PlainDateTime) return info.clone();
|
||||||
|
if (T == PartialDateTime) return fromPartial(info);
|
||||||
|
|
||||||
|
// Handle string types (both literals and slices)
|
||||||
|
const type_info = @typeInfo(T);
|
||||||
|
switch (type_info) {
|
||||||
|
.pointer => {
|
||||||
|
const ptr = type_info.pointer;
|
||||||
|
const ChildType = switch (@typeInfo(ptr.child)) {
|
||||||
|
.array => |arr| arr.child,
|
||||||
|
else => ptr.child,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ChildType == u8) return fromUtf8(info);
|
||||||
|
if (ChildType == u16) return fromUtf16(info);
|
||||||
|
},
|
||||||
|
else => @compileError("from() expects a Duration, []const u8, or []const u16, or Temporal.Duration.PartialDuration"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fromPartial(info: PartialDateTime) !PlainDateTime {
|
||||||
|
_ = info;
|
||||||
|
return error.NotImplemented;
|
||||||
|
// return abi.c.temporal_rs_PlainDateTime_from_partial(.{
|
||||||
|
// .date = .{
|
||||||
|
// .
|
||||||
|
// }
|
||||||
|
// }, overflow: struct_ArithmeticOverflow_option)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fromUtf8(utf8: []const u8) !PlainDateTime {
|
fn fromUtf8(utf8: []const u8) !PlainDateTime {
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,7 @@ pub fn subtract(self: ZonedDateTime, duration: Duration) !ZonedDateTime {
|
||||||
/// Convert to Instant
|
/// Convert to Instant
|
||||||
pub fn toInstant(self: ZonedDateTime) !Instant {
|
pub fn toInstant(self: ZonedDateTime) !Instant {
|
||||||
const instant_ptr = abi.c.temporal_rs_ZonedDateTime_to_instant(self._inner) orelse return error.TemporalError;
|
const instant_ptr = abi.c.temporal_rs_ZonedDateTime_to_instant(self._inner) orelse return error.TemporalError;
|
||||||
const epoch_ms = abi.c.temporal_rs_Instant_epoch_milliseconds(instant_ptr);
|
return .{ ._inner = instant_ptr };
|
||||||
const epoch_ns_parts = abi.c.temporal_rs_Instant_epoch_nanoseconds(instant_ptr);
|
|
||||||
const epoch_ns = abi.fromI128Nanoseconds(epoch_ns_parts);
|
|
||||||
return .{ ._inner = instant_ptr, .epoch_milliseconds = epoch_ms, .epoch_nanoseconds = epoch_ns };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to JSON string (ISO 8601 format)
|
/// Convert to JSON string (ISO 8601 format)
|
||||||
|
|
@ -502,7 +499,7 @@ test toInstant {
|
||||||
const instant = try zdt.toInstant();
|
const instant = try zdt.toInstant();
|
||||||
defer instant.deinit();
|
defer instant.deinit();
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(i64, 1609459200000), instant.epoch_milliseconds);
|
try std.testing.expectEqual(@as(i64, 1609459200000), instant.epochMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
test toPlainDate {
|
test toPlainDate {
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ test Duration {
|
||||||
"ToStringRoundingOptions",
|
"ToStringRoundingOptions",
|
||||||
"Sign",
|
"Sign",
|
||||||
"TotalOptions",
|
"TotalOptions",
|
||||||
|
"CompareOptions",
|
||||||
};
|
};
|
||||||
|
|
||||||
try assertDecls(Duration, checks);
|
try assertDecls(Duration, checks);
|
||||||
|
|
@ -142,7 +143,7 @@ test PlainDate {
|
||||||
const checks = .{
|
const checks = .{
|
||||||
// Constructor
|
// Constructor
|
||||||
"init",
|
"init",
|
||||||
"initWithCalendar",
|
"calInit",
|
||||||
|
|
||||||
// Static methods
|
// Static methods
|
||||||
"compare",
|
"compare",
|
||||||
|
|
@ -200,7 +201,7 @@ test PlainDateTime {
|
||||||
const checks = .{
|
const checks = .{
|
||||||
// Constructor
|
// Constructor
|
||||||
"init", // Temporal.PlainDateTime()
|
"init", // Temporal.PlainDateTime()
|
||||||
"initWithCalendar",
|
"calInit",
|
||||||
|
|
||||||
// Static methods
|
// Static methods
|
||||||
"compare",
|
"compare",
|
||||||
|
|
|
||||||
|
|
@ -159,10 +159,10 @@ pub const ToStringRoundingOptions = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn toCUnit(opt: ?Unit) ?c.Unit {
|
pub fn toCUnit(opt: ?Unit) ?c.Unit {
|
||||||
return if (opt) |u| @as(c.Unit, @intCast(u.toCApi())) else null;
|
return if (opt) |u| @as(c.Unit, @intCast(u.toCApi())) else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toCRoundingMode(opt: ?RoundingMode) ?c.RoundingMode {
|
pub fn toCRoundingMode(opt: ?RoundingMode) ?c.RoundingMode {
|
||||||
return if (opt) |m| @as(c.RoundingMode, @intCast(m.toCApi())) else null;
|
return if (opt) |m| @as(c.RoundingMode, @intCast(m.toCApi())) else null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue