temporalz/src/PlainDate.zig
2026-05-27 12:08:02 +06:00

710 lines
24 KiB
Zig

const std = @import("std");
const abi = @import("abi.zig");
const t = @import("temporal.zig");
const PlainDateTime = @import("PlainDateTime.zig");
const PlainMonthDay = @import("PlainMonthDay.zig");
const PlainYearMonth = @import("PlainYearMonth.zig");
const ZonedDateTime = @import("ZonedDateTime.zig");
const PlainTime = @import("PlainTime.zig");
const Duration = @import("Duration.zig");
/// # Temporal.PlainDate
///
/// The `Temporal.PlainDate` object represents a calendar date (year, month, day) with no time or time zone.
///
/// The `Temporal.PlainDate` object represents a calendar date (year, month, day) with no time or time zone.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate
const PlainDate = @This();
/// Internal pointer to the underlying C PlainDate object.
_inner: *abi.c.PlainDate,
/// 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 dates.
pub const DifferenceSettings = t.DifferenceSettings;
/// Options for `toString()` method.
pub const ToStringOptions = struct {
/// Controls how the calendar is displayed in the string output.
calendar_id: ?CalendarDisplay = null,
};
/// 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 `toZonedDateTime()` method.
pub const ToZonedDateTimeOptions = struct {
/// The time zone identifier (IANA string).
time_zone: []const u8,
/// Optional PlainTime to use for the time part.
plain_time: ?PlainTime = null,
};
/// Options for `with()` method.
pub const WithOptions = struct {
/// Year value to override.
year: ?i32 = null,
/// Month value to override.
month: ?u8 = null,
/// Calendar-specific month code to override.
month_code: ?[]const u8 = null,
/// Day value to override.
day: ?u8 = null,
/// Era value to override.
era: ?[]const u8 = null,
/// Era year value to override.
era_year: ?i32 = null,
};
/// Creates a new PlainDate from the given ISO year, month, and day.
pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate {
return calInit(year_val, month_val, day_val, "iso8601");
}
/// Creates a new PlainDate with a specific calendar.
/// - `calendar`: Calendar identifier string (e.g., "iso8601").
pub fn calInit(year_val: i32, month_val: u8, day_val: u8, calendar: []const u8) !PlainDate {
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 wrapPlainDate(abi.c.temporal_rs_PlainDate_try_new(year_val, month_val, day_val, cal_kind));
}
/// Creates a PlainDate from another PlainDate or from a string (ISO 8601) or UTF-16 array.
pub fn from(info: anytype) !PlainDate {
const T = @TypeOf(info);
if (T == PlainDate) return info.clone();
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 PlainDate, []const u8, or []const u16"),
}
}
/// Internal: Creates a PlainDate from a UTF-8 string.
inline fn fromUtf8(text: []const u8) !PlainDate {
const view = abi.toDiplomatStringView(text);
return wrapPlainDate(abi.c.temporal_rs_PlainDate_from_utf8(view));
}
/// Internal: Creates a PlainDate from a UTF-16 string.
inline fn fromUtf16(text: []const u16) !PlainDate {
const view = abi.toDiplomatString16View(text);
return wrapPlainDate(abi.c.temporal_rs_PlainDate_from_utf16(view));
}
/// Compares two PlainDate objects. Returns -1, 0, or 1.
pub fn compare(a: PlainDate, b: PlainDate) i8 {
return abi.c.temporal_rs_PlainDate_compare(a._inner, b._inner);
}
/// Returns true if two PlainDate objects represent the same date.
pub fn equals(self: PlainDate, other: PlainDate) bool {
return abi.c.temporal_rs_PlainDate_equals(self._inner, other._inner);
}
/// Returns a new PlainDate by adding a Duration to this date.
pub fn add(self: PlainDate, duration: Duration) !PlainDate {
const overflow_opt = abi.c.ArithmeticOverflow_option{ .is_ok = true, .unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain } };
return wrapPlainDate(abi.c.temporal_rs_PlainDate_add(self._inner, duration._inner, overflow_opt));
}
/// Returns a new PlainDate by subtracting a Duration from this date.
pub fn subtract(self: PlainDate, duration: Duration) !PlainDate {
const overflow_opt = abi.c.ArithmeticOverflow_option{ .is_ok = true, .unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain } };
return wrapPlainDate(abi.c.temporal_rs_PlainDate_subtract(self._inner, duration._inner, overflow_opt));
}
/// Returns the Duration until another PlainDate, according to the given settings.
pub fn until(self: PlainDate, other: PlainDate, settings: DifferenceSettings) !Duration {
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_until(self._inner, other._inner, abi.to.diffsettings(settings)))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns the Duration since another PlainDate, according to the given settings.
pub fn since(self: PlainDate, other: PlainDate, settings: DifferenceSettings) !Duration {
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_since(self._inner, other._inner, abi.to.diffsettings(settings)))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a new PlainDate with some fields replaced.
pub fn with(self: PlainDate, fields: WithOptions) !PlainDate {
const partial_date = abi.c.PartialDate{
.year = if (fields.year) |y| abi.toOption(abi.c.OptionI32, y) else .{ .is_ok = false },
.month = if (fields.month) |m| abi.toOption(abi.c.OptionU8, m) else .{ .is_ok = false },
.day = if (fields.day) |d| abi.toOption(abi.c.OptionU8, d) else .{ .is_ok = false },
.month_code = if (fields.month_code) |mc| abi.toDiplomatStringView(mc) else .{ .data = null, .len = 0 },
.era = if (fields.era) |e| abi.toDiplomatStringView(e) else .{ .data = null, .len = 0 },
.era_year = if (fields.era_year) |ey| abi.toOption(abi.c.OptionI32, ey) else .{ .is_ok = false },
.calendar = abi.c.AnyCalendarKind_Iso,
};
const overflow = abi.c.ArithmeticOverflow_option{
.is_ok = true,
.unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain },
};
return wrapPlainDate(abi.c.temporal_rs_PlainDate_with(
self._inner,
partial_date,
overflow,
));
}
/// Returns a new PlainDate with a different calendar.
pub fn withCalendar(self: PlainDate, calendar: []const u8) !PlainDate {
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_PlainDate_with_calendar(self._inner, cal_kind) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a PlainDateTime by combining this date with a PlainTime (or midnight if null).
pub fn toPlainDateTime(self: PlainDate, time: ?PlainTime) !PlainDateTime {
const time_ptr = if (time) |tt| tt._inner else null;
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_to_plain_date_time(self._inner, time_ptr))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a PlainMonthDay representing the month and day in this date.
pub fn toPlainMonthDay(self: PlainDate) !PlainMonthDay {
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_to_plain_month_day(self._inner))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a PlainYearMonth representing the year and month in this date.
pub fn toPlainYearMonth(self: PlainDate) !PlainYearMonth {
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_to_plain_year_month(self._inner))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a ZonedDateTime by combining this date with a time and time zone.
pub fn toZonedDateTime(self: PlainDate, options: ToZonedDateTimeOptions) !ZonedDateTime {
const tz_view = abi.toDiplomatStringView(options.time_zone);
const tz_result = abi.c.temporal_rs_TimeZone_try_from_str(tz_view);
const time_zone = try abi.extractResult(tz_result);
const time_ptr = if (options.plain_time) |tt| tt._inner else null;
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_to_zoned_date_time(self._inner, time_zone, time_ptr))) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Returns a string representation of the date.
pub fn toString(self: PlainDate, allocator: std.mem.Allocator, options: ToStringOptions) ![]u8 {
var write = abi.DiplomatWrite.init(allocator);
defer write.deinit();
const display_cal: abi.c.DisplayCalendar = if (options.calendar_id) |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);
abi.c.temporal_rs_PlainDate_to_ixdtf_string(self._inner, display_cal, &write.inner);
return try write.toOwnedSlice();
}
/// Returns a string suitable for use as JSON.
pub fn toJSON(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
return self.toString(allocator, .{});
}
/// Not implemented. Throws TemporalNotImplemented error.
pub fn toLocaleString(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
_ = self;
_ = allocator;
return error.TemporalNotImplemented;
}
/// Not supported. Throws TemporalValueOfNotSupported error.
pub fn valueOf(self: PlainDate) !void {
_ = self;
return error.TemporalValueOfNotSupported;
}
/// Returns the day of the month (1-31).
pub fn day(self: PlainDate) u8 {
return abi.c.temporal_rs_PlainDate_day(self._inner);
}
/// Returns the day of the week (1 = Monday, 7 = Sunday).
pub fn dayOfWeek(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_day_of_week(self._inner);
}
/// Returns the day of the year (1-366).
pub fn dayOfYear(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_day_of_year(self._inner);
}
/// Returns the number of days in the month for this date.
pub fn daysInMonth(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_days_in_month(self._inner);
}
/// Returns the number of days in the week for this date.
pub fn daysInWeek(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_days_in_week(self._inner);
}
/// Returns the number of days in the year for this date.
pub fn daysInYear(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_days_in_year(self._inner);
}
/// Returns the month code (e.g., "M03").
pub fn monthCode(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
var write = abi.DiplomatWrite.init(allocator);
defer write.deinit();
abi.c.temporal_rs_PlainDate_month_code(self._inner, &write.inner);
return try write.toOwnedSlice();
}
/// Returns the month (1-12).
pub fn month(self: PlainDate) u8 {
return abi.c.temporal_rs_PlainDate_month(self._inner);
}
/// Returns the number of months in the year for this date.
pub fn monthsInYear(self: PlainDate) u16 {
return abi.c.temporal_rs_PlainDate_months_in_year(self._inner);
}
/// Returns the year.
pub fn year(self: PlainDate) i32 {
return abi.c.temporal_rs_PlainDate_year(self._inner);
}
/// Returns true if the year is a leap year.
pub fn inLeapYear(self: PlainDate) bool {
return abi.c.temporal_rs_PlainDate_in_leap_year(self._inner);
}
/// Returns the calendar identifier for this date.
pub fn calendarId(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
const calendar_ptr = abi.c.temporal_rs_PlainDate_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 era for this date, if any.
pub fn era(self: PlainDate, allocator: std.mem.Allocator) !?[]u8 {
var write = abi.DiplomatWrite.init(allocator);
defer write.deinit();
abi.c.temporal_rs_PlainDate_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, if any.
pub fn eraYear(self: PlainDate) ?i32 {
const result = abi.c.temporal_rs_PlainDate_era_year(self._inner);
if (!result.is_ok) return null;
return result.unnamed_0.ok;
}
/// Returns the week of the year for this date, if any.
pub fn weekOfYear(self: PlainDate) ?u8 {
const result = abi.c.temporal_rs_PlainDate_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, if any.
pub fn yearOfWeek(self: PlainDate) ?i32 {
const result = abi.c.temporal_rs_PlainDate_year_of_week(self._inner);
if (!result.is_ok) return null;
return result.unnamed_0.ok;
}
/// Returns a clone of this PlainDate.
fn clone(self: PlainDate) PlainDate {
const ptr = abi.c.temporal_rs_PlainDate_clone(self._inner) orelse unreachable;
return .{ ._inner = ptr };
}
/// Frees resources associated with this PlainDate.
pub fn deinit(self: PlainDate) void {
abi.c.temporal_rs_PlainDate_destroy(self._inner);
}
/// Internal: Wraps a result as a PlainDate.
fn wrapPlainDate(res: anytype) !PlainDate {
const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic;
return .{ ._inner = ptr };
}
/// Internal: Handles a void result from an FFI call.
fn handleVoidResult(res: anytype) !void {
_ = try abi.extractResult(res);
}
test init {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(i32, 2024), date.year());
try std.testing.expectEqual(@as(u8, 3), date.month());
try std.testing.expectEqual(@as(u8, 15), date.day());
}
test from {
const date = try PlainDate.from("2024-03-15");
defer date.deinit();
try std.testing.expectEqual(@as(i32, 2024), date.year());
try std.testing.expectEqual(@as(u8, 3), date.month());
try std.testing.expectEqual(@as(u8, 15), date.day());
const date2 = try PlainDate.from(date);
defer date2.deinit();
try std.testing.expect(PlainDate.equals(date, date2));
}
test compare {
const date1 = try PlainDate.init(2024, 1, 1);
defer date1.deinit();
const date2 = try PlainDate.init(2024, 1, 1);
defer date2.deinit();
const date3 = try PlainDate.init(2024, 12, 31);
defer date3.deinit();
try std.testing.expectEqual(@as(i8, 0), PlainDate.compare(date1, date2));
try std.testing.expectEqual(@as(i8, -1), PlainDate.compare(date1, date3));
try std.testing.expectEqual(@as(i8, 1), PlainDate.compare(date3, date1));
}
test equals {
const date1 = try PlainDate.init(2024, 3, 15);
defer date1.deinit();
const date2 = try PlainDate.init(2024, 3, 15);
defer date2.deinit();
const date3 = try PlainDate.init(2024, 3, 16);
defer date3.deinit();
try std.testing.expect(PlainDate.equals(date1, date2));
try std.testing.expect(!PlainDate.equals(date1, date3));
}
test add {
const date = try PlainDate.init(2024, 1, 1);
defer date.deinit();
var duration = try Duration.from("P1M");
defer duration.deinit();
const result = try date.add(duration);
defer result.deinit();
try std.testing.expectEqual(@as(i32, 2024), result.year());
try std.testing.expectEqual(@as(u8, 2), result.month());
try std.testing.expectEqual(@as(u8, 1), result.day());
}
test subtract {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
var duration = try Duration.from("P10D");
defer duration.deinit();
const result = try date.subtract(duration);
defer result.deinit();
try std.testing.expectEqual(@as(i32, 2024), result.year());
try std.testing.expectEqual(@as(u8, 3), result.month());
try std.testing.expectEqual(@as(u8, 5), result.day());
}
test until {
const start = try PlainDate.init(2024, 1, 1);
defer start.deinit();
const end = try PlainDate.init(2024, 2, 1);
defer end.deinit();
const settings = DifferenceSettings{
.largest_unit = .month,
.smallest_unit = .day,
.rounding_mode = .trunc,
.rounding_increment = null,
};
const duration = try start.until(end, settings);
defer duration.deinit();
try std.testing.expectEqual(@as(i64, 1), duration.months());
}
test since {
const end = try PlainDate.init(2024, 2, 1);
defer end.deinit();
const start = try PlainDate.init(2024, 1, 1);
defer start.deinit();
const settings = DifferenceSettings{
.largest_unit = .month,
.smallest_unit = .day,
.rounding_mode = .trunc,
.rounding_increment = null,
};
const duration = try end.since(start, settings);
defer duration.deinit();
try std.testing.expectEqual(@as(i64, 1), duration.months());
}
test toString {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const allocator = std.testing.allocator;
const str = try date.toString(allocator, .{});
defer allocator.free(str);
try std.testing.expect(std.mem.indexOf(u8, str, "2024-03-15") != null);
}
test toJSON {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const allocator = std.testing.allocator;
const str = try date.toJSON(allocator);
defer allocator.free(str);
try std.testing.expect(std.mem.indexOf(u8, str, "2024-03-15") != null);
}
test toLocaleString {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const allocator = std.testing.allocator;
try std.testing.expectError(error.TemporalNotImplemented, date.toLocaleString(allocator));
}
test toPlainDateTime {
const date = try PlainDate.init(2024, 1, 15);
const datetime = try date.toPlainDateTime(null);
try std.testing.expectEqual(@as(i32, 2024), datetime.year());
try std.testing.expectEqual(@as(u8, 1), datetime.month());
try std.testing.expectEqual(@as(u8, 15), datetime.day());
try std.testing.expectEqual(@as(u8, 0), datetime.hour());
try std.testing.expectEqual(@as(u8, 0), datetime.minute());
}
test toPlainMonthDay {
const date = try PlainDate.init(2024, 12, 25);
const md = try date.toPlainMonthDay();
try std.testing.expectEqual(@as(u8, 25), md.day());
const month_code = try md.monthCode(std.testing.allocator);
defer std.testing.allocator.free(month_code);
try std.testing.expect(month_code.len > 0);
}
test toPlainYearMonth {
const date = try PlainDate.init(2024, 12, 25);
const ym = try date.toPlainYearMonth();
try std.testing.expectEqual(@as(i32, 2024), ym.year());
try std.testing.expectEqual(@as(u8, 12), ym.month());
}
test toZonedDateTime {
const date = try PlainDate.init(2024, 1, 15);
const zdt = try date.toZonedDateTime(.{ .time_zone = "UTC", .plain_time = null });
try std.testing.expectEqual(@as(i32, 2024), zdt.year());
try std.testing.expectEqual(@as(u8, 1), zdt.month());
try std.testing.expectEqual(@as(u8, 15), zdt.day());
}
test with {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectError(error.TypeError, date.with(.{}));
const changed = try date.with(.{ .year = 2025, .month = 4, .day = 16 });
defer changed.deinit();
try std.testing.expectEqual(@as(i32, 2025), changed.year());
try std.testing.expectEqual(@as(u8, 4), changed.month());
try std.testing.expectEqual(@as(u8, 16), changed.day());
}
test withCalendar {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const result = try date.withCalendar("iso8601");
defer result.deinit();
try std.testing.expectEqual(@as(i32, 2024), result.year());
}
test calInit {
const date = try PlainDate.calInit(2024, 3, 15, "iso8601");
defer date.deinit();
try std.testing.expectEqual(@as(i32, 2024), date.year());
}
test valueOf {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectError(error.TemporalValueOfNotSupported, date.valueOf());
}
test day {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u8, 15), date.day());
}
test dayOfWeek {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const dow = date.dayOfWeek();
try std.testing.expect(dow >= 1 and dow <= 7);
}
test dayOfYear {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const doy = date.dayOfYear();
try std.testing.expect(doy >= 1 and doy <= 366);
}
test daysInMonth {
const date = try PlainDate.init(2024, 2, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u16, 29), date.daysInMonth());
}
test daysInWeek {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u16, 7), date.daysInWeek());
}
test daysInYear {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u16, 366), date.daysInYear());
}
test monthCode {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const code = try date.monthCode(std.testing.allocator);
defer std.testing.allocator.free(code);
try std.testing.expectEqualStrings("M03", code);
}
test month {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u8, 3), date.month());
}
test monthsInYear {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(u16, 12), date.monthsInYear());
}
test year {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
try std.testing.expectEqual(@as(i32, 2024), date.year());
}
test inLeapYear {
const leap = try PlainDate.init(2024, 3, 15);
defer leap.deinit();
try std.testing.expect(leap.inLeapYear());
const non_leap = try PlainDate.init(2023, 3, 15);
defer non_leap.deinit();
try std.testing.expect(!non_leap.inLeapYear());
}
test calendarId {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const cal = try date.calendarId(std.testing.allocator);
defer std.testing.allocator.free(cal);
try std.testing.expectEqualStrings("iso8601", cal);
}
test era {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const e = try date.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 date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const ey = date.eraYear();
if (ey) |y| {
try std.testing.expect(y > 0);
}
}
test weekOfYear {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const woy = date.weekOfYear();
if (woy) |week| {
try std.testing.expect(week >= 1 and week <= 53);
}
}
test yearOfWeek {
const date = try PlainDate.init(2024, 3, 15);
defer date.deinit();
const yow = date.yearOfWeek();
if (yow) |y| {
try std.testing.expect(y >= 2020);
}
}