850 lines
30 KiB
Zig
850 lines
30 KiB
Zig
const std = @import("std");
|
|
const abi = @import("abi.zig");
|
|
const t = @import("temporal.zig");
|
|
|
|
const PlainDate = @import("PlainDate.zig");
|
|
const PlainTime = @import("PlainTime.zig");
|
|
const ZonedDateTime = @import("ZonedDateTime.zig");
|
|
const Duration = @import("Duration.zig");
|
|
|
|
/// # Temporal.PlainDateTime
|
|
///
|
|
/// The `Temporal.PlainDateTime` object represents a calendar date and wall-clock time, but no time zone or offset.
|
|
///
|
|
/// - [MDN Temporal.PlainDateTime](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime)
|
|
/// The `Temporal.PlainDateTime` object represents a calendar date and wall-clock time, but no time zone or offset.
|
|
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime
|
|
const PlainDateTime = @This();
|
|
|
|
/// Internal pointer to the underlying C PlainDateTime object.
|
|
_inner: *abi.c.PlainDateTime,
|
|
|
|
/// Units used for date and time calculations.
|
|
pub const Unit = t.Unit;
|
|
/// Rounding modes for date/time operations.
|
|
pub const RoundingMode = t.RoundingMode;
|
|
/// Sign type for durations.
|
|
pub const Sign = t.Sign;
|
|
/// Settings for difference calculations between date-times.
|
|
pub const DifferenceSettings = t.DifferenceSettings;
|
|
/// Options for rounding operations.
|
|
pub const RoundOptions = t.RoundingOptions;
|
|
|
|
/// Controls how the calendar is displayed in string output.
|
|
pub const CalendarDisplay = enum {
|
|
/// Display calendar only if not ISO.
|
|
auto,
|
|
/// Always display calendar.
|
|
always,
|
|
/// Never display calendar.
|
|
never,
|
|
/// Display calendar as a critical flag.
|
|
critical,
|
|
};
|
|
|
|
/// Options for `toString()` method.
|
|
pub const ToStringOptions = struct {
|
|
/// Number of fractional second digits to include.
|
|
fractional_second_digits: ?u8 = null,
|
|
/// The smallest unit to include in the output.
|
|
smallest_unit: ?Unit = null,
|
|
/// Rounding mode for the output.
|
|
rounding_mode: ?RoundingMode = null,
|
|
/// How to display the calendar in the output.
|
|
calendar_display: ?CalendarDisplay = null,
|
|
};
|
|
|
|
/// Options for `toZonedDateTime()` method.
|
|
pub const ToZonedDateTimeOptions = struct {
|
|
/// The time zone identifier (IANA string).
|
|
timeZone: []const u8,
|
|
/// Disambiguation option for ambiguous times.
|
|
disambiguation: ?[]const u8 = null,
|
|
};
|
|
|
|
/// Options for `with()` method.
|
|
pub const WithOptions = struct {
|
|
/// Year value to override.
|
|
year: ?i32 = null,
|
|
/// Month value to override.
|
|
month: ?u8 = null,
|
|
/// Day value to override.
|
|
day: ?u8 = null,
|
|
/// Hour value to override.
|
|
hour: ?u8 = null,
|
|
/// Minute value to override.
|
|
minute: ?u8 = null,
|
|
/// Second value to override.
|
|
second: ?u8 = null,
|
|
/// Millisecond value to override.
|
|
millisecond: ?u16 = null,
|
|
/// Microsecond value to override.
|
|
microsecond: ?u16 = null,
|
|
/// Nanosecond value to override.
|
|
nanosecond: ?u16 = null,
|
|
};
|
|
|
|
const FromOptions = struct {
|
|
overflow: ?Overflow = null,
|
|
};
|
|
|
|
const Overflow = enum {
|
|
constrain,
|
|
reject,
|
|
};
|
|
|
|
const PartialDateTime = struct {
|
|
// Date fields (recognized by PlainDate.from)
|
|
calendar: ?[]const u8 = null,
|
|
era: ?[]const u8 = null,
|
|
eraYear: ?u32 = null,
|
|
year: ?i32 = null,
|
|
month: ?u8 = null,
|
|
monthCode: ?[]const u8 = null,
|
|
day: ?u8 = null,
|
|
// Time fields (recognized by PlainTime.from)
|
|
hour: ?u8 = null,
|
|
minute: ?u8 = null,
|
|
second: ?u8 = null,
|
|
millisecond: ?u16 = null,
|
|
microsecond: ?u16 = null,
|
|
nanosecond: ?u16 = null,
|
|
};
|
|
|
|
/// Creates a new PlainDateTime with all date and time components.
|
|
pub fn init(
|
|
year_val: i32,
|
|
month_val: u8,
|
|
day_val: u8,
|
|
hour_val: u8,
|
|
minute_val: u8,
|
|
second_val: u8,
|
|
millisecond_val: u16,
|
|
microsecond_val: u16,
|
|
nanosecond_val: u16,
|
|
) !PlainDateTime {
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_try_new(
|
|
year_val,
|
|
month_val,
|
|
day_val,
|
|
hour_val,
|
|
minute_val,
|
|
second_val,
|
|
millisecond_val,
|
|
microsecond_val,
|
|
nanosecond_val,
|
|
abi.c.AnyCalendarKind_Iso,
|
|
));
|
|
}
|
|
|
|
/// Creates a new PlainDateTime with a specific calendar.
|
|
/// - `calendar`: Calendar identifier string (e.g., "iso8601").
|
|
pub fn calInit(
|
|
year_val: i32,
|
|
month_val: u8,
|
|
day_val: u8,
|
|
hour_val: u8,
|
|
minute_val: u8,
|
|
second_val: u8,
|
|
millisecond_val: u16,
|
|
microsecond_val: u16,
|
|
nanosecond_val: u16,
|
|
calendar: []const u8,
|
|
) !PlainDateTime {
|
|
const cal_view = abi.toDiplomatStringView(calendar);
|
|
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
|
const cal_kind = try abi.extractResult(cal_result);
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_try_new(
|
|
year_val,
|
|
month_val,
|
|
day_val,
|
|
hour_val,
|
|
minute_val,
|
|
second_val,
|
|
millisecond_val,
|
|
microsecond_val,
|
|
nanosecond_val,
|
|
cal_kind,
|
|
));
|
|
}
|
|
|
|
const FromInit = union(enum) { plain_date: PlainDate, plain_date_time: PlainDateTime };
|
|
|
|
/// Creates a PlainDateTime from another PlainDateTime, PlainDate, PlainTime, or from a string (ISO 8601) or UTF-16 array.
|
|
pub fn from(info: anytype, opts: FromOptions) !PlainDateTime {
|
|
const T = @TypeOf(info);
|
|
|
|
const overflow = if (opts.overflow) |f| abi.to.toArithmeticOverflow(f) else null;
|
|
if (T == PlainDateTime) return info.clone();
|
|
if (T == PlainTime) return abi.c.temporal_rs_PlainDateTime_from_partial(.{ .time = info._inner }, abi.toArithmeticOverflowOption(overflow));
|
|
if (T == PlainDate) return abi.c.temporal_rs_PlainDateTime_from_partial(.{ .date = info._inner }, abi.toArithmeticOverflowOption(overflow));
|
|
|
|
// Handle string types (both literals and slices)
|
|
const type_info = @typeInfo(T);
|
|
switch (type_info) {
|
|
.pointer => |ptr_info| {
|
|
const ChildType = switch (@typeInfo(ptr_info.child)) {
|
|
.array => |arr| arr.child,
|
|
else => ptr_info.child,
|
|
};
|
|
|
|
if (ChildType == u8) return fromUtf8(info);
|
|
if (ChildType == u16) return fromUtf16(info);
|
|
return abi.TemporalError.Generic;
|
|
},
|
|
else => return abi.TemporalError.Generic,
|
|
}
|
|
}
|
|
|
|
fn fromUtf8(utf8: []const u8) !PlainDateTime {
|
|
const view = abi.toDiplomatStringView(utf8);
|
|
const parsed = abi.c.temporal_rs_ParsedDateTime_from_utf8(view);
|
|
const ptr = try abi.extractResult(parsed);
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_from_parsed(ptr));
|
|
}
|
|
|
|
fn fromUtf16(utf16: []const u16) !PlainDateTime {
|
|
const view = abi.toDiplomatString16View(utf16);
|
|
const parsed = abi.c.temporal_rs_ParsedDateTime_from_utf16(view);
|
|
const ptr = try abi.extractResult(parsed);
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_from_parsed(ptr));
|
|
}
|
|
|
|
/// Compares two PlainDateTime objects. Returns -1, 0, or 1.
|
|
pub fn compare(a: PlainDateTime, b: PlainDateTime) i8 {
|
|
return abi.c.temporal_rs_PlainDateTime_compare(a._inner, b._inner);
|
|
}
|
|
|
|
/// Returns true if two PlainDateTime objects represent the same date and time.
|
|
pub fn equals(self: PlainDateTime, other: PlainDateTime) bool {
|
|
return compare(self, other) == 0;
|
|
}
|
|
|
|
/// Returns a new PlainDateTime by adding a Duration to this date-time.
|
|
pub fn add(self: PlainDateTime, duration: Duration) !PlainDateTime {
|
|
const overflow_opt = abi.c.ArithmeticOverflow_option{
|
|
.is_ok = true,
|
|
.unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain },
|
|
};
|
|
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_add(self._inner, duration._inner, overflow_opt));
|
|
}
|
|
|
|
/// Returns a new PlainDateTime by subtracting a Duration from this date-time.
|
|
pub fn subtract(self: PlainDateTime, duration: Duration) !PlainDateTime {
|
|
const overflow_opt = abi.c.ArithmeticOverflow_option{
|
|
.is_ok = true,
|
|
.unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain },
|
|
};
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_subtract(self._inner, duration._inner, overflow_opt));
|
|
}
|
|
|
|
/// Returns the Duration until another PlainDateTime, according to the given settings.
|
|
pub fn until(self: PlainDateTime, other: PlainDateTime, options: DifferenceSettings) !Duration {
|
|
const result = abi.c.temporal_rs_PlainDateTime_until(self._inner, other._inner, abi.to.diffsettings(options));
|
|
const ptr = (try abi.extractResult(result)) orelse return abi.TemporalError.Generic;
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns the Duration since another PlainDateTime, according to the given settings.
|
|
pub fn since(self: PlainDateTime, other: PlainDateTime, options: DifferenceSettings) !Duration {
|
|
const result = abi.c.temporal_rs_PlainDateTime_since(self._inner, other._inner, abi.to.diffsettings(options));
|
|
const ptr = (try abi.extractResult(result)) orelse return abi.TemporalError.Generic;
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns a new PlainDateTime rounded according to the given options.
|
|
pub fn round(self: PlainDateTime, options: RoundOptions) !PlainDateTime {
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_round(self._inner, abi.to.roundingOpts(options)));
|
|
}
|
|
|
|
/// Returns the calendar identifier for this date-time.
|
|
pub fn calendarId(self: PlainDateTime, allocator: std.mem.Allocator) ![]u8 {
|
|
const calendar_ptr = abi.c.temporal_rs_PlainDateTime_calendar(self._inner) orelse return error.TemporalError;
|
|
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
|
return try allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]);
|
|
}
|
|
|
|
/// Returns the day of the month (1-31).
|
|
pub fn day(self: PlainDateTime) u8 {
|
|
return abi.c.temporal_rs_PlainDateTime_day(self._inner);
|
|
}
|
|
|
|
/// Returns the day of the week (1 = Monday, 7 = Sunday).
|
|
pub fn dayOfWeek(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_day_of_week(self._inner);
|
|
}
|
|
|
|
/// Returns the day of the year (1-366).
|
|
pub fn dayOfYear(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_day_of_year(self._inner);
|
|
}
|
|
|
|
/// Returns the number of days in the month for this date-time.
|
|
pub fn daysInMonth(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_days_in_month(self._inner);
|
|
}
|
|
|
|
/// Returns the number of days in the week for this date-time.
|
|
pub fn daysInWeek(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_days_in_week(self._inner);
|
|
}
|
|
|
|
/// Returns the number of days in the year for this date-time.
|
|
pub fn daysInYear(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_days_in_year(self._inner);
|
|
}
|
|
|
|
/// Returns the month (1-12).
|
|
pub fn month(self: PlainDateTime) u8 {
|
|
return abi.c.temporal_rs_PlainDateTime_month(self._inner);
|
|
}
|
|
|
|
/// Returns the month code (e.g., "M03").
|
|
pub fn monthCode(self: PlainDateTime, allocator: std.mem.Allocator) ![]u8 {
|
|
var write = abi.DiplomatWrite.init(allocator);
|
|
defer write.deinit();
|
|
abi.c.temporal_rs_PlainDateTime_month_code(self._inner, &write.inner);
|
|
return try write.toOwnedSlice();
|
|
}
|
|
|
|
/// Returns the number of months in the year for this date-time.
|
|
pub fn monthsInYear(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_months_in_year(self._inner);
|
|
}
|
|
|
|
/// Returns the year.
|
|
pub fn year(self: PlainDateTime) i32 {
|
|
return abi.c.temporal_rs_PlainDateTime_year(self._inner);
|
|
}
|
|
|
|
/// Returns true if the year is a leap year.
|
|
pub fn inLeapYear(self: PlainDateTime) bool {
|
|
return abi.c.temporal_rs_PlainDateTime_in_leap_year(self._inner);
|
|
}
|
|
|
|
/// Returns the era for this date-time, if any.
|
|
pub fn era(self: PlainDateTime, allocator: std.mem.Allocator) !?[]u8 {
|
|
var write = abi.DiplomatWrite.init(allocator);
|
|
defer write.deinit();
|
|
abi.c.temporal_rs_PlainDateTime_era(self._inner, &write.inner);
|
|
const result = try write.toOwnedSlice();
|
|
if (result.len == 0) {
|
|
allocator.free(result);
|
|
return null;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// Returns the era year for this date-time, if any.
|
|
pub fn eraYear(self: PlainDateTime) !?i32 {
|
|
const result = abi.c.temporal_rs_PlainDateTime_era_year(self._inner);
|
|
if (!result.is_ok) return null;
|
|
return result.unnamed_0.ok;
|
|
}
|
|
|
|
/// Returns the week of the year for this date-time, if any.
|
|
pub fn weekOfYear(self: PlainDateTime) !?u16 {
|
|
const result = abi.c.temporal_rs_PlainDateTime_week_of_year(self._inner);
|
|
if (!result.is_ok) return null;
|
|
return result.unnamed_0.ok;
|
|
}
|
|
|
|
/// Returns the year of the week for this date-time, if any.
|
|
pub fn yearOfWeek(self: PlainDateTime) !?i32 {
|
|
const result = abi.c.temporal_rs_PlainDateTime_year_of_week(self._inner);
|
|
if (!result.is_ok) return null;
|
|
return result.unnamed_0.ok;
|
|
}
|
|
|
|
/// Returns the hour (0-23).
|
|
pub fn hour(self: PlainDateTime) u8 {
|
|
return abi.c.temporal_rs_PlainDateTime_hour(self._inner);
|
|
}
|
|
|
|
/// Returns the minute (0-59).
|
|
pub fn minute(self: PlainDateTime) u8 {
|
|
return abi.c.temporal_rs_PlainDateTime_minute(self._inner);
|
|
}
|
|
|
|
/// Returns the second (0-59).
|
|
pub fn second(self: PlainDateTime) u8 {
|
|
return abi.c.temporal_rs_PlainDateTime_second(self._inner);
|
|
}
|
|
|
|
/// Returns the millisecond (0-999).
|
|
pub fn millisecond(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_millisecond(self._inner);
|
|
}
|
|
|
|
/// Returns the microsecond (0-999).
|
|
pub fn microsecond(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_microsecond(self._inner);
|
|
}
|
|
|
|
/// Returns the nanosecond (0-999).
|
|
pub fn nanosecond(self: PlainDateTime) u16 {
|
|
return abi.c.temporal_rs_PlainDateTime_nanosecond(self._inner);
|
|
}
|
|
|
|
/// Returns a new PlainDateTime with some fields replaced.
|
|
pub fn with(self: PlainDateTime, partial: WithOptions) !PlainDateTime {
|
|
// Extract fields from partial, or use current values as defaults
|
|
const new_year = partial.year orelse self.year();
|
|
const new_month = partial.month orelse self.month();
|
|
const new_day = partial.day orelse self.day();
|
|
const new_hour = partial.hour orelse self.hour();
|
|
const new_minute = partial.minute orelse self.minute();
|
|
const new_second = partial.second orelse self.second();
|
|
const new_millisecond = partial.millisecond orelse self.millisecond();
|
|
const new_microsecond = partial.microsecond orelse self.microsecond();
|
|
const new_nanosecond = partial.nanosecond orelse self.nanosecond();
|
|
|
|
// Preserve calendar
|
|
const calendar_ptr = abi.c.temporal_rs_PlainDateTime_calendar(self._inner) orelse return error.TemporalError;
|
|
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
|
const cal_view = abi.c.DiplomatStringView{ .data = cal_id_view.data, .len = cal_id_view.len };
|
|
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
|
const cal_kind = try abi.extractResult(cal_result);
|
|
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_try_new(
|
|
new_year,
|
|
new_month,
|
|
new_day,
|
|
new_hour,
|
|
new_minute,
|
|
new_second,
|
|
new_millisecond,
|
|
new_microsecond,
|
|
new_nanosecond,
|
|
cal_kind,
|
|
));
|
|
}
|
|
|
|
/// Returns a new PlainDateTime with a different calendar.
|
|
pub fn withCalendar(self: PlainDateTime, calendar: []const u8) !PlainDateTime {
|
|
const cal_view = abi.toDiplomatStringView(calendar);
|
|
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
|
const cal_kind = try abi.extractResult(cal_result);
|
|
const ptr = abi.c.temporal_rs_PlainDateTime_with_calendar(self._inner, cal_kind) orelse return abi.TemporalError.Generic;
|
|
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns a new PlainDateTime with the time fields replaced by the given PlainTime (or zeroed if null).
|
|
pub fn withPlainTime(self: PlainDateTime, time: ?PlainTime) !PlainDateTime {
|
|
const new_hour: u8 = if (time) |tt| tt.hour() else 0;
|
|
const new_minute: u8 = if (time) |tt| tt.minute() else 0;
|
|
const new_second: u8 = if (time) |tt| tt.second() else 0;
|
|
const new_millisecond: u16 = if (time) |tt| tt.millisecond() else 0;
|
|
const new_microsecond: u16 = if (time) |tt| tt.microsecond() else 0;
|
|
const new_nanosecond: u16 = if (time) |tt| tt.nanosecond() else 0;
|
|
|
|
const calendar_ptr = abi.c.temporal_rs_PlainDateTime_calendar(self._inner) orelse return error.TemporalError;
|
|
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
|
const cal_view = abi.c.DiplomatStringView{ .data = cal_id_view.data, .len = cal_id_view.len };
|
|
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
|
const cal_kind = try abi.extractResult(cal_result);
|
|
|
|
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_try_new(
|
|
self.year(),
|
|
self.month(),
|
|
self.day(),
|
|
new_hour,
|
|
new_minute,
|
|
new_second,
|
|
new_millisecond,
|
|
new_microsecond,
|
|
new_nanosecond,
|
|
cal_kind,
|
|
));
|
|
}
|
|
|
|
/// Returns a PlainDate representing the date part of this PlainDateTime.
|
|
pub fn toPlainDate(self: PlainDateTime) !PlainDate {
|
|
const ptr = abi.c.temporal_rs_PlainDateTime_to_plain_date(self._inner) orelse return error.TemporalError;
|
|
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns a PlainTime representing the time part of this PlainDateTime.
|
|
pub fn toPlainTime(self: PlainDateTime) !PlainTime {
|
|
const ptr = abi.c.temporal_rs_PlainDateTime_to_plain_time(self._inner) orelse return error.TemporalError;
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns a ZonedDateTime by combining this date-time with a time zone.
|
|
pub fn toZonedDateTime(self: PlainDateTime, options: ToZonedDateTimeOptions) !ZonedDateTime {
|
|
const tz_view = abi.toDiplomatStringView(options.timeZone);
|
|
const tz_result = abi.c.temporal_rs_TimeZone_try_from_str(tz_view);
|
|
const time_zone = try abi.extractResult(tz_result);
|
|
|
|
// Convert to PlainDate and PlainTime
|
|
const date = try self.toPlainDate();
|
|
const time = try self.toPlainTime();
|
|
|
|
// Use PlainDate's toZonedDateTime with the time component
|
|
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_to_zoned_date_time(date._inner, time_zone, time._inner))) orelse return abi.TemporalError.Generic;
|
|
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
/// Returns a string representation of the date-time.
|
|
pub fn toString(self: PlainDateTime, allocator: std.mem.Allocator, options: ToStringOptions) ![]u8 {
|
|
var write = abi.DiplomatWrite.init(allocator);
|
|
defer write.deinit();
|
|
|
|
const rounding_options = std.mem.zeroes(abi.c.ToStringRoundingOptions);
|
|
|
|
const display_cal: abi.c.DisplayCalendar = if (options.calendar_display) |cal| switch (cal) {
|
|
.auto => @intCast(abi.c.DisplayCalendar_Auto),
|
|
.always => @intCast(abi.c.DisplayCalendar_Always),
|
|
.never => @intCast(abi.c.DisplayCalendar_Never),
|
|
.critical => @intCast(abi.c.DisplayCalendar_Critical),
|
|
} else @intCast(abi.c.DisplayCalendar_Auto);
|
|
|
|
const result = abi.c.temporal_rs_PlainDateTime_to_ixdtf_string(
|
|
self._inner,
|
|
rounding_options,
|
|
display_cal,
|
|
&write.inner,
|
|
);
|
|
|
|
if (!result.is_ok) return error.TemporalError;
|
|
return try write.toOwnedSlice();
|
|
}
|
|
|
|
/// Returns a string suitable for use as JSON.
|
|
pub fn toJSON(self: PlainDateTime, allocator: std.mem.Allocator) ![]u8 {
|
|
return try self.toString(allocator, .{});
|
|
}
|
|
|
|
/// Returns a locale-specific string representation of the date-time.
|
|
pub fn toLocaleString(self: PlainDateTime, allocator: std.mem.Allocator) []const u8 {
|
|
const s = self.toString(allocator, .{}) catch return "PlainDateTime.toLocaleString error";
|
|
return s;
|
|
}
|
|
|
|
/// Not supported. Throws ComparisonNotSupported error.
|
|
pub fn valueOf(self: PlainDateTime) !void {
|
|
_ = self;
|
|
return error.ComparisonNotSupported;
|
|
}
|
|
|
|
/// Returns a clone of this PlainDateTime.
|
|
fn clone(self: PlainDateTime) PlainDateTime {
|
|
return abi.c.temporal_rs_PlainDateTime_clone(self._inner);
|
|
}
|
|
|
|
/// Frees resources associated with this PlainDateTime.
|
|
pub fn deinit(self: *PlainDateTime) void {
|
|
abi.c.temporal_rs_PlainDateTime_destroy(self._inner);
|
|
}
|
|
|
|
/// Internal: Wraps a result as a PlainDateTime.
|
|
fn wrapPlainDateTime(res: anytype) !PlainDateTime {
|
|
const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic;
|
|
return .{ ._inner = ptr };
|
|
}
|
|
|
|
// ---------- Tests ---------------------
|
|
test init {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(i32, 2024), dt.year());
|
|
try std.testing.expectEqual(@as(u8, 1), dt.month());
|
|
try std.testing.expectEqual(@as(u8, 15), dt.day());
|
|
try std.testing.expectEqual(@as(u8, 14), dt.hour());
|
|
try std.testing.expectEqual(@as(u8, 30), dt.minute());
|
|
try std.testing.expectEqual(@as(u8, 45), dt.second());
|
|
}
|
|
|
|
test from {
|
|
const dt = try PlainDateTime.from("2024-01-15T14:30:45", .{});
|
|
try std.testing.expectEqual(@as(i32, 2024), dt.year());
|
|
try std.testing.expectEqual(@as(u8, 1), dt.month());
|
|
try std.testing.expectEqual(@as(u8, 15), dt.day());
|
|
}
|
|
|
|
test compare {
|
|
const dt1 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dt2 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dt3 = try PlainDateTime.init(2024, 1, 16, 14, 30, 0, 0, 0, 0);
|
|
|
|
try std.testing.expectEqual(@as(i8, 0), PlainDateTime.compare(dt1, dt2));
|
|
try std.testing.expectEqual(@as(i8, -1), PlainDateTime.compare(dt1, dt3));
|
|
try std.testing.expectEqual(@as(i8, 1), PlainDateTime.compare(dt3, dt1));
|
|
}
|
|
|
|
test equals {
|
|
const dt1 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dt2 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dt3 = try PlainDateTime.init(2024, 1, 16, 14, 30, 0, 0, 0, 0);
|
|
|
|
try std.testing.expect(dt1.equals(dt2));
|
|
try std.testing.expect(!dt1.equals(dt3));
|
|
}
|
|
|
|
test add {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dur = try Duration.from("P1D");
|
|
const result = try dt.add(dur);
|
|
|
|
try std.testing.expectEqual(@as(u8, 16), result.day());
|
|
}
|
|
|
|
test subtract {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dur = try Duration.from("P1D");
|
|
const result = try dt.subtract(dur);
|
|
|
|
try std.testing.expectEqual(@as(u8, 14), result.day());
|
|
}
|
|
|
|
test until {
|
|
const dt1 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dt2 = try PlainDateTime.init(2024, 1, 16, 14, 30, 0, 0, 0, 0);
|
|
const dur = try dt1.until(dt2, .{});
|
|
|
|
try std.testing.expectEqual(@as(i64, 1), dur.days());
|
|
}
|
|
|
|
test since {
|
|
const dt1 = try PlainDateTime.init(2024, 1, 16, 14, 30, 0, 0, 0, 0);
|
|
const dt2 = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const dur = try dt1.since(dt2, .{});
|
|
|
|
try std.testing.expectEqual(@as(i64, 1), dur.days());
|
|
}
|
|
|
|
test round {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 999, 999, 999);
|
|
const rounded = try dt.round(.{ .smallest_unit = .second });
|
|
|
|
try std.testing.expectEqual(@as(u8, 46), rounded.second());
|
|
try std.testing.expectEqual(@as(u16, 0), rounded.millisecond());
|
|
try std.testing.expectEqual(@as(u16, 0), rounded.microsecond());
|
|
try std.testing.expectEqual(@as(u16, 0), rounded.nanosecond());
|
|
}
|
|
|
|
test toString {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 0, 0);
|
|
const str = try dt.toString(std.testing.allocator, .{});
|
|
defer std.testing.allocator.free(str);
|
|
|
|
try std.testing.expect(std.mem.indexOf(u8, str, "2024-01-15") != null);
|
|
try std.testing.expect(std.mem.indexOf(u8, str, "14:30:45") != null);
|
|
}
|
|
|
|
test toJSON {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 0, 0, 0);
|
|
const str = try dt.toJSON(std.testing.allocator);
|
|
defer std.testing.allocator.free(str);
|
|
|
|
try std.testing.expect(std.mem.indexOf(u8, str, "2024-01-15") != null);
|
|
}
|
|
|
|
test toLocaleString {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 0, 0, 0);
|
|
const s_const = dt.toLocaleString(std.testing.allocator);
|
|
const s = @constCast(s_const);
|
|
defer std.testing.allocator.free(s);
|
|
try std.testing.expect(std.mem.indexOf(u8, s, "2024-01-15") != null);
|
|
}
|
|
|
|
test toPlainDate {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const date = try dt.toPlainDate();
|
|
|
|
try std.testing.expectEqual(@as(i32, 2024), date.year());
|
|
try std.testing.expectEqual(@as(u8, 1), date.month());
|
|
try std.testing.expectEqual(@as(u8, 15), date.day());
|
|
}
|
|
|
|
test toPlainTime {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 0, 0, 0);
|
|
const tt = try dt.toPlainTime();
|
|
try std.testing.expectEqual(@as(u8, 14), tt.hour());
|
|
try std.testing.expectEqual(@as(u8, 30), tt.minute());
|
|
try std.testing.expectEqual(@as(u8, 45), tt.second());
|
|
}
|
|
|
|
test toZonedDateTime {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const zdt = try dt.toZonedDateTime(.{ .timeZone = "UTC" });
|
|
|
|
const pdt = try zdt.toPlainDateTime();
|
|
try std.testing.expectEqual(@as(i32, 2024), pdt.year());
|
|
try std.testing.expectEqual(@as(u8, 1), pdt.month());
|
|
try std.testing.expectEqual(@as(u8, 15), pdt.day());
|
|
}
|
|
|
|
test with {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 0, 0, 0);
|
|
const result = try dt.with(.{ .day = 20, .hour = 16 });
|
|
try std.testing.expectEqual(@as(i32, 2024), result.year());
|
|
try std.testing.expectEqual(@as(u8, 1), result.month());
|
|
try std.testing.expectEqual(@as(u8, 20), result.day());
|
|
try std.testing.expectEqual(@as(u8, 16), result.hour());
|
|
try std.testing.expectEqual(@as(u8, 30), result.minute());
|
|
}
|
|
|
|
test withCalendar {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const result = try dt.withCalendar("iso8601");
|
|
|
|
try std.testing.expectEqual(@as(i32, 2024), result.year());
|
|
const cal_id = try result.calendarId(std.testing.allocator);
|
|
defer std.testing.allocator.free(cal_id);
|
|
try std.testing.expectEqualStrings("iso8601", cal_id);
|
|
}
|
|
|
|
test withPlainTime {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
|
const tt = try PlainTime.from("10:15:30");
|
|
const result = try dt.withPlainTime(tt);
|
|
try std.testing.expectEqual(@as(u8, 10), result.hour());
|
|
try std.testing.expectEqual(@as(u8, 15), result.minute());
|
|
try std.testing.expectEqual(@as(u8, 30), result.second());
|
|
}
|
|
|
|
test calInit {
|
|
const dt = try PlainDateTime.calInit(2024, 1, 15, 14, 30, 45, 123, 456, 789, "iso8601");
|
|
try std.testing.expectEqual(@as(i32, 2024), dt.year());
|
|
}
|
|
|
|
test calendarId {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const cal_id = try dt.calendarId(std.testing.allocator);
|
|
defer std.testing.allocator.free(cal_id);
|
|
try std.testing.expectEqualStrings("iso8601", cal_id);
|
|
}
|
|
|
|
test day {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u8, 15), dt.day());
|
|
}
|
|
|
|
test dayOfWeek {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const dow = dt.dayOfWeek();
|
|
try std.testing.expect(dow >= 1 and dow <= 7);
|
|
}
|
|
|
|
test dayOfYear {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const doy = dt.dayOfYear();
|
|
try std.testing.expect(doy >= 1 and doy <= 366);
|
|
}
|
|
|
|
test daysInMonth {
|
|
const dt = try PlainDateTime.init(2024, 2, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 29), dt.daysInMonth());
|
|
}
|
|
|
|
test daysInWeek {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 7), dt.daysInWeek());
|
|
}
|
|
|
|
test daysInYear {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 366), dt.daysInYear());
|
|
}
|
|
|
|
test month {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u8, 1), dt.month());
|
|
}
|
|
|
|
test monthCode {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const code = try dt.monthCode(std.testing.allocator);
|
|
defer std.testing.allocator.free(code);
|
|
try std.testing.expectEqualStrings("M01", code);
|
|
}
|
|
|
|
test monthsInYear {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 12), dt.monthsInYear());
|
|
}
|
|
|
|
test year {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(i32, 2024), dt.year());
|
|
}
|
|
|
|
test inLeapYear {
|
|
const leap = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expect(leap.inLeapYear());
|
|
|
|
const non_leap = try PlainDateTime.init(2023, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expect(!non_leap.inLeapYear());
|
|
}
|
|
|
|
test era {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const e = try dt.era(std.testing.allocator);
|
|
if (e) |era_val| {
|
|
defer std.testing.allocator.free(era_val);
|
|
try std.testing.expect(era_val.len > 0);
|
|
}
|
|
}
|
|
|
|
test eraYear {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const ey = try dt.eraYear();
|
|
if (ey) |y| {
|
|
try std.testing.expect(y > 0);
|
|
}
|
|
}
|
|
|
|
test weekOfYear {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const woy = try dt.weekOfYear();
|
|
if (woy) |week| {
|
|
try std.testing.expect(week >= 1 and week <= 53);
|
|
}
|
|
}
|
|
|
|
test yearOfWeek {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
const yow = try dt.yearOfWeek();
|
|
if (yow) |y| {
|
|
try std.testing.expect(y >= 2020);
|
|
}
|
|
}
|
|
|
|
test hour {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u8, 14), dt.hour());
|
|
}
|
|
|
|
test minute {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u8, 30), dt.minute());
|
|
}
|
|
|
|
test second {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u8, 45), dt.second());
|
|
}
|
|
|
|
test millisecond {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 123), dt.millisecond());
|
|
}
|
|
|
|
test microsecond {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 456), dt.microsecond());
|
|
}
|
|
|
|
test nanosecond {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectEqual(@as(u16, 789), dt.nanosecond());
|
|
}
|
|
|
|
test valueOf {
|
|
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 45, 123, 456, 789);
|
|
try std.testing.expectError(error.ComparisonNotSupported, dt.valueOf());
|
|
}
|