720 lines
26 KiB
Zig
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));
|
|
}
|