temporalz/src/Duration.zig
2026-07-15 17:23:58 +06:00

720 lines
26 KiB
Zig

const std = @import("std");
const abi = @import("abi.zig");
const t = @import("temporal.zig");
const PlainDate = @import("PlainDate.zig");
const PlainDateTime = @import("PlainDateTime.zig");
const ZonedDateTime = @import("ZonedDateTime.zig");
/// The `Temporal.Duration` object represents a difference between two time points, which can be used in date/time arithmetic.
/// It is fundamentally represented as a combination of years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds values.
///
/// See: [Temporal.Duration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration)
const Duration = @This();
_inner: *abi.c.Duration,
/// Options for controlling stringification of a Duration.
pub const ToStringOptions = t.ToStringRoundingOptions;
/// Options for controlling stringification of a Duration (alias).
pub const ToStringRoundingOptions = t.ToStringRoundingOptions;
/// Units supported by Temporal.Duration (years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
pub const Unit = t.Unit;
/// Rounding modes for Duration operations.
pub const RoundingMode = t.RoundingMode;
/// The sign of a Duration: positive, zero, or negative.
pub const Sign = t.Sign;
/// Options for rounding a Duration.
pub const RoundingOptions = struct {
/// The largest unit to round to.
largest_unit: ?Unit = null,
/// The smallest unit to round to.
smallest_unit: ?Unit = null,
/// The rounding mode to use.
rounding_mode: ?RoundingMode = null,
/// The increment to round to.
rounding_increment: ?u32 = null,
/// The relative-to context (PlainDate, PlainDateTime, or ZonedDateTime).
relative_to: ?RelativeTo = null,
};
/// Partial duration specification for creating Duration objects.
/// This is a wrapper around the C API type to avoid exposing C types directly.
///
/// See: [Temporal.Duration.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/from)
pub const PartialDuration = struct {
years: ?i64 = null,
months: ?i64 = null,
weeks: ?i64 = null,
days: ?i64 = null,
hours: ?i64 = null,
minutes: ?i64 = null,
seconds: ?i64 = null,
milliseconds: ?i64 = null,
microseconds: ?f64 = null,
nanoseconds: ?f64 = null,
};
/// Relative-to context for duration operations, used for balancing and calendar-aware math.
///
/// See: [Temporal.Duration#calendar_durations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration#calendar_durations)
pub const RelativeTo = union(enum) {
plain_date: PlainDate,
plain_date_time: PlainDateTime,
zoned_date_time: ZonedDateTime,
};
/// Options for Duration.total() providing unit and relative-to context.
pub const TotalOptions = struct {
/// The unit to total in.
unit: Unit,
/// The relative-to context (PlainDate, PlainDateTime, or ZonedDateTime).
relative_to: ?RelativeTo = null,
};
/// Options for Duration.compare() providing relative-to context.
pub const CompareOptions = struct {
/// The relative-to context (PlainDate, PlainDateTime, or ZonedDateTime).
relative_to: ?RelativeTo = null,
};
/// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds.
/// Equivalent to `Temporal.Duration()` constructor.
///
/// See: [Temporal.Duration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/Duration)
pub fn init(
years_val: i64,
months_val: i64,
weeks_val: i64,
days_val: i64,
hours_val: i64,
minutes_val: i64,
seconds_val: i64,
milliseconds_val: i64,
microseconds_val: f64,
nanoseconds_val: f64,
) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_try_new(
years_val,
months_val,
weeks_val,
days_val,
hours_val,
minutes_val,
seconds_val,
milliseconds_val,
microseconds_val,
nanoseconds_val,
));
}
/// The Temporal.Duration.from() static method creates a new Temporal.Duration object from one of the following:
/// - A Temporal.Duration instance, which creates a copy of the instance.
/// - An ISO 8601 string representing a duration.
/// - 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"),
}
}
inline fn fromUtf16(text: []const u16) !Duration {
const view = abi.toDiplomatString16View(text);
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).
fn fromPartialDuration(partial: PartialDuration) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(abi.to.partialdur(partial)));
}
/// Check if the time portion of the duration is within valid ranges.
fn isTimeWithinRange(self: Duration) bool {
return abi.c.temporal_rs_Duration_is_time_within_range(self._inner);
}
/// Returns the number of years in the duration.
///
/// See: [Temporal.Duration.years](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/years)
pub fn years(self: Duration) i64 {
return abi.c.temporal_rs_Duration_years(self._inner);
}
/// Returns the number of months in the duration.
///
/// See: [Temporal.Duration.months](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/months)
pub fn months(self: Duration) i64 {
return abi.c.temporal_rs_Duration_months(self._inner);
}
/// Returns the number of weeks in the duration.
///
/// See: [Temporal.Duration.weeks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/weeks)
pub fn weeks(self: Duration) i64 {
return abi.c.temporal_rs_Duration_weeks(self._inner);
}
/// Returns the number of days in the duration.
///
/// See: [Temporal.Duration.days](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/days)
pub fn days(self: Duration) i64 {
return abi.c.temporal_rs_Duration_days(self._inner);
}
/// Returns the number of hours in the duration.
///
/// See: [Temporal.Duration.hours](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/hours)
pub fn hours(self: Duration) i64 {
return abi.c.temporal_rs_Duration_hours(self._inner);
}
/// Returns the number of minutes in the duration.
///
/// See: [Temporal.Duration.minutes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/minutes)
pub fn minutes(self: Duration) i64 {
return abi.c.temporal_rs_Duration_minutes(self._inner);
}
/// Returns the number of seconds in the duration.
///
/// See: [Temporal.Duration.seconds](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/seconds)
pub fn seconds(self: Duration) i64 {
return abi.c.temporal_rs_Duration_seconds(self._inner);
}
/// Returns the number of milliseconds in the duration.
///
/// See: [Temporal.Duration.milliseconds](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/milliseconds)
pub fn milliseconds(self: Duration) i64 {
return abi.c.temporal_rs_Duration_milliseconds(self._inner);
}
/// Returns the number of microseconds in the duration.
///
/// See: [Temporal.Duration.microseconds](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/microseconds)
pub fn microseconds(self: Duration) f64 {
return abi.c.temporal_rs_Duration_microseconds(self._inner);
}
/// Returns the number of nanoseconds in the duration.
///
/// See: [Temporal.Duration.nanoseconds](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/nanoseconds)
pub fn nanoseconds(self: Duration) f64 {
return abi.c.temporal_rs_Duration_nanoseconds(self._inner);
}
/// Returns the sign of the duration: positive (1), zero (0), or negative (-1).
///
/// See: [Temporal.Duration.sign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/sign)
pub fn sign(self: Duration) Sign {
return abi.from.sign(abi.c.temporal_rs_Duration_sign(self._inner));
}
/// Returns true if the duration is zero (all fields are zero), false otherwise.
///
/// See: [Temporal.Duration.blank](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/blank)
pub fn blank(self: Duration) bool {
return abi.c.temporal_rs_Duration_is_zero(self._inner);
}
/// Returns a new Duration with the absolute value (all components positive).
///
/// See: [Temporal.Duration.abs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/abs)
pub fn abs(self: Duration) Duration {
const ptr: *abi.c.Duration = abi.c.temporal_rs_Duration_abs(self._inner) orelse unreachable;
return .{ ._inner = ptr };
}
/// Returns a new Duration with all components negated (sign reversed).
///
/// See: [Temporal.Duration.negated](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/negated)
pub fn negated(self: Duration) Duration {
const ptr: *abi.c.Duration = abi.c.temporal_rs_Duration_negated(self._inner) orelse unreachable;
return .{ ._inner = ptr };
}
/// Returns a new Duration with the sum of this duration and another (balanced as needed).
///
/// See: [Temporal.Duration.add](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/add)
pub fn add(self: Duration, other: Duration) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_add(self._inner, other._inner));
}
/// Returns a new Duration with the difference between this duration and another (balanced as needed).
///
/// See: [Temporal.Duration.subtract](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/subtract)
pub fn subtract(self: Duration, other: Duration) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_subtract(self._inner, other._inner));
}
/// Returns a new Duration rounded to the given smallest/largest unit and/or balanced.
///
/// See: [Temporal.Duration.round](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/round)
pub fn round(self: Duration, options: RoundingOptions) !Duration {
const rel = if (options.relative_to) |r| abi.to.durRelativeTo(r) else abi.c.RelativeTo{ .date = null, .zoned = null };
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, abi.to.durRoundingOpts(options), rel));
}
/// Round the duration with an explicit provider.
fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: RelativeTo, provider: *const abi.c.Provider) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_round_with_provider(self._inner, options.toCApi(), relative_to, provider));
}
/// Compares two durations, returning -1, 0, or 1 if this duration is shorter, equal, or longer than the other.
///
/// See: [Temporal.Duration.compare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/compare)
pub fn compare(self: Duration, other: Duration, options: CompareOptions) !i8 {
const rel = if (options.relative_to) |r| abi.to.durRelativeTo(r) 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);
}
/// Compare two durations with an explicit provider.
fn compareWithProvider(self: Duration, other: Duration, relative_to: RelativeTo, provider: *const abi.c.Provider) !i8 {
const res = abi.c.temporal_rs_Duration_compare_with_provider(self._inner, other._inner, relative_to.toCApi(), provider);
return try abi.extractResult(res);
}
/// Returns the total value of the duration in the specified unit.
///
/// See: [Temporal.Duration.total](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/total)
pub fn total(self: Duration, options: TotalOptions) !f64 {
const rel = if (options.relative_to) |r| abi.to.durRelativeTo(r) else abi.c.RelativeTo{ .date = null, .zoned = null };
const res = abi.c.temporal_rs_Duration_total(self._inner, abi.to.unit(options.unit).?, rel);
return try abi.extractResult(res);
}
/// Get the total value of the duration with an explicit provider.
fn totalWithProvider(self: Duration, options: TotalOptions, provider: *const abi.c.Provider) !f64 {
const rel = if (options.relative_to) |r| abi.to.durRelativeTo(r) else abi.c.RelativeTo{ .date = null, .zoned = null };
const res = abi.c.temporal_rs_Duration_total_with_provider(self._inner, options.unit.toCApi(), rel, provider);
return try abi.extractResult(res);
}
/// Returns a string representing this duration in the ISO 8601 format.
///
/// See: [Temporal.Duration.toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toString)
pub fn toString(self: Duration, allocator: std.mem.Allocator, options: ToStringRoundingOptions) ![]u8 {
var write = abi.DiplomatWrite.init(allocator);
defer write.deinit();
const res = abi.c.temporal_rs_Duration_to_string(self._inner, abi.to.strRoundingOpts(options), &write.inner);
try handleVoidResult(res);
return try write.toOwnedSlice();
}
/// Returns a string representing this duration in the ISO 8601 format (same as toString). Intended for JSON serialization.
///
/// See: [Temporal.Duration.toJSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toJSON)
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.
///
/// See: [Temporal.Duration.toLocaleString](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;
_ = allocator;
return error.TemporalNotImplemented;
}
/// Clone the underlying duration.
fn clone(self: Duration) Duration {
const ptr: *abi.c.Duration = abi.c.temporal_rs_Duration_clone(self._inner) orelse unreachable;
return .{ ._inner = ptr };
}
/// Deinitialize the Duration, freeing underlying resources.
pub fn deinit(self: Duration) void {
abi.c.temporal_rs_Duration_destroy(self._inner);
}
inline fn handleVoidResult(res: anytype) !void {
_ = try abi.extractResult(res);
}
fn wrapDuration(res: anytype) !Duration {
const ptr = try abi.extractResult(res);
if (ptr == null) return abi.TemporalError.RangeError;
return .{ ._inner = ptr.? };
}
const OptionU8 = abi.c.OptionU8;
const OptionU32 = abi.c.OptionU32;
const OptionI64 = abi.c.OptionI64;
const OptionF64 = abi.c.OptionF64;
const Precision = abi.c.Precision;
const Unit_option = abi.c.Unit_option;
const RoundingMode_option = abi.c.RoundingMode_option;
test init {
// Create a simple duration: 1 hour, 30 minutes
const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 1), dur.hours());
try std.testing.expectEqual(@as(i64, 30), dur.minutes());
try std.testing.expectEqual(@as(i64, 0), dur.seconds());
}
test from {
{ // P1Y2M3DT4H5M6.789S = 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6.789 seconds
const dur = try Duration.from("P1Y2M3DT4H5M6.789S");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 1), dur.years());
try std.testing.expectEqual(@as(i64, 2), dur.months());
try std.testing.expectEqual(@as(i64, 3), dur.days());
try std.testing.expectEqual(@as(i64, 4), dur.hours());
try std.testing.expectEqual(@as(i64, 5), dur.minutes());
try std.testing.expectEqual(@as(i64, 6), dur.seconds());
// from-time
// PT2H30M = 2 hours, 30 minutes
const dur_time = try Duration.from("PT2H30M");
defer dur_time.deinit();
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, 30), dur_time.minutes());
// from-negative
const dur_negative = try Duration.from("-P1D");
defer dur_negative.deinit();
try std.testing.expectEqual(@as(i64, -1), dur_negative.days());
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 {
const dur = try Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
defer dur.deinit();
try std.testing.expect(dur.blank());
try std.testing.expectEqual(Sign.zero, dur.sign());
}
test add {
const dur1 = try Duration.from("PT1H");
defer dur1.deinit();
const dur2 = try Duration.from("PT30M");
defer dur2.deinit();
const result = try dur1.add(dur2);
defer result.deinit();
try std.testing.expectEqual(@as(i64, 1), result.hours());
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 {
const dur1 = try Duration.from("PT2H");
defer dur1.deinit();
const dur2 = try Duration.from("PT30M");
defer dur2.deinit();
const result = try dur1.subtract(dur2);
defer result.deinit();
try std.testing.expectEqual(@as(i64, 1), result.hours());
try std.testing.expectEqual(@as(i64, 30), result.minutes());
}
test abs {
const dur = try Duration.from("-PT1H30M");
defer dur.deinit();
const abs_dur = dur.abs();
defer abs_dur.deinit();
try std.testing.expectEqual(@as(i64, 1), abs_dur.hours());
try std.testing.expectEqual(@as(i64, 30), abs_dur.minutes());
try std.testing.expectEqual(Sign.positive, abs_dur.sign());
}
test negated {
const dur = try Duration.from("PT1H");
defer dur.deinit();
const neg = dur.negated();
defer neg.deinit();
try std.testing.expectEqual(@as(i64, -1), neg.hours());
try std.testing.expectEqual(Sign.negative, neg.sign());
}
test toString {
const dur = try Duration.from("P1Y2M3DT4H5M6S");
defer dur.deinit();
const allocator = std.testing.allocator;
const str = try dur.toString(allocator, .{});
defer allocator.free(str);
// Should output ISO 8601 duration format
try std.testing.expect(str.len > 0);
}
test fromPartialDuration {
const partial = PartialDuration{
.hours = 3,
.minutes = 45,
};
const dur = try Duration.fromPartialDuration(partial);
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 3), dur.hours());
try std.testing.expectEqual(@as(i64, 45), dur.minutes());
try std.testing.expectEqual(@as(i64, 0), dur.days());
}
test isTimeWithinRange {
const dur = try Duration.from("PT23H59M59S");
defer dur.deinit();
try std.testing.expect(dur.isTimeWithinRange());
}
test clone {
const dur = try Duration.from("PT1H");
defer dur.deinit();
const cloned = dur.clone();
defer cloned.deinit();
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 {
{
const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);
defer dur.deinit();
const ttl = try dur.total(.{ .unit = .hour });
try std.testing.expectEqual(1.5, ttl);
}
{
const dur = try Duration.from("PT4H5M6S");
defer dur.deinit();
const ttl = try dur.total(.{ .unit = .hour });
try std.testing.expectEqual(4.085, ttl);
}
{
// Calendar units require relative_to; the C API will return RangeError.
const dur = try Duration.from("P1Y");
defer dur.deinit();
try std.testing.expectError(error.RangeError, dur.total(.{ .unit = .day }));
}
}
test years {
const dur = try Duration.from("P5Y");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 5), dur.years());
}
test months {
const dur = try Duration.from("P2M");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 2), dur.months());
}
test weeks {
const dur = try Duration.from("P3W");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 3), dur.weeks());
}
test days {
const dur = try Duration.from("P7D");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 7), dur.days());
}
test hours {
const dur = try Duration.from("PT5H");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 5), dur.hours());
}
test minutes {
const dur = try Duration.from("PT30M");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 30), dur.minutes());
}
test seconds {
const dur = try Duration.from("PT45S");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 45), dur.seconds());
}
test milliseconds {
const dur = try Duration.from("PT0.500S");
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 500), dur.milliseconds());
}
test microseconds {
const dur = try Duration.from("PT0.000500S");
defer dur.deinit();
const us = dur.microseconds();
try std.testing.expect(us > 499 and us < 501);
}
test nanoseconds {
const dur = try Duration.from("PT0.000000500S");
defer dur.deinit();
const ns = dur.nanoseconds();
try std.testing.expect(ns > 499 and ns < 501);
}
test sign {
const pos = try Duration.from("P1Y");
defer pos.deinit();
try std.testing.expectEqual(Sign.positive, pos.sign());
const neg = try Duration.from("-P1Y");
defer neg.deinit();
try std.testing.expectEqual(Sign.negative, neg.sign());
const zero = try Duration.init(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
defer zero.deinit();
try std.testing.expectEqual(Sign.zero, zero.sign());
}
test toJSON {
const dur = try Duration.from("P1Y2M3DT4H5M6.789S");
defer dur.deinit();
const json_str = try dur.toJSON(std.testing.allocator);
defer std.testing.allocator.free(json_str);
try std.testing.expect(json_str.len > 0);
try std.testing.expect(std.mem.indexOf(u8, json_str, "P") != null);
}
test toLocaleString {
const dur = try Duration.from("P1Y2M3DT4H5M6S");
defer dur.deinit();
try std.testing.expectError(error.TemporalNotImplemented, dur.toLocaleString(std.testing.allocator));
}