// The `Temporal` namespace contains date and time related objects and functions, providing a modern alternative to the existing `Date` object in JavaScript. const Temporal = @This(); /// 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. /// /// - [MDN Temporal.Duration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration) pub const Duration = @import("Duration.zig"); /// The `Temporal.Instant` object represents a unique point in time, with nanosecond precision. /// It is fundamentally represented as the number of nanoseconds since the Unix epoch (midnight at the beginning of January 1, 1970, UTC), without any time zone or calendar system. /// /// - [MDN Temporal.Instant](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant) pub const Instant = @import("Instant.zig"); /// The `Temporal.Now` namespace object contains static methods for getting the current time in various formats. /// All properties and methods are static. /// /// - [MDN Temporal.Now](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now) pub const Now = @import("Now.zig"); /// The `Temporal.PlainDate` object represents a calendar date (year, month, day) with no time or time zone. /// /// - [MDN Temporal.PlainDate](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate) pub const PlainDate = @import("PlainDate.zig"); /// 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) pub const PlainDateTime = @import("PlainDateTime.zig"); /// The `Temporal.PlainMonthDay` object represents a month and day in a calendar, with no year or time. /// /// - [MDN Temporal.PlainMonthDay](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay) pub const PlainMonthDay = @import("PlainMonthDay.zig"); /// The `Temporal.PlainTime` object represents a wall-clock time, with no date or time zone. /// /// - [MDN Temporal.PlainTime](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime) pub const PlainTime = @import("PlainTime.zig"); /// The `Temporal.PlainYearMonth` object represents a particular month in a specific year, with no day or time. /// /// - [MDN Temporal.PlainYearMonth](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth) pub const PlainYearMonth = @import("PlainYearMonth.zig"); /// The `Temporal.ZonedDateTime` object represents an exact time, including a time zone and calendar. /// /// - [MDN Temporal.ZonedDateTime](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime) pub const ZonedDateTime = @import("ZonedDateTime.zig"); test Temporal { const std = @import("std"); _ = @import("abi.zig"); const expected_scopes = .{ "Duration", "Instant", "Now", "PlainDate", "PlainDateTime", "PlainMonthDay", "PlainTime", "PlainYearMonth", "ZonedDateTime", }; inline for (expected_scopes) |scope| { const has = @hasDecl(Temporal, scope); if (!has) std.log.err("Missing Temporal scope: {s}", .{scope}); try std.testing.expect(has); } } test Duration { const checks = .{ // Constructor "init", // Temporal.Duration() // Static methods "compare", "from", // Instance methods "abs", "add", "negated", "round", "subtract", "toJSON", "toLocaleString", "toString", "total", "valueOf", // "with", // Properties "blank", "days", "hours", "microseconds", "milliseconds", "minutes", "months", "nanoseconds", "seconds", "sign", "weeks", "years", // Public types "ToStringOptions", "PartialDuration", "RelativeTo", "Unit", "RoundingMode", "RoundingOptions", "ToStringRoundingOptions", "Sign", "TotalOptions", "CompareOptions", }; try assertDecls(Duration, checks); } test Instant { const checks = .{ // Constructor "init", // Temporal.Instant() // Static methods "compare", "from", "fromEpochMilliseconds", "fromEpochNanoseconds", // Instance methods "add", "equals", "round", "since", "subtract", "toJSON", "toLocaleString", "toString", "toZonedDateTimeISO", "until", "valueOf", // Properties "epochMilliseconds", "epochNanoseconds", // Public types "ToStringOptions", "TimeZone", "Unit", "RoundingMode", "Sign", "RoundingOptions", "DifferenceSettings", }; try assertDecls(Instant, checks); } test Now { const checks = .{ // Static methods "instant", "plainDateISO", "plainDateTimeISO", "plainTimeISO", "timeZoneId", "zonedDateTimeISO", }; try assertDecls(Now, checks); } test PlainDate { const checks = .{ // Constructor "init", "calInit", // Static methods "compare", "from", // Instance methods "add", "equals", "since", "subtract", "toJSON", "toLocaleString", "toPlainDateTime", "toPlainMonthDay", "toPlainYearMonth", "toString", "toZonedDateTime", "until", "valueOf", "with", "withCalendar", // Properties (now as methods) "calendarId", "day", "dayOfWeek", "dayOfYear", "daysInMonth", "daysInWeek", "daysInYear", "era", "eraYear", "inLeapYear", "month", "monthCode", "monthsInYear", "weekOfYear", "year", "yearOfWeek", // Public types "ToStringOptions", "CalendarDisplay", "ToZonedDateTimeOptions", "Unit", "RoundingMode", "Sign", "DifferenceSettings", }; try assertDecls(PlainDate, checks); } test PlainDateTime { const checks = .{ // Constructor "init", // Temporal.PlainDateTime() "calInit", // Static methods "compare", "from", // "fromUtf8", // "fromUtf16", // Instance methods "add", "equals", "round", "since", "subtract", "toJSON", "toLocaleString", "toPlainDate", "toPlainTime", "toString", "toZonedDateTime", "until", "valueOf", "with", "withCalendar", "withPlainTime", // Properties "calendarId", "day", "dayOfWeek", "dayOfYear", "daysInMonth", "daysInWeek", "daysInYear", "era", "eraYear", "hour", "inLeapYear", "microsecond", "millisecond", "minute", "month", "monthCode", "monthsInYear", "nanosecond", "second", "weekOfYear", "year", "yearOfWeek", // Public types "Unit", "RoundingMode", "Sign", "CalendarDisplay", "DifferenceSettings", "RoundOptions", "ToStringOptions", "ToZonedDateTimeOptions", "WithOptions", }; try assertDecls(PlainDateTime, checks); } test PlainMonthDay { const checks = .{ // Constructor "init", // Temporal.PlainMonthDay() // Static methods "from", // Instance methods "equals", "toJSON", "toLocaleString", "toPlainDate", "toString", "valueOf", "with", // Properties "calendarId", "day", "monthCode", // Public types "CalendarDisplay", "ToStringOptions", "WithOptions", }; try assertDecls(PlainMonthDay, checks); } test PlainTime { const checks = .{ // Constructor "init", // Temporal.PlainTime() // Static methods "compare", "from", // Instance methods "add", "equals", "round", "since", "subtract", "toJSON", "toLocaleString", "toString", "until", "valueOf", "with", // Properties "hour", "microsecond", "millisecond", "minute", "nanosecond", "second", // Public types "Unit", "RoundingMode", "DifferenceSettings", "RoundOptions", "WithOptions", }; try assertDecls(PlainTime, checks); } test PlainYearMonth { const checks = .{ // Constructor "init", // Temporal.PlainYearMonth() // Static methods "compare", "from", // Instance methods "add", "equals", "since", "subtract", "toJSON", "toLocaleString", "toPlainDate", "toString", "until", "valueOf", "with", // Properties "calendarId", "daysInMonth", "daysInYear", "era", "eraYear", "inLeapYear", "month", "monthCode", "monthsInYear", "year", // Public types "Unit", "RoundingMode", "CalendarDisplay", "DifferenceSettings", "RoundOptions", "ToStringOptions", "WithOptions", }; try assertDecls(PlainYearMonth, checks); } test ZonedDateTime { const checks = .{ // Constructor "init", // Temporal.ZonedDateTime() // Static methods "compare", "from", "fromEpochMilliseconds", "fromEpochNanoseconds", // Instance methods "add", "clone", "equals", "getTimeZoneTransition", "round", "since", "startOfDay", "subtract", "toInstant", "toJSON", "toLocaleString", "toPlainDate", "toPlainDateTime", "toPlainTime", "toString", "until", "valueOf", "with", "withCalendar", "withPlainTime", "withTimeZone", // Properties "calendarId", "day", "dayOfWeek", "dayOfYear", "daysInMonth", "daysInWeek", "daysInYear", "epochMilliseconds", "epochNanoseconds", "era", "eraYear", "hour", "hoursInDay", "inLeapYear", "microsecond", "millisecond", "minute", "month", "monthCode", "monthsInYear", "nanosecond", "offset", "offsetNanoseconds", "second", "timeZoneId", "weekOfYear", "year", "yearOfWeek", // Public types "Unit", "RoundingMode", "Sign", "DifferenceSettings", "RoundOptions", "TimeZone", "Disambiguation", "OffsetDisambiguation", "CalendarDisplay", "DisplayOffset", "DisplayTimeZone", "ToStringOptions", }; try assertDecls(ZonedDateTime, checks); } fn assertDecls(comptime T: type, checks: anytype) !void { @setEvalBranchQuota(5000); // Increase branch quota for large check lists const std = @import("std"); const typeInfo = @typeInfo(T); // Check: all items in checks exist (either as decls or as fields) inline for (checks) |check| { const should_ignore = std.mem.eql(u8, check, "deinit") or std.mem.eql(u8, check, "valueOf"); if (!should_ignore) { const hasDecl = @hasDecl(T, check); // Also check if it's a field (property) var hasField = false; if (typeInfo == .@"struct") { const struct_info = typeInfo.@"struct"; inline for (struct_info.fields) |field| { // Check both camelCase and snake_case if (std.mem.eql(u8, field.name, check) or std.mem.eql(u8, field.name, camelToSnakeCase(check))) { hasField = true; break; } } } const has = hasDecl or hasField; if (!has) std.log.err("Missing {s} decl or field: {s}", .{ @typeName(T), check }); try std.testing.expect(has); } } // Check: no extraneous declarations or fields beyond checks if (typeInfo == .@"struct") { const struct_info = typeInfo.@"struct"; // Check declarations inline for (struct_info.decls) |decl| { // Allow deinit as extraneous if (comptime std.mem.eql(u8, decl.name, "deinit")) continue; var found = false; inline for (checks) |check| { if (std.mem.eql(u8, decl.name, check)) { found = true; break; } } if (!found) { std.log.err("Extraneous {s} decl: {s}", .{ @typeName(T), decl.name }); try std.testing.expect(false); } } // Check fields (properties) inline for (struct_info.fields) |field| { // Allow internal fields (starting with underscore) if (comptime std.mem.startsWith(u8, field.name, "_")) continue; var found = false; inline for (checks) |check| { if (std.mem.eql(u8, field.name, camelToSnakeCase(check))) { found = true; break; } } if (!found) { std.log.err("Extraneous {s} field: {s}", .{ @typeName(T), field.name }); try std.testing.expect(false); } } } } fn camelToSnakeCase(comptime input: []const u8) []const u8 { comptime var len: usize = undefined; comptime { var llen: usize = input.len; for (input, 0..) |c, i| { if (c >= 'A' and c <= 'Z' and i != 0) { llen += 1; } } len = llen; } comptime var result: [len]u8 = undefined; comptime var write_index: usize = 0; comptime { for (input, 0..) |c, i| { if (c >= 'A' and c <= 'Z') { if (i != 0) { result[write_index] = '_'; write_index += 1; } result[write_index] = c + 32; // Convert to lowercase } else { result[write_index] = c; } write_index += 1; } } const final = result; return &final; }