feat(plain): add initial wrapper
This commit is contained in:
parent
6a8a52640f
commit
3000ef85c4
11 changed files with 2687 additions and 547 deletions
14
README.md
14
README.md
|
|
@ -7,18 +7,18 @@ Temporalz provides Zig bindings to the Rust-based [temporal_rs](https://github.c
|
|||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
#### Prerequisites
|
||||
|
||||
- Zig 0.15.2
|
||||
- Rust toolchain (only required if [prebuilt binaries](#prebuilt) are not available for your platform)
|
||||
|
||||
### Add as a Dependency
|
||||
#### Add as a Dependency
|
||||
|
||||
```bash
|
||||
zig fetch --save=temporalz https://github.com/nurulhudaapon/temporalz/archive/refs/tags/v0.1.2.tar.gz
|
||||
zig fetch --save https://github.com/nurulhudaapon/temporalz/archive/refs/tags/v0.1.2.tar.gz
|
||||
```
|
||||
|
||||
### Use in build.zig
|
||||
#### Use in build.zig
|
||||
|
||||
Add temporalz to your executable in `build.zig`:
|
||||
|
||||
|
|
@ -57,20 +57,20 @@ For other platforms, the library will build from source; you need the Rust toolc
|
|||
|
||||
## Development
|
||||
|
||||
### Clone the Repository
|
||||
#### Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/nurulhudaapon/temporalz.git
|
||||
cd temporalz
|
||||
```
|
||||
|
||||
### Build and Run
|
||||
#### Build and Run
|
||||
|
||||
```bash
|
||||
zig build run
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
#### Run Tests
|
||||
|
||||
```bash
|
||||
zig build test
|
||||
|
|
|
|||
|
|
@ -10,16 +10,56 @@ pub const ToStringRoundingOptions = temporal.ToStringRoundingOptions;
|
|||
pub const Unit = temporal.Unit;
|
||||
pub const RoundingMode = temporal.RoundingMode;
|
||||
pub const Sign = temporal.Sign;
|
||||
pub const PartialDuration = abi.c.PartialDuration;
|
||||
|
||||
/// Partial duration specification for creating Duration objects.
|
||||
/// This is a wrapper around the C API type to avoid exposing C types directly.
|
||||
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,
|
||||
|
||||
fn toCApi(self: PartialDuration) abi.c.PartialDuration {
|
||||
return .{
|
||||
.years = abi.toOption(abi.c.OptionI64, self.years),
|
||||
.months = abi.toOption(abi.c.OptionI64, self.months),
|
||||
.weeks = abi.toOption(abi.c.OptionI64, self.weeks),
|
||||
.days = abi.toOption(abi.c.OptionI64, self.days),
|
||||
.hours = abi.toOption(abi.c.OptionI64, self.hours),
|
||||
.minutes = abi.toOption(abi.c.OptionI64, self.minutes),
|
||||
.seconds = abi.toOption(abi.c.OptionI64, self.seconds),
|
||||
.milliseconds = abi.toOption(abi.c.OptionI64, self.milliseconds),
|
||||
.microseconds = abi.toOption(abi.c.OptionF64, self.microseconds),
|
||||
.nanoseconds = abi.toOption(abi.c.OptionF64, self.nanoseconds),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Wrapper for PlainDate reference in RelativeTo
|
||||
const PlainDateRef = struct {
|
||||
_inner: *abi.c.PlainDate,
|
||||
};
|
||||
|
||||
/// Wrapper for ZonedDateTime reference in RelativeTo
|
||||
const ZonedDateTimeRef = struct {
|
||||
_inner: *abi.c.ZonedDateTime,
|
||||
};
|
||||
|
||||
/// Relative-to context for duration operations.
|
||||
pub const RelativeTo = extern struct {
|
||||
plain_date: ?*abi.c.PlainDate,
|
||||
zoned_date_time: ?*abi.c.ZonedDateTime,
|
||||
pub const RelativeTo = struct {
|
||||
plain_date: ?PlainDateRef = null,
|
||||
zoned_date_time: ?ZonedDateTimeRef = null,
|
||||
|
||||
fn toCApi(self: RelativeTo) abi.c.RelativeTo {
|
||||
return .{
|
||||
.date = self.plain_date,
|
||||
.zoned = self.zoned_date_time,
|
||||
.date = if (self.plain_date) |pd| pd._inner else null,
|
||||
.zoned = if (self.zoned_date_time) |zdt| zdt._inner else null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -73,7 +113,7 @@ fn fromUtf16(text: []const u16) !Duration {
|
|||
|
||||
/// 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(partial));
|
||||
return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(partial.toCApi()));
|
||||
}
|
||||
|
||||
/// Check if the time portion of the duration is within valid ranges.
|
||||
|
|
@ -362,20 +402,9 @@ test toString {
|
|||
}
|
||||
|
||||
test fromPartialDuration {
|
||||
const empty_i64 = abi.toOption(abi.c.OptionI64, null);
|
||||
const empty_f64 = abi.toOption(abi.c.OptionF64, null);
|
||||
|
||||
const partial = abi.c.PartialDuration{
|
||||
.years = empty_i64,
|
||||
.months = empty_i64,
|
||||
.weeks = empty_i64,
|
||||
.days = empty_i64,
|
||||
.hours = .{ .is_ok = true, .unnamed_0 = .{ .ok = 3 } },
|
||||
.minutes = .{ .is_ok = true, .unnamed_0 = .{ .ok = 45 } },
|
||||
.seconds = empty_i64,
|
||||
.milliseconds = empty_i64,
|
||||
.microseconds = empty_f64,
|
||||
.nanoseconds = empty_f64,
|
||||
const partial = PartialDuration{
|
||||
.hours = 3,
|
||||
.minutes = 45,
|
||||
};
|
||||
|
||||
const dur = try Duration.fromPartialDuration(partial);
|
||||
|
|
|
|||
136
src/Instant.zig
136
src/Instant.zig
|
|
@ -11,6 +11,21 @@ pub const RoundingMode = temporal.RoundingMode;
|
|||
pub const Sign = temporal.Sign;
|
||||
pub const RoundingOptions = temporal.RoundingOptions;
|
||||
pub const DifferenceSettings = temporal.DifferenceSettings;
|
||||
|
||||
/// Time zone identifier for Temporal operations
|
||||
pub const TimeZone = struct {
|
||||
_inner: abi.c.TimeZone,
|
||||
|
||||
pub fn init(id: []const u8) TimeZone {
|
||||
const view = abi.toDiplomatStringView(id);
|
||||
return .{ ._inner = .{ .id = view } };
|
||||
}
|
||||
|
||||
fn toCApi(self: TimeZone) abi.c.TimeZone {
|
||||
return self._inner;
|
||||
}
|
||||
};
|
||||
|
||||
/// Options for Instant.toString()
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toString
|
||||
pub const ToStringOptions = struct {
|
||||
|
|
@ -29,8 +44,7 @@ pub const ToStringOptions = struct {
|
|||
smallest_unit: ?Unit = null,
|
||||
|
||||
/// Time zone to use. Either a time zone identifier string or null for UTC.
|
||||
/// Note: In the Zig API, this must be pre-resolved to a TimeZone struct.
|
||||
time_zone: ?abi.c.TimeZone = null,
|
||||
time_zone: ?TimeZone = null,
|
||||
};
|
||||
|
||||
_inner: *abi.c.Instant,
|
||||
|
|
@ -99,13 +113,15 @@ pub fn subtract(self: Instant, duration: *Duration) !Instant {
|
|||
}
|
||||
|
||||
/// Difference until another instant (Temporal.Instant.prototype.until).
|
||||
pub fn until(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle {
|
||||
return wrapDuration(abi.c.temporal_rs_Instant_until(self._inner, other._inner, settings.toCApi()));
|
||||
pub fn until(self: Instant, other: Instant, settings: DifferenceSettings) !Duration {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_Instant_until(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Difference since another instant (Temporal.Instant.prototype.since).
|
||||
pub fn since(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle {
|
||||
return wrapDuration(abi.c.temporal_rs_Instant_since(self._inner, other._inner, settings.toCApi()));
|
||||
pub fn since(self: Instant, other: Instant, settings: DifferenceSettings) !Duration {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_Instant_since(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Round this instant (Temporal.Instant.prototype.round).
|
||||
|
|
@ -125,7 +141,7 @@ pub fn equals(a: Instant, b: Instant) bool {
|
|||
|
||||
/// Convert to string using compiled TZ data; caller owns returned slice.
|
||||
pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptions) ![]u8 {
|
||||
const zone_opt = abi.toTimeZoneOption(opts.time_zone);
|
||||
const zone_opt = if (opts.time_zone) |tz| abi.toTimeZoneOption(tz._inner) else abi.toTimeZoneOption(null);
|
||||
const rounding = optsToRounding(opts);
|
||||
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
|
|
@ -139,7 +155,7 @@ pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptio
|
|||
|
||||
/// Convert to string using an explicit provider.
|
||||
fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *const abi.c.Provider, opts: ToStringOptions) ![]u8 {
|
||||
const zone_opt = abi.toTimeZoneOption(opts.time_zone);
|
||||
const zone_opt = if (opts.time_zone) |tz| abi.toTimeZoneOption(tz._inner) else abi.toTimeZoneOption(null);
|
||||
const rounding = optsToRounding(opts);
|
||||
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
|
|
@ -155,20 +171,23 @@ pub fn toJSON(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
|||
return self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
/// Convert to a locale string representation.
|
||||
/// Per Temporal spec, toLocaleString returns a formatted string without taking locale/options parameters.
|
||||
/// This uses auto precision for fractional seconds.
|
||||
pub fn toLocaleString(self: Instant, allocator: std.mem.Allocator) ![]u8 {
|
||||
_ = self;
|
||||
_ = allocator;
|
||||
return error.TemporalNotImplemented;
|
||||
return self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
/// Convert to ZonedDateTime using built-in provider (Temporal.Instant.prototype.toZonedDateTimeISO).
|
||||
pub fn toZonedDateTimeISO(self: Instant, zone: abi.c.TimeZone) !ZonedDateTimeHandle {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_Instant_to_zoned_date_time_iso(self._inner, zone));
|
||||
pub fn toZonedDateTimeISO(self: Instant, zone: TimeZone) !ZonedDateTime {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_Instant_to_zoned_date_time_iso(self._inner, zone._inner)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Convert to ZonedDateTime using an explicit provider.
|
||||
fn toZonedDateTimeIsoWithProvider(self: Instant, zone: abi.c.TimeZone, provider: *const abi.c.Provider) !ZonedDateTimeHandle {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self._inner, zone, provider));
|
||||
fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *const abi.c.Provider) !ZonedDateTime {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self._inner, zone._inner, provider)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Clone the underlying instant.
|
||||
|
|
@ -192,16 +211,6 @@ fn wrapInstant(res: anytype) !Instant {
|
|||
};
|
||||
}
|
||||
|
||||
fn wrapDuration(res: anytype) !DurationHandle {
|
||||
const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ .ptr = ptr };
|
||||
}
|
||||
|
||||
fn wrapZonedDateTime(res: anytype) !ZonedDateTimeHandle {
|
||||
const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ .ptr = ptr };
|
||||
}
|
||||
|
||||
fn handleVoidResult(res: anytype) !void {
|
||||
_ = abi.success(res) orelse return error.TemporalError;
|
||||
}
|
||||
|
|
@ -232,26 +241,13 @@ fn optsToRounding(opts: ToStringOptions) abi.c.ToStringRoundingOptions {
|
|||
};
|
||||
}
|
||||
|
||||
fn parseDuration(text: []const u8) !DurationHandle {
|
||||
const view = abi.toDiplomatStringView(text);
|
||||
return wrapDuration(abi.c.temporal_rs_Duration_from_utf8(view));
|
||||
}
|
||||
// --- Forward declarations -----------------------------------------------------
|
||||
|
||||
// --- Helper types -----------------------------------------------------
|
||||
const ZonedDateTime = struct {
|
||||
_inner: *abi.c.ZonedDateTime,
|
||||
|
||||
const DurationHandle = struct {
|
||||
ptr: *abi.c.Duration,
|
||||
|
||||
pub fn deinit(self: DurationHandle) void {
|
||||
abi.c.temporal_rs_Duration_destroy(self.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
const ZonedDateTimeHandle = struct {
|
||||
ptr: *abi.c.ZonedDateTime,
|
||||
|
||||
pub fn deinit(self: ZonedDateTimeHandle) void {
|
||||
abi.c.temporal_rs_ZonedDateTime_destroy(self.ptr);
|
||||
pub fn deinit(self: ZonedDateTime) void {
|
||||
abi.c.temporal_rs_ZonedDateTime_destroy(self._inner);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -365,15 +361,15 @@ test until {
|
|||
.rounding_increment = null,
|
||||
};
|
||||
|
||||
var until_handle = try earlier.until(later, settings);
|
||||
defer until_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, Sign.fromCApi(abi.c.temporal_rs_Duration_sign(until_handle.ptr)));
|
||||
try std.testing.expectEqual(@as(i64, 1), abi.c.temporal_rs_Duration_hours(until_handle.ptr));
|
||||
const until_dur = try earlier.until(later, settings);
|
||||
defer until_dur.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, until_dur.sign());
|
||||
try std.testing.expectEqual(@as(i64, 1), until_dur.hours());
|
||||
|
||||
var since_handle = try later.since(earlier, settings);
|
||||
defer since_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, Sign.fromCApi(abi.c.temporal_rs_Duration_sign(since_handle.ptr)));
|
||||
try std.testing.expectEqual(@as(i64, 1), abi.c.temporal_rs_Duration_hours(since_handle.ptr));
|
||||
const since_dur = try later.since(earlier, settings);
|
||||
defer since_dur.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, since_dur.sign());
|
||||
try std.testing.expectEqual(@as(i64, 1), since_dur.hours());
|
||||
}
|
||||
|
||||
test since {
|
||||
|
|
@ -389,15 +385,15 @@ test since {
|
|||
.rounding_increment = null,
|
||||
};
|
||||
|
||||
var until_handle = try earlier.until(later, settings);
|
||||
defer until_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, Sign.fromCApi(abi.c.temporal_rs_Duration_sign(until_handle.ptr)));
|
||||
try std.testing.expectEqual(@as(i64, 1), abi.c.temporal_rs_Duration_hours(until_handle.ptr));
|
||||
const until_dur = try earlier.until(later, settings);
|
||||
defer until_dur.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, until_dur.sign());
|
||||
try std.testing.expectEqual(@as(i64, 1), until_dur.hours());
|
||||
|
||||
var since_handle = try later.since(earlier, settings);
|
||||
defer since_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, Sign.fromCApi(abi.c.temporal_rs_Duration_sign(since_handle.ptr)));
|
||||
try std.testing.expectEqual(@as(i64, 1), abi.c.temporal_rs_Duration_hours(since_handle.ptr));
|
||||
const since_dur = try later.since(earlier, settings);
|
||||
defer since_dur.deinit();
|
||||
try std.testing.expectEqual(Sign.positive, since_dur.sign());
|
||||
try std.testing.expectEqual(@as(i64, 1), since_dur.hours());
|
||||
}
|
||||
|
||||
test round {
|
||||
|
|
@ -450,3 +446,25 @@ test toString {
|
|||
defer allocator.free(with_unit);
|
||||
// Output should truncate to seconds
|
||||
}
|
||||
|
||||
test toLocaleString {
|
||||
const epoch_ns: i128 = 1704067200000000000; // 2024-01-01 00:00:00 UTC
|
||||
const inst = try Instant.init(epoch_ns);
|
||||
defer inst.deinit();
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// toLocaleString should return a formatted string (same as toString with defaults)
|
||||
const locale_str = try inst.toLocaleString(allocator);
|
||||
defer allocator.free(locale_str);
|
||||
try std.testing.expectEqualStrings(locale_str, "2024-01-01T00:00:00Z");
|
||||
|
||||
// Test with a different instant with fractional seconds
|
||||
const inst2 = try Instant.fromEpochNanoseconds(1704067200123456789);
|
||||
defer inst2.deinit();
|
||||
|
||||
const locale_str2 = try inst2.toLocaleString(allocator);
|
||||
defer allocator.free(locale_str2);
|
||||
// Auto precision should include fractional seconds
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, locale_str2, 1, "2024-01-01T00:00:00"));
|
||||
}
|
||||
|
|
|
|||
114
src/Now.zig
114
src/Now.zig
|
|
@ -1,3 +1,4 @@
|
|||
const std = @import("std");
|
||||
const Instant = @import("Instant.zig");
|
||||
const PlainDate = @import("PlainDate.zig");
|
||||
const PlainDateTime = @import("PlainDateTime.zig");
|
||||
|
|
@ -6,47 +7,124 @@ const ZonedDateTime = @import("ZonedDateTime.zig");
|
|||
|
||||
const Now = @This();
|
||||
|
||||
pub fn instant() error{Todo}!Instant {
|
||||
return error.Todo;
|
||||
pub fn instant() !Instant {
|
||||
const ns: i128 = std.time.nanoTimestamp();
|
||||
return Instant.fromEpochNanoseconds(ns);
|
||||
}
|
||||
|
||||
pub fn plainDateISO() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
pub fn plainDateISO() !PlainDate {
|
||||
const now = currentParts();
|
||||
return PlainDate.init(now.year, now.month, now.day);
|
||||
}
|
||||
|
||||
pub fn plainDateTimeISO() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
pub fn plainDateTimeISO() !PlainDateTime {
|
||||
const now = currentParts();
|
||||
return PlainDateTime.init(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
now.hour,
|
||||
now.minute,
|
||||
now.second,
|
||||
now.millisecond,
|
||||
now.microsecond,
|
||||
now.nanosecond,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn plainTimeISO() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
pub fn plainTimeISO() !PlainTime {
|
||||
const now = currentParts();
|
||||
return PlainTime.init(
|
||||
now.hour,
|
||||
now.minute,
|
||||
now.second,
|
||||
now.millisecond,
|
||||
now.microsecond,
|
||||
now.nanosecond,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn timeZoneId() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn timeZoneId() []const u8 {
|
||||
return "UTC";
|
||||
}
|
||||
|
||||
pub fn zonedDateTimeISO() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
pub fn zonedDateTimeISO() !ZonedDateTime {
|
||||
return error.TemporalNotImplemented;
|
||||
}
|
||||
|
||||
const CurrentParts = struct {
|
||||
year: i32,
|
||||
month: u8,
|
||||
day: u8,
|
||||
hour: u8,
|
||||
minute: u8,
|
||||
second: u8,
|
||||
millisecond: u16,
|
||||
microsecond: u16,
|
||||
nanosecond: u16,
|
||||
};
|
||||
|
||||
fn currentParts() CurrentParts {
|
||||
const ns: i128 = std.time.nanoTimestamp();
|
||||
const seconds: i64 = @intCast(@divTrunc(ns, 1_000_000_000));
|
||||
const subsec_nanos_u64: u64 = @intCast(@rem(ns, 1_000_000_000));
|
||||
|
||||
const days_since_epoch: u64 = @intCast(@divTrunc(seconds, std.time.epoch.secs_per_day));
|
||||
const secs_of_day: u64 = @intCast(@mod(seconds, std.time.epoch.secs_per_day));
|
||||
|
||||
const epoch_day = std.time.epoch.EpochDay{ .day = @intCast(days_since_epoch) };
|
||||
const year_day = epoch_day.calculateYearDay();
|
||||
const month_day = year_day.calculateMonthDay();
|
||||
const day_seconds = std.time.epoch.DaySeconds{ .secs = @intCast(secs_of_day) };
|
||||
|
||||
const millisecond: u16 = @intCast(subsec_nanos_u64 / 1_000_000);
|
||||
const microsecond: u16 = @intCast((subsec_nanos_u64 / 1_000) % 1_000);
|
||||
const nanosecond: u16 = @intCast(subsec_nanos_u64 % 1_000);
|
||||
|
||||
return .{
|
||||
.year = @intCast(year_day.year),
|
||||
.month = @intCast(month_day.month.numeric()),
|
||||
.day = @intCast(month_day.day_index + 1),
|
||||
.hour = @intCast(day_seconds.getHoursIntoDay()),
|
||||
.minute = @intCast(day_seconds.getMinutesIntoHour()),
|
||||
.second = @intCast(day_seconds.getSecondsIntoMinute()),
|
||||
.millisecond = millisecond,
|
||||
.microsecond = microsecond,
|
||||
.nanosecond = nanosecond,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test instant {
|
||||
if (true) return error.Todo;
|
||||
const inst = try instant();
|
||||
defer inst.deinit();
|
||||
try std.testing.expect(inst.epoch_nanoseconds > 0);
|
||||
}
|
||||
|
||||
test plainDateISO {
|
||||
if (true) return error.Todo;
|
||||
const date = try plainDateISO();
|
||||
try std.testing.expect(date.year() >= 1970);
|
||||
try std.testing.expect(date.month() >= 1 and date.month() <= 12);
|
||||
try std.testing.expect(date.day() >= 1 and date.day() <= 31);
|
||||
}
|
||||
test plainDateTimeISO {
|
||||
if (true) return error.Todo;
|
||||
const dt = try plainDateTimeISO();
|
||||
try std.testing.expect(dt.year() >= 1970);
|
||||
try std.testing.expect(dt.month() >= 1 and dt.month() <= 12);
|
||||
try std.testing.expect(dt.day() >= 1 and dt.day() <= 31);
|
||||
try std.testing.expect(dt.hour() < 24);
|
||||
try std.testing.expect(dt.minute() < 60);
|
||||
}
|
||||
test plainTimeISO {
|
||||
if (true) return error.Todo;
|
||||
const t = try plainTimeISO();
|
||||
try std.testing.expect(t.hour() < 24);
|
||||
try std.testing.expect(t.minute() < 60);
|
||||
try std.testing.expect(t.second() < 60);
|
||||
}
|
||||
test timeZoneId {
|
||||
if (true) return error.Todo;
|
||||
const tz = timeZoneId();
|
||||
try std.testing.expectEqualStrings("UTC", tz);
|
||||
}
|
||||
test zonedDateTimeISO {
|
||||
if (true) return error.Todo;
|
||||
try std.testing.expectError(error.TemporalNotImplemented, zonedDateTimeISO());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,148 +1,497 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal = @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");
|
||||
|
||||
const PlainDate = @This();
|
||||
|
||||
pub const Unit = temporal.Unit;
|
||||
pub const RoundingMode = temporal.RoundingMode;
|
||||
pub const Sign = temporal.Sign;
|
||||
pub const DifferenceSettings = temporal.DifferenceSettings;
|
||||
|
||||
pub const ToStringOptions = struct {
|
||||
calendar_id: ?CalendarDisplay = null,
|
||||
};
|
||||
|
||||
pub const CalendarDisplay = enum {
|
||||
auto,
|
||||
always,
|
||||
never,
|
||||
critical,
|
||||
|
||||
fn toCApi(self: CalendarDisplay) abi.c.ShowCalendar {
|
||||
return switch (self) {
|
||||
.auto => .Auto,
|
||||
.always => .Always,
|
||||
.never => .Never,
|
||||
.critical => .Critical,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ToZonedDateTimeOptions = struct {
|
||||
time_zone: []const u8,
|
||||
plain_time: ?PlainTime = null,
|
||||
};
|
||||
|
||||
_inner: *abi.c.PlainDate,
|
||||
calendar_id: []const u8,
|
||||
day: i64 = 0,
|
||||
day_of_week: i64,
|
||||
day_of_year: i64,
|
||||
days_in_month: i64,
|
||||
days_in_week: i64,
|
||||
days_in_year: i64,
|
||||
era: []const u8,
|
||||
era_year: i64,
|
||||
in_leap_year: bool,
|
||||
month: i64,
|
||||
month_code: []const u8,
|
||||
months_in_year: i64,
|
||||
week_of_year: i64,
|
||||
year: i64,
|
||||
year_of_week: i64,
|
||||
|
||||
pub fn init() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate {
|
||||
return initWithCalendar(year_val, month_val, day_val, "iso8601");
|
||||
}
|
||||
|
||||
pub fn compare() error{Todo}!i8 {
|
||||
return error.Todo;
|
||||
pub fn initWithCalendar(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 = abi.success(cal_result) orelse return error.TemporalError;
|
||||
return wrapPlainDate(abi.c.temporal_rs_PlainDate_try_new(year_val, month_val, day_val, cal_kind));
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
inline fn fromUtf8(text: []const u8) !PlainDate {
|
||||
const view = abi.toDiplomatStringView(text);
|
||||
return wrapPlainDate(abi.c.temporal_rs_PlainDate_from_utf8(view));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
inline fn fromUtf16(text: []const u16) !PlainDate {
|
||||
const view = abi.toDiplomatString16View(text);
|
||||
return wrapPlainDate(abi.c.temporal_rs_PlainDate_from_utf16(view));
|
||||
}
|
||||
|
||||
pub fn since() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
pub fn compare(a: PlainDate, b: PlainDate) i8 {
|
||||
return abi.c.temporal_rs_PlainDate_compare(a._inner, b._inner);
|
||||
}
|
||||
|
||||
pub fn subtract() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
pub fn equals(self: PlainDate, other: PlainDate) bool {
|
||||
return abi.c.temporal_rs_PlainDate_equals(self._inner, other._inner);
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn toPlainDateTime() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
pub fn until(self: PlainDate, other: PlainDate, settings: DifferenceSettings) !Duration {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_until(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toPlainMonthDay() error{Todo}!PlainMonthDay {
|
||||
return error.Todo;
|
||||
pub fn since(self: PlainDate, other: PlainDate, settings: DifferenceSettings) !Duration {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_since(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toPlainYearMonth() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
pub fn with(self: PlainDate, fields: anytype) !PlainDate {
|
||||
_ = self;
|
||||
_ = fields;
|
||||
return error.TemporalNotImplemented;
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
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 = abi.success(cal_result) orelse return error.TemporalError;
|
||||
const ptr = abi.c.temporal_rs_PlainDate_with_calendar(self._inner, cal_kind) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
pub fn toZonedDateTime() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
pub fn toPlainDateTime(self: PlainDate, time: ?PlainTime) !PlainDateTime {
|
||||
const time_ptr = if (time) |t| t._inner else null;
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_to_plain_date_time(self._inner, time_ptr)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn until() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
pub fn toPlainMonthDay(self: PlainDate) !PlainMonthDay {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_to_plain_month_day(self._inner)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
pub fn toPlainYearMonth(self: PlainDate) !PlainYearMonth {
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_to_plain_year_month(self._inner)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
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 = abi.success(tz_result) orelse return error.TemporalError;
|
||||
|
||||
const time_ptr = if (options.plain_time) |t| t._inner else null;
|
||||
const ptr = (abi.success(abi.c.temporal_rs_PlainDate_to_zoned_date_time(self._inner, time_zone, time_ptr)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
// Get time zone identifier for ZonedDateTime
|
||||
const allocator = std.heap.page_allocator;
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
abi.c.temporal_rs_TimeZone_identifier(time_zone, &write.inner);
|
||||
const tz_id = try write.toOwnedSlice();
|
||||
|
||||
return .{ ._inner = ptr, .time_zone_id = tz_id, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn withCalendar() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
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();
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test compare {
|
||||
if (true) return error.Todo;
|
||||
pub fn toJSON(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
|
||||
return self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
pub fn toLocaleString(self: PlainDate, allocator: std.mem.Allocator) ![]u8 {
|
||||
_ = self;
|
||||
_ = allocator;
|
||||
return error.TemporalNotImplemented;
|
||||
}
|
||||
|
||||
pub fn valueOf(self: PlainDate) !void {
|
||||
_ = self;
|
||||
return error.TemporalValueOfNotSupported;
|
||||
}
|
||||
|
||||
pub fn day(self: PlainDate) u8 {
|
||||
return abi.c.temporal_rs_PlainDate_day(self._inner);
|
||||
}
|
||||
|
||||
pub fn dayOfWeek(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_day_of_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn dayOfYear(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_day_of_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInMonth(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_days_in_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInWeek(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_days_in_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInYear(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_days_in_year(self._inner);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn month(self: PlainDate) u8 {
|
||||
return abi.c.temporal_rs_PlainDate_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn monthsInYear(self: PlainDate) u16 {
|
||||
return abi.c.temporal_rs_PlainDate_months_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn year(self: PlainDate) i32 {
|
||||
return abi.c.temporal_rs_PlainDate_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn inLeapYear(self: PlainDate) bool {
|
||||
return abi.c.temporal_rs_PlainDate_in_leap_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn era(self: PlainDate, allocator: std.mem.Allocator) !?[]u8 {
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
const has_era = abi.c.temporal_rs_PlainDate_era(self._inner, &write.inner);
|
||||
if (!has_era) return null;
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn eraYear(self: PlainDate) ?u32 {
|
||||
const result = abi.c.temporal_rs_PlainDate_era_year(self._inner);
|
||||
if (!result.is_ok) return null;
|
||||
return result.unnamed_0.ok;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fn clone(self: PlainDate) PlainDate {
|
||||
const ptr = abi.c.temporal_rs_PlainDate_clone(self._inner) orelse unreachable;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn deinit(self: PlainDate) void {
|
||||
abi.c.temporal_rs_PlainDate_destroy(self._inner);
|
||||
}
|
||||
|
||||
fn wrapPlainDate(res: anytype) !PlainDate {
|
||||
const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
fn handleVoidResult(res: anytype) !void {
|
||||
_ = abi.success(res) orelse return error.TemporalError;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 add {
|
||||
if (true) return error.Todo;
|
||||
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 since {
|
||||
if (true) return error.Todo;
|
||||
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toPlainDateTime {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toPlainMonthDay {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toPlainYearMonth {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toZonedDateTime {
|
||||
if (true) return error.Todo;
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 {
|
||||
if (true) return error.SkipZigTest; // ZonedDateTime not fully implemented yet
|
||||
// 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 {
|
||||
if (true) return error.Todo;
|
||||
const date = try PlainDate.init(2024, 3, 15);
|
||||
defer date.deinit();
|
||||
|
||||
try std.testing.expectError(error.TemporalNotImplemented, date.with(.{}));
|
||||
}
|
||||
|
||||
test withCalendar {
|
||||
if (true) return error.Todo;
|
||||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal = @import("temporal.zig");
|
||||
|
||||
const PlainDate = @import("PlainDate.zig");
|
||||
const PlainTime = @import("PlainTime.zig");
|
||||
|
|
@ -7,154 +9,593 @@ const Duration = @import("Duration.zig");
|
|||
|
||||
const PlainDateTime = @This();
|
||||
|
||||
_inner: *abi.c.PlainDateTime,
|
||||
calendar_id: []const u8,
|
||||
day: i64,
|
||||
day_of_week: i64,
|
||||
day_of_year: i64,
|
||||
days_in_month: i64,
|
||||
days_in_week: i64,
|
||||
days_in_year: i64,
|
||||
era: []const u8,
|
||||
era_year: i64,
|
||||
hour: i64,
|
||||
in_leap_year: bool,
|
||||
microsecond: i64,
|
||||
millisecond: i64,
|
||||
minute: i64,
|
||||
month: i64,
|
||||
month_code: []const u8,
|
||||
months_in_year: i64,
|
||||
nanosecond: i64,
|
||||
second: i64,
|
||||
week_of_year: i64,
|
||||
year: i64,
|
||||
year_of_week: i64,
|
||||
|
||||
pub fn init() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
// Import types from temporal.zig
|
||||
pub const Unit = temporal.Unit;
|
||||
pub const RoundingMode = temporal.RoundingMode;
|
||||
pub const Sign = temporal.Sign;
|
||||
pub const DifferenceSettings = temporal.DifferenceSettings;
|
||||
pub const RoundOptions = temporal.RoundingOptions;
|
||||
// Type definitions for API compatibility
|
||||
pub const CalendarDisplay = enum {
|
||||
auto,
|
||||
always,
|
||||
never,
|
||||
critical,
|
||||
};
|
||||
|
||||
pub const ToStringOptions = struct {
|
||||
fractional_second_digits: ?u8 = null,
|
||||
smallest_unit: ?Unit = null,
|
||||
rounding_mode: ?RoundingMode = null,
|
||||
calendar_display: ?CalendarDisplay = null,
|
||||
};
|
||||
|
||||
pub const ToZonedDateTimeOptions = struct {
|
||||
timeZone: []const u8,
|
||||
disambiguation: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub const WithOptions = struct {
|
||||
year: ?i32 = null,
|
||||
month: ?u8 = null,
|
||||
day: ?u8 = null,
|
||||
hour: ?u8 = null,
|
||||
minute: ?u8 = null,
|
||||
second: ?u8 = null,
|
||||
millisecond: ?u16 = null,
|
||||
microsecond: ?u16 = null,
|
||||
nanosecond: ?u16 = null,
|
||||
};
|
||||
|
||||
// Constructor - creates a PlainDateTime with all parameters
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn compare() error{Todo}!i8 {
|
||||
return error.Todo;
|
||||
pub fn initWithCalendar(
|
||||
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 = abi.success(cal_result) orelse return error.TemporalError;
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
// Parse from string
|
||||
pub fn from(s: []const u8) !PlainDateTime {
|
||||
return fromUtf8(s);
|
||||
}
|
||||
|
||||
pub fn add() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
fn fromUtf8(utf8: []const u8) !PlainDateTime {
|
||||
const view = abi.toDiplomatStringView(utf8);
|
||||
const parsed = abi.c.temporal_rs_ParsedDateTime_from_utf8(view);
|
||||
const ptr = abi.success(parsed) orelse return error.TemporalError;
|
||||
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_from_parsed(ptr));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
fn fromUtf16(utf16: []const u16) !PlainDateTime {
|
||||
const view = abi.toDiplomatString16View(utf16);
|
||||
const parsed = abi.c.temporal_rs_ParsedDateTime_from_utf16(view);
|
||||
const ptr = abi.success(parsed) orelse return error.TemporalError;
|
||||
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_from_parsed(ptr));
|
||||
}
|
||||
|
||||
pub fn round() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
// Comparison
|
||||
pub fn compare(a: PlainDateTime, b: PlainDateTime) i8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_compare(a._inner, b._inner);
|
||||
}
|
||||
|
||||
pub fn since() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
pub fn equals(self: PlainDateTime, other: PlainDateTime) bool {
|
||||
if (compare(self, other) != 0) return false;
|
||||
return std.mem.eql(u8, self.calendar_id, other.calendar_id);
|
||||
}
|
||||
|
||||
pub fn subtract() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
// Arithmetic
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn until(self: PlainDateTime, other: PlainDateTime, options: DifferenceSettings) !Duration {
|
||||
const result = abi.c.temporal_rs_PlainDateTime_until(self._inner, other._inner, options.toCApi());
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toPlainDate() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
pub fn since(self: PlainDateTime, other: PlainDateTime, options: DifferenceSettings) !Duration {
|
||||
const result = abi.c.temporal_rs_PlainDateTime_since(self._inner, other._inner, options.toCApi());
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toPlainTime() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
// Rounding
|
||||
pub fn round(self: PlainDateTime, options: RoundOptions) !PlainDateTime {
|
||||
return wrapPlainDateTime(abi.c.temporal_rs_PlainDateTime_round(self._inner, options.toCApi()));
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
// Property accessors - Date fields
|
||||
pub fn calendarId(self: PlainDateTime) []const u8 {
|
||||
return self.calendar_id;
|
||||
}
|
||||
|
||||
pub fn toZonedDateTime() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
pub fn day(self: PlainDateTime) u8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_day(self._inner);
|
||||
}
|
||||
|
||||
pub fn until() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
pub fn dayOfWeek(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_day_of_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
pub fn dayOfYear(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_day_of_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
pub fn daysInMonth(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_days_in_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn withCalendar() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
pub fn daysInWeek(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_days_in_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn withPlainTime() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
pub fn daysInYear(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_days_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn month(self: PlainDateTime) u8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_month(self._inner);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn monthsInYear(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_months_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn year(self: PlainDateTime) i32 {
|
||||
return abi.c.temporal_rs_PlainDateTime_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn inLeapYear(self: PlainDateTime) bool {
|
||||
return abi.c.temporal_rs_PlainDateTime_in_leap_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn era(self: PlainDateTime, allocator: std.mem.Allocator) !?[]u8 {
|
||||
const result = abi.c.temporal_rs_PlainDateTime_era(self._inner);
|
||||
if (!result.is_ok) return null;
|
||||
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
abi.c.temporal_rs_Era_to_string(result.unnamed_0.ok, &write.inner);
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn eraYear(self: PlainDateTime) !?u32 {
|
||||
const result = abi.c.temporal_rs_PlainDateTime_era_year(self._inner);
|
||||
if (!result.is_ok) return null;
|
||||
return result.unnamed_0.ok;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Property accessors - Time fields
|
||||
pub fn hour(self: PlainDateTime) u8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_hour(self._inner);
|
||||
}
|
||||
|
||||
pub fn minute(self: PlainDateTime) u8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_minute(self._inner);
|
||||
}
|
||||
|
||||
pub fn second(self: PlainDateTime) u8 {
|
||||
return abi.c.temporal_rs_PlainDateTime_second(self._inner);
|
||||
}
|
||||
|
||||
pub fn millisecond(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_millisecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn microsecond(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_microsecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn nanosecond(self: PlainDateTime) u16 {
|
||||
return abi.c.temporal_rs_PlainDateTime_nanosecond(self._inner);
|
||||
}
|
||||
|
||||
// Modification methods
|
||||
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 cal_view = abi.toDiplomatStringView(self.calendar_id);
|
||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||
const cal_kind = abi.success(cal_result) orelse return error.TemporalError;
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
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 = abi.success(cal_result) orelse return error.TemporalError;
|
||||
const ptr = abi.c.temporal_rs_PlainDateTime_with_calendar(self._inner, cal_kind) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDateTime_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
pub fn withPlainTime(self: PlainDateTime, time: ?PlainTime) !PlainDateTime {
|
||||
const new_hour: u8 = if (time) |t| t.hour() else 0;
|
||||
const new_minute: u8 = if (time) |t| t.minute() else 0;
|
||||
const new_second: u8 = if (time) |t| t.second() else 0;
|
||||
const new_millisecond: u16 = if (time) |t| t.millisecond() else 0;
|
||||
const new_microsecond: u16 = if (time) |t| t.microsecond() else 0;
|
||||
const new_nanosecond: u16 = if (time) |t| t.nanosecond() else 0;
|
||||
|
||||
const cal_view = abi.toDiplomatStringView(self.calendar_id);
|
||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||
const cal_kind = abi.success(cal_result) orelse return error.TemporalError;
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
// Conversion methods
|
||||
pub fn toPlainDate(self: PlainDateTime) !PlainDate {
|
||||
const ptr = abi.c.temporal_rs_PlainDateTime_to_plain_date(self._inner) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
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 = abi.success(tz_result) orelse return error.TemporalError;
|
||||
|
||||
// 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 = (abi.success(abi.c.temporal_rs_PlainDate_to_zoned_date_time(date._inner, time_zone, time._inner)) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_ZonedDateTime_calendar(ptr);
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
pub fn toJSON(self: PlainDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
return try self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
pub fn toLocaleString(self: PlainDateTime, allocator: std.mem.Allocator) []const u8 {
|
||||
const s = self.toString(allocator, .{}) catch return "PlainDateTime.toLocaleString error";
|
||||
return s;
|
||||
}
|
||||
|
||||
pub fn valueOf(self: PlainDateTime) !void {
|
||||
_ = self;
|
||||
return error.ComparisonNotSupported;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
fn clone(self: PlainDateTime) PlainDateTime {
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PlainDateTime) void {
|
||||
_ = self;
|
||||
// The C API manages memory
|
||||
}
|
||||
|
||||
fn wrapPlainDateTime(res: anytype) !PlainDateTime {
|
||||
const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDateTime_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test compare {
|
||||
if (true) return error.Todo;
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 add {
|
||||
if (true) return error.Todo;
|
||||
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 round {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test since {
|
||||
if (true) return error.Todo;
|
||||
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toPlainDate {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toPlainTime {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toZonedDateTime {
|
||||
if (true) return error.Todo;
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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 t = try dt.toPlainTime();
|
||||
try std.testing.expectEqual(@as(u8, 14), t.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), t.minute());
|
||||
try std.testing.expectEqual(@as(u8, 45), t.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 {
|
||||
if (true) return error.Todo;
|
||||
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 {
|
||||
if (true) return error.Todo;
|
||||
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());
|
||||
try std.testing.expectEqualStrings("iso8601", result.calendar_id);
|
||||
}
|
||||
|
||||
test withPlainTime {
|
||||
if (true) return error.Todo;
|
||||
const dt = try PlainDateTime.init(2024, 1, 15, 14, 30, 0, 0, 0, 0);
|
||||
const t = try PlainTime.from("10:15:30");
|
||||
const result = try dt.withPlainTime(t);
|
||||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,268 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal_types = @import("temporal.zig");
|
||||
|
||||
const PlainMonthDay = @This();
|
||||
const PlainDate = @import("PlainDate.zig");
|
||||
|
||||
_inner: *abi.c.PlainMonthDay,
|
||||
calendar_id: []const u8,
|
||||
day: i64,
|
||||
month_code: []const u8,
|
||||
|
||||
pub fn init() error{Todo}!PlainMonthDay {
|
||||
return error.Todo;
|
||||
// Type definitions for API compatibility
|
||||
pub const CalendarDisplay = enum {
|
||||
auto,
|
||||
always,
|
||||
never,
|
||||
critical,
|
||||
};
|
||||
|
||||
pub const ToStringOptions = struct {
|
||||
calendar_display: ?CalendarDisplay = null,
|
||||
};
|
||||
|
||||
pub const WithOptions = struct {
|
||||
month_code: ?[]const u8 = null,
|
||||
day: ?u8 = null,
|
||||
};
|
||||
|
||||
// Helper to wrap PlainMonthDay pointer
|
||||
fn wrapPlainMonthDay(result: anytype) !PlainMonthDay {
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainMonthDay_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!PlainMonthDay {
|
||||
return error.Todo;
|
||||
// Constructor
|
||||
pub fn init(month_val: u8, day_val: u8, calendar: ?[]const u8) !PlainMonthDay {
|
||||
const cal_kind = if (calendar) |cal| blk: {
|
||||
const cal_view = abi.toDiplomatStringView(cal);
|
||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||
break :blk abi.success(cal_result) orelse return error.TemporalError;
|
||||
} else abi.c.AnyCalendarKind_Iso;
|
||||
|
||||
const overflow = abi.c.ArithmeticOverflow_Constrain;
|
||||
const ref_year = abi.c.OptionI32{ .is_ok = false };
|
||||
|
||||
return wrapPlainMonthDay(abi.c.temporal_rs_PlainMonthDay_try_new_with_overflow(
|
||||
month_val,
|
||||
day_val,
|
||||
cal_kind,
|
||||
overflow,
|
||||
ref_year,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
// Parse from string
|
||||
pub fn from(s: []const u8) !PlainMonthDay {
|
||||
return fromUtf8(s);
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
fn fromUtf8(utf8: []const u8) !PlainMonthDay {
|
||||
const view = abi.toDiplomatStringView(utf8);
|
||||
return wrapPlainMonthDay(abi.c.temporal_rs_PlainMonthDay_from_utf8(view));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
fn fromUtf16(utf16: []const u16) !PlainMonthDay {
|
||||
const view = abi.toDiplomatString16View(utf16);
|
||||
return wrapPlainMonthDay(abi.c.temporal_rs_PlainMonthDay_from_utf16(view));
|
||||
}
|
||||
|
||||
pub fn toPlainDate() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
// Comparison
|
||||
pub fn equals(self: PlainMonthDay, other: PlainMonthDay) bool {
|
||||
return abi.c.temporal_rs_PlainMonthDay_equals(self._inner, other._inner);
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
// Property accessors
|
||||
pub fn calendarId(self: PlainMonthDay) []const u8 {
|
||||
return self.calendar_id;
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
pub fn day(self: PlainMonthDay) u8 {
|
||||
return abi.c.temporal_rs_PlainMonthDay_day(self._inner);
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!PlainMonthDay {
|
||||
return error.Todo;
|
||||
pub fn monthCode(self: PlainMonthDay, allocator: std.mem.Allocator) ![]const u8 {
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
abi.c.temporal_rs_PlainMonthDay_month_code(self._inner, &write.inner);
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
// Modification
|
||||
pub fn with(self: PlainMonthDay, options: WithOptions) !PlainMonthDay {
|
||||
// Build month_code view
|
||||
const month_code_view = if (options.month_code) |mc|
|
||||
abi.toDiplomatStringView(mc)
|
||||
else
|
||||
abi.c.DiplomatStringView{ .data = null, .len = 0 };
|
||||
|
||||
// Build partial date with only the fields we want to modify
|
||||
const partial_date = abi.c.PartialDate{
|
||||
.year = .{ .is_ok = false },
|
||||
.month = .{ .is_ok = false },
|
||||
.day = if (options.day) |d| abi.toOption(abi.c.OptionU8, d) else .{ .is_ok = false },
|
||||
.month_code = month_code_view,
|
||||
.era = .{ .data = null, .len = 0 },
|
||||
.era_year = .{ .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 wrapPlainMonthDay(abi.c.temporal_rs_PlainMonthDay_with(
|
||||
self._inner,
|
||||
partial_date,
|
||||
overflow,
|
||||
));
|
||||
}
|
||||
|
||||
// Conversion
|
||||
pub fn toPlainDate(self: PlainMonthDay, year: i32) !PlainDate {
|
||||
const partial_date = abi.c.PartialDate_option{
|
||||
.is_ok = true,
|
||||
.unnamed_0 = .{
|
||||
.ok = .{
|
||||
.year = abi.toOption(abi.c.OptionI32, year),
|
||||
.month = .{ .is_ok = false },
|
||||
.day = .{ .is_ok = false },
|
||||
.month_code = .{ .data = null, .len = 0 },
|
||||
.era = .{ .data = null, .len = 0 },
|
||||
.era_year = .{ .is_ok = false },
|
||||
.calendar = abi.c.AnyCalendarKind_Iso,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = abi.c.temporal_rs_PlainMonthDay_to_plain_date(self._inner, partial_date);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return PlainDate{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
// String conversions
|
||||
pub fn toString(self: PlainMonthDay, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toStringWithOptions(self, allocator, .{});
|
||||
}
|
||||
|
||||
fn toStringWithOptions(self: PlainMonthDay, allocator: std.mem.Allocator, options: ToStringOptions) ![]u8 {
|
||||
_ = options;
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
const display = abi.c.DisplayCalendar_Auto;
|
||||
abi.c.temporal_rs_PlainMonthDay_to_ixdtf_string(self._inner, display, &write.inner);
|
||||
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn toJSON(self: PlainMonthDay, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn toLocaleString(self: PlainMonthDay, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn valueOf(self: PlainMonthDay) !void {
|
||||
_ = self;
|
||||
return error.ValueError;
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
|
||||
test init {
|
||||
const md = try init(12, 25, null);
|
||||
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 from {
|
||||
if (true) return error.Todo;
|
||||
const md = try from("12-25");
|
||||
try std.testing.expectEqual(@as(u8, 25), md.day());
|
||||
}
|
||||
|
||||
test equals {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const md1 = try init(12, 25, null);
|
||||
const md2 = try init(12, 25, null);
|
||||
try std.testing.expect(md1.equals(md2));
|
||||
}
|
||||
|
||||
{
|
||||
const md1 = try init(12, 25, null);
|
||||
const md2 = try init(12, 24, null);
|
||||
try std.testing.expect(!md1.equals(md2));
|
||||
}
|
||||
}
|
||||
|
||||
test toPlainDate {
|
||||
if (true) return error.Todo;
|
||||
const md = try init(12, 25, null);
|
||||
const date = try md.toPlainDate(2024);
|
||||
try std.testing.expectEqual(@as(i32, 2024), date.year());
|
||||
try std.testing.expectEqual(@as(u8, 25), date.day());
|
||||
}
|
||||
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
const md = try init(12, 25, null);
|
||||
const str = try md.toString(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
|
||||
try std.testing.expect(str.len > 0);
|
||||
}
|
||||
|
||||
test toJSON {
|
||||
const md = try init(12, 25, null);
|
||||
const str = try md.toJSON(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
|
||||
try std.testing.expect(str.len > 0);
|
||||
}
|
||||
|
||||
test with {
|
||||
if (true) return error.Todo;
|
||||
// Test modifying day
|
||||
const md1 = try init(12, 25, null);
|
||||
const md2 = try md1.with(.{ .day = 31 });
|
||||
try std.testing.expectEqual(@as(u8, 31), md2.day());
|
||||
|
||||
// Test modifying month_code
|
||||
const md3 = try init(12, 25, null);
|
||||
const md4 = try md3.with(.{ .month_code = "M01" });
|
||||
const month_code = try md4.monthCode(std.testing.allocator);
|
||||
defer std.testing.allocator.free(month_code);
|
||||
try std.testing.expectEqualStrings("M01", month_code);
|
||||
try std.testing.expectEqual(@as(u8, 25), md4.day());
|
||||
|
||||
// Test modifying both
|
||||
const md5 = try init(6, 15, null);
|
||||
const md6 = try md5.with(.{ .month_code = "M12", .day = 1 });
|
||||
const month_code2 = try md6.monthCode(std.testing.allocator);
|
||||
defer std.testing.allocator.free(month_code2);
|
||||
try std.testing.expectEqualStrings("M12", month_code2);
|
||||
try std.testing.expectEqual(@as(u8, 1), md6.day());
|
||||
}
|
||||
|
||||
test toLocaleString {
|
||||
const md = try init(12, 25, null);
|
||||
const str = try md.toLocaleString(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
try std.testing.expect(str.len > 0);
|
||||
try std.testing.expect(std.mem.indexOf(u8, str, "12") != null or std.mem.indexOf(u8, str, "25") != null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,104 +1,359 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal_types = @import("temporal.zig");
|
||||
|
||||
const PlainTime = @This();
|
||||
const Duration = @import("Duration.zig");
|
||||
|
||||
hour: i64,
|
||||
microsecond: i64,
|
||||
millisecond: i64,
|
||||
minute: i64,
|
||||
nanosecond: i64,
|
||||
second: i64,
|
||||
_inner: *abi.c.PlainTime,
|
||||
|
||||
pub fn init() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
// Import types from temporal.zig
|
||||
pub const Unit = temporal_types.Unit;
|
||||
pub const RoundingMode = temporal_types.RoundingMode;
|
||||
pub const DifferenceSettings = temporal_types.DifferenceSettings;
|
||||
pub const RoundOptions = temporal_types.RoundingOptions;
|
||||
|
||||
pub const WithOptions = struct {
|
||||
hour: ?u8 = null,
|
||||
minute: ?u8 = null,
|
||||
second: ?u8 = null,
|
||||
millisecond: ?u16 = null,
|
||||
microsecond: ?u16 = null,
|
||||
nanosecond: ?u16 = null,
|
||||
};
|
||||
|
||||
// Helper to wrap PlainTime pointer
|
||||
fn wrapPlainTime(result: anytype) !PlainTime {
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return PlainTime{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn compare() error{Todo}!i8 {
|
||||
return error.Todo;
|
||||
// Constructor - creates a PlainTime with all parameters
|
||||
pub fn init(
|
||||
hour_val: u8,
|
||||
minute_val: u8,
|
||||
second_val: u8,
|
||||
millisecond_val: u16,
|
||||
microsecond_val: u16,
|
||||
nanosecond_val: u16,
|
||||
) !PlainTime {
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_try_new(
|
||||
hour_val,
|
||||
minute_val,
|
||||
second_val,
|
||||
millisecond_val,
|
||||
microsecond_val,
|
||||
nanosecond_val,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
// Parse from string
|
||||
pub fn from(s: []const u8) !PlainTime {
|
||||
return fromUtf8(s);
|
||||
}
|
||||
|
||||
pub fn add() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
fn fromUtf8(utf8: []const u8) !PlainTime {
|
||||
const view = abi.toDiplomatStringView(utf8);
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_from_utf8(view));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
fn fromUtf16(utf16: []const u16) !PlainTime {
|
||||
const view = abi.toDiplomatString16View(utf16);
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_from_utf16(view));
|
||||
}
|
||||
|
||||
pub fn round() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
// Comparison
|
||||
pub fn compare(a: PlainTime, b: PlainTime) i8 {
|
||||
return abi.c.temporal_rs_PlainTime_compare(a._inner, b._inner);
|
||||
}
|
||||
|
||||
pub fn since() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
pub fn equals(self: PlainTime, other: PlainTime) bool {
|
||||
return abi.c.temporal_rs_PlainTime_equals(self._inner, other._inner);
|
||||
}
|
||||
|
||||
pub fn subtract() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
// Arithmetic
|
||||
pub fn add(self: PlainTime, duration: Duration) !PlainTime {
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_add(self._inner, duration._inner));
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn subtract(self: PlainTime, duration: Duration) !PlainTime {
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_subtract(self._inner, duration._inner));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn until(self: PlainTime, other: PlainTime, options: DifferenceSettings) !Duration {
|
||||
const settings = options.toCApi();
|
||||
const result = abi.c.temporal_rs_PlainTime_until(self._inner, other._inner, settings);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn since(self: PlainTime, other: PlainTime, options: DifferenceSettings) !Duration {
|
||||
const settings = options.toCApi();
|
||||
const result = abi.c.temporal_rs_PlainTime_since(self._inner, other._inner, settings);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn until() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
// Rounding
|
||||
pub fn round(self: PlainTime, options: RoundOptions) !PlainTime {
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_round(self._inner, options.toCApi()));
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
// Property accessors
|
||||
pub fn hour(self: PlainTime) u8 {
|
||||
return abi.c.temporal_rs_PlainTime_hour(self._inner);
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
pub fn minute(self: PlainTime) u8 {
|
||||
return abi.c.temporal_rs_PlainTime_minute(self._inner);
|
||||
}
|
||||
|
||||
pub fn second(self: PlainTime) u8 {
|
||||
return abi.c.temporal_rs_PlainTime_second(self._inner);
|
||||
}
|
||||
|
||||
pub fn millisecond(self: PlainTime) u16 {
|
||||
return abi.c.temporal_rs_PlainTime_millisecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn microsecond(self: PlainTime) u16 {
|
||||
return abi.c.temporal_rs_PlainTime_microsecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn nanosecond(self: PlainTime) u16 {
|
||||
return abi.c.temporal_rs_PlainTime_nanosecond(self._inner);
|
||||
}
|
||||
|
||||
// Modification
|
||||
pub fn with(self: PlainTime, options: WithOptions) !PlainTime {
|
||||
const partial = abi.c.PartialTime{
|
||||
.hour = abi.toOption(abi.c.OptionU8, options.hour),
|
||||
.minute = abi.toOption(abi.c.OptionU8, options.minute),
|
||||
.second = abi.toOption(abi.c.OptionU8, options.second),
|
||||
.millisecond = abi.toOption(abi.c.OptionU16, options.millisecond),
|
||||
.microsecond = abi.toOption(abi.c.OptionU16, options.microsecond),
|
||||
.nanosecond = abi.toOption(abi.c.OptionU16, options.nanosecond),
|
||||
};
|
||||
const overflow_opt = abi.c.ArithmeticOverflow_option{
|
||||
.is_ok = true,
|
||||
.unnamed_0 = .{ .ok = abi.c.ArithmeticOverflow_Constrain },
|
||||
};
|
||||
return wrapPlainTime(abi.c.temporal_rs_PlainTime_with(self._inner, partial, overflow_opt));
|
||||
}
|
||||
|
||||
// String conversions
|
||||
pub fn toString(self: PlainTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toStringWithOptions(self, allocator, .{});
|
||||
}
|
||||
|
||||
fn toStringWithOptions(self: PlainTime, allocator: std.mem.Allocator, options: temporal_types.ToStringRoundingOptions) ![]u8 {
|
||||
const rounding_opts = options.toCApi();
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
const result = abi.c.temporal_rs_PlainTime_to_ixdtf_string(
|
||||
self._inner,
|
||||
rounding_opts,
|
||||
&write.inner,
|
||||
);
|
||||
|
||||
if (!result.is_ok) return error.TemporalError;
|
||||
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn toJSON(self: PlainTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn toLocaleString(self: PlainTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
// For now, just use toString - locale-specific formatting would require more work
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn valueOf(self: PlainTime) !void {
|
||||
_ = self;
|
||||
// PlainTime should not be used in arithmetic/comparison operations implicitly
|
||||
return error.ValueError;
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test compare {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test init {
|
||||
{
|
||||
const time = try init(14, 30, 45, 123, 456, 789);
|
||||
try std.testing.expectEqual(@as(u8, 14), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 45), time.second());
|
||||
try std.testing.expectEqual(@as(u16, 123), time.millisecond());
|
||||
try std.testing.expectEqual(@as(u16, 456), time.microsecond());
|
||||
try std.testing.expectEqual(@as(u16, 789), time.nanosecond());
|
||||
}
|
||||
{
|
||||
const time = try init(0, 0, 0, 0, 0, 0);
|
||||
try std.testing.expectEqual(@as(u8, 0), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 0), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 0), time.second());
|
||||
}
|
||||
{
|
||||
const time = try init(23, 59, 59, 999, 999, 999);
|
||||
try std.testing.expectEqual(@as(u8, 23), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 59), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 59), time.second());
|
||||
}
|
||||
}
|
||||
|
||||
test from {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const time = try from("14:30:45.123456789");
|
||||
try std.testing.expectEqual(@as(u8, 14), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 45), time.second());
|
||||
try std.testing.expectEqual(@as(u16, 123), time.millisecond());
|
||||
try std.testing.expectEqual(@as(u16, 456), time.microsecond());
|
||||
try std.testing.expectEqual(@as(u16, 789), time.nanosecond());
|
||||
}
|
||||
{
|
||||
const time = try from("14:30");
|
||||
try std.testing.expectEqual(@as(u8, 14), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 0), time.second());
|
||||
}
|
||||
|
||||
{
|
||||
const time = try from("14:30:45");
|
||||
try std.testing.expectEqual(@as(u8, 14), time.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), time.minute());
|
||||
try std.testing.expectEqual(@as(u8, 45), time.second());
|
||||
}
|
||||
}
|
||||
test add {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test compare {
|
||||
{
|
||||
const time1 = try init(14, 30, 45, 123, 456, 789);
|
||||
const time2 = try init(14, 30, 45, 123, 456, 789);
|
||||
try std.testing.expectEqual(@as(i8, 0), compare(time1, time2));
|
||||
}
|
||||
{
|
||||
const time1 = try init(14, 30, 45, 123, 456, 789);
|
||||
const time2 = try init(15, 30, 45, 123, 456, 789);
|
||||
try std.testing.expectEqual(@as(i8, -1), compare(time1, time2));
|
||||
}
|
||||
{
|
||||
const time1 = try init(15, 30, 45, 123, 456, 789);
|
||||
const time2 = try init(14, 30, 45, 123, 456, 789);
|
||||
try std.testing.expectEqual(@as(i8, 1), compare(time1, time2));
|
||||
}
|
||||
}
|
||||
|
||||
test equals {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const time1 = try init(14, 30, 45, 123, 456, 789);
|
||||
const time2 = try init(14, 30, 45, 123, 456, 789);
|
||||
try std.testing.expect(time1.equals(time2));
|
||||
}
|
||||
{
|
||||
const time1 = try init(14, 30, 45, 123, 456, 789);
|
||||
const time2 = try init(15, 30, 45, 123, 456, 789);
|
||||
try std.testing.expect(!time1.equals(time2));
|
||||
}
|
||||
}
|
||||
test round {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test since {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test add {
|
||||
{
|
||||
const time = try init(14, 30, 0, 0, 0, 0);
|
||||
const duration = try Duration.from("PT1H30M");
|
||||
const result = try time.add(duration);
|
||||
try std.testing.expectEqual(@as(u8, 16), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 0), result.minute());
|
||||
}
|
||||
{
|
||||
const time = try init(23, 30, 0, 0, 0, 0);
|
||||
const duration = try Duration.from("PT1H");
|
||||
const result = try time.add(duration);
|
||||
try std.testing.expectEqual(@as(u8, 0), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), result.minute());
|
||||
}
|
||||
}
|
||||
|
||||
test subtract {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test until {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const time = try init(16, 0, 0, 0, 0, 0);
|
||||
const duration = try Duration.from("PT1H30M");
|
||||
const result = try time.subtract(duration);
|
||||
try std.testing.expectEqual(@as(u8, 14), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), result.minute());
|
||||
}
|
||||
{
|
||||
const time = try init(0, 30, 0, 0, 0, 0);
|
||||
const duration = try Duration.from("PT1H");
|
||||
const result = try time.subtract(duration);
|
||||
try std.testing.expectEqual(@as(u8, 23), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), result.minute());
|
||||
}
|
||||
}
|
||||
|
||||
test with {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const time = try init(14, 30, 45, 123, 456, 789);
|
||||
const result = try time.with(.{ .hour = 18 });
|
||||
try std.testing.expectEqual(@as(u8, 18), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), result.minute());
|
||||
try std.testing.expectEqual(@as(u8, 45), result.second());
|
||||
}
|
||||
|
||||
{
|
||||
const time = try init(14, 30, 45, 123, 456, 789);
|
||||
const result = try time.with(.{ .hour = 18, .minute = 15, .second = 0 });
|
||||
try std.testing.expectEqual(@as(u8, 18), result.hour());
|
||||
try std.testing.expectEqual(@as(u8, 15), result.minute());
|
||||
try std.testing.expectEqual(@as(u8, 0), result.second());
|
||||
}
|
||||
}
|
||||
|
||||
test toString {
|
||||
const time = try init(14, 30, 45, 123, 456, 789);
|
||||
const str = try time.toString(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
|
||||
// Should be in format: 14:30:45.123456789
|
||||
try std.testing.expect(str.len > 0);
|
||||
try std.testing.expect(std.mem.indexOf(u8, str, "14:30:45") != null);
|
||||
}
|
||||
|
||||
test toJSON {
|
||||
const time = try init(14, 30, 45, 123, 456, 789);
|
||||
const str = try time.toJSON(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
try std.testing.expect(str.len > 0);
|
||||
}
|
||||
|
||||
test round {
|
||||
const time = try init(14, 30, 45, 999, 999, 999);
|
||||
const rounded = try time.round(.{ .smallest_unit = .second });
|
||||
|
||||
try std.testing.expectEqual(@as(u8, 14), rounded.hour());
|
||||
try std.testing.expectEqual(@as(u8, 30), rounded.minute());
|
||||
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 since {
|
||||
const time1 = try init(15, 30, 0, 0, 0, 0);
|
||||
const time2 = try init(14, 0, 0, 0, 0, 0);
|
||||
const dur = try time1.since(time2, .{});
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 1), dur.hours());
|
||||
try std.testing.expectEqual(@as(i64, 30), dur.minutes());
|
||||
}
|
||||
|
||||
test until {
|
||||
const time1 = try init(14, 0, 0, 0, 0, 0);
|
||||
const time2 = try init(15, 30, 0, 0, 0, 0);
|
||||
const dur = try time1.until(time2, .{});
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 1), dur.hours());
|
||||
try std.testing.expectEqual(@as(i64, 30), dur.minutes());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,109 +1,418 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal_types = @import("temporal.zig");
|
||||
|
||||
const PlainYearMonth = @This();
|
||||
const PlainDate = @import("PlainDate.zig");
|
||||
const Duration = @import("Duration.zig");
|
||||
|
||||
_inner: *abi.c.PlainYearMonth,
|
||||
calendar_id: []const u8,
|
||||
days_in_month: i64,
|
||||
days_in_year: i64,
|
||||
era: []const u8,
|
||||
era_year: i64,
|
||||
in_leap_year: bool,
|
||||
month: i64,
|
||||
month_code: []const u8,
|
||||
months_in_year: i64,
|
||||
year: i64,
|
||||
|
||||
pub fn init() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
// Import types from temporal.zig
|
||||
pub const Unit = temporal_types.Unit;
|
||||
pub const RoundingMode = temporal_types.RoundingMode;
|
||||
pub const DifferenceSettings = temporal_types.DifferenceSettings;
|
||||
pub const RoundOptions = temporal_types.RoundingOptions;
|
||||
|
||||
// Type definitions for API compatibility
|
||||
pub const CalendarDisplay = enum {
|
||||
auto,
|
||||
always,
|
||||
never,
|
||||
critical,
|
||||
};
|
||||
|
||||
pub const ToStringOptions = struct {
|
||||
calendar_display: ?CalendarDisplay = null,
|
||||
};
|
||||
|
||||
pub const WithOptions = struct {
|
||||
year: ?i32 = null,
|
||||
month: ?u8 = null,
|
||||
month_code: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
// Helper to wrap PlainYearMonth pointer
|
||||
fn wrapPlainYearMonth(result: anytype) !PlainYearMonth {
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainYearMonth_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
pub fn compare() error{Todo}!i8 {
|
||||
return error.Todo;
|
||||
// Constructor
|
||||
pub fn init(year_val: i32, month_val: u8, calendar: ?[]const u8) !PlainYearMonth {
|
||||
const cal_kind = if (calendar) |cal| blk: {
|
||||
const cal_view = abi.toDiplomatStringView(cal);
|
||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||
break :blk abi.success(cal_result) orelse return error.TemporalError;
|
||||
} else abi.c.AnyCalendarKind_Iso;
|
||||
|
||||
const overflow = abi.c.ArithmeticOverflow_Constrain;
|
||||
const ref_day = abi.c.OptionU8{ .is_ok = false };
|
||||
|
||||
return wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_try_new_with_overflow(
|
||||
year_val,
|
||||
month_val,
|
||||
ref_day,
|
||||
cal_kind,
|
||||
overflow,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
// Parse from string
|
||||
pub fn from(s: []const u8) !PlainYearMonth {
|
||||
return fromUtf8(s);
|
||||
}
|
||||
|
||||
pub fn add() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
fn fromUtf8(utf8: []const u8) !PlainYearMonth {
|
||||
const view = abi.toDiplomatStringView(utf8);
|
||||
return wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_from_utf8(view));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
fn fromUtf16(utf16: []const u16) !PlainYearMonth {
|
||||
const view = abi.toDiplomatString16View(utf16);
|
||||
return wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_from_utf16(view));
|
||||
}
|
||||
|
||||
pub fn since() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
// Comparison
|
||||
pub fn compare(a: PlainYearMonth, b: PlainYearMonth) i8 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_compare(a._inner, b._inner);
|
||||
}
|
||||
|
||||
pub fn subtract() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
pub fn equals(self: PlainYearMonth, other: PlainYearMonth) bool {
|
||||
return abi.c.temporal_rs_PlainYearMonth_equals(self._inner, other._inner);
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
// Arithmetic
|
||||
pub fn add(self: PlainYearMonth, duration: Duration) !PlainYearMonth {
|
||||
const overflow = abi.c.ArithmeticOverflow_Constrain;
|
||||
return wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_add(self._inner, duration._inner, overflow));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn subtract(self: PlainYearMonth, duration: Duration) !PlainYearMonth {
|
||||
const overflow = abi.c.ArithmeticOverflow_Constrain;
|
||||
return wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_subtract(self._inner, duration._inner, overflow));
|
||||
}
|
||||
|
||||
pub fn toPlainDate() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
pub fn until(self: PlainYearMonth, other: PlainYearMonth, options: DifferenceSettings) !Duration {
|
||||
const settings = options.toCApi();
|
||||
const result = abi.c.temporal_rs_PlainYearMonth_until(self._inner, other._inner, settings);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
pub fn since(self: PlainYearMonth, other: PlainYearMonth, options: DifferenceSettings) !Duration {
|
||||
const settings = options.toCApi();
|
||||
const result = abi.c.temporal_rs_PlainYearMonth_since(self._inner, other._inner, settings);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn until() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
// Property accessors
|
||||
pub fn calendarId(self: PlainYearMonth) []const u8 {
|
||||
return self.calendar_id;
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
pub fn year(self: PlainYearMonth) i32 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!PlainYearMonth {
|
||||
return error.Todo;
|
||||
pub fn month(self: PlainYearMonth) u8 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn monthCode(self: PlainYearMonth) ![]const u8 {
|
||||
const allocator = std.heap.page_allocator;
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
abi.c.temporal_rs_PlainYearMonth_month_code(self._inner, &write.inner);
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn daysInMonth(self: PlainYearMonth) u16 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_days_in_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInYear(self: PlainYearMonth) u16 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_days_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn monthsInYear(self: PlainYearMonth) u16 {
|
||||
return abi.c.temporal_rs_PlainYearMonth_months_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn inLeapYear(self: PlainYearMonth) bool {
|
||||
return abi.c.temporal_rs_PlainYearMonth_in_leap_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn era(self: PlainYearMonth) !?[]const u8 {
|
||||
const allocator = std.heap.page_allocator;
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
abi.c.temporal_rs_PlainYearMonth_era(self._inner, &write.inner);
|
||||
const result = try write.toOwnedSlice();
|
||||
if (result.len == 0) return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn eraYear(self: PlainYearMonth) ?i32 {
|
||||
const result = abi.c.temporal_rs_PlainYearMonth_era_year(self._inner);
|
||||
if (!result.is_ok) return null;
|
||||
return result.unnamed_0.ok;
|
||||
}
|
||||
|
||||
// Modification
|
||||
pub fn with(self: PlainYearMonth, options: WithOptions) !PlainYearMonth {
|
||||
// Build month_code view
|
||||
const month_code_view = if (options.month_code) |mc|
|
||||
abi.toDiplomatStringView(mc)
|
||||
else
|
||||
abi.c.DiplomatStringView{ .data = null, .len = 0 };
|
||||
|
||||
// Build partial date with only the fields we want to modify
|
||||
const partial_date = abi.c.PartialDate{
|
||||
.year = if (options.year) |y| abi.toOption(abi.c.OptionI32, y) else .{ .is_ok = false },
|
||||
.month = if (options.month) |m| abi.toOption(abi.c.OptionU8, m) else .{ .is_ok = false },
|
||||
.day = .{ .is_ok = false },
|
||||
.month_code = month_code_view,
|
||||
.era = .{ .data = null, .len = 0 },
|
||||
.era_year = .{ .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 wrapPlainYearMonth(abi.c.temporal_rs_PlainYearMonth_with(
|
||||
self._inner,
|
||||
partial_date,
|
||||
overflow,
|
||||
));
|
||||
}
|
||||
|
||||
// Conversion
|
||||
pub fn toPlainDate(self: PlainYearMonth, day: u8) !PlainDate {
|
||||
const partial_date = abi.c.PartialDate_option{
|
||||
.is_ok = true,
|
||||
.unnamed_0 = .{
|
||||
.ok = .{
|
||||
.year = .{ .is_ok = false },
|
||||
.month = .{ .is_ok = false },
|
||||
.day = abi.toOption(abi.c.OptionU8, day),
|
||||
.month_code = .{ .data = null, .len = 0 },
|
||||
.era = .{ .data = null, .len = 0 },
|
||||
.era_year = .{ .is_ok = false },
|
||||
.calendar = abi.c.AnyCalendarKind_Iso,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = abi.c.temporal_rs_PlainYearMonth_to_plain_date(self._inner, partial_date);
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
|
||||
const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError;
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
|
||||
return PlainDate{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
// String conversions
|
||||
pub fn toString(self: PlainYearMonth, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toStringWithOptions(self, allocator, .{});
|
||||
}
|
||||
|
||||
fn toStringWithOptions(self: PlainYearMonth, allocator: std.mem.Allocator, options: ToStringOptions) ![]u8 {
|
||||
_ = options;
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
const display = abi.c.DisplayCalendar_Auto;
|
||||
abi.c.temporal_rs_PlainYearMonth_to_ixdtf_string(self._inner, display, &write.inner);
|
||||
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn toJSON(self: PlainYearMonth, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn toLocaleString(self: PlainYearMonth, allocator: std.mem.Allocator) ![]u8 {
|
||||
return toString(self, allocator);
|
||||
}
|
||||
|
||||
pub fn valueOf(self: PlainYearMonth) !void {
|
||||
_ = self;
|
||||
return error.ValueError;
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test compare {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test init {
|
||||
const ym = try init(2024, 12, null);
|
||||
try std.testing.expectEqual(@as(i32, 2024), ym.year());
|
||||
try std.testing.expectEqual(@as(u8, 12), ym.month());
|
||||
}
|
||||
|
||||
test from {
|
||||
if (true) return error.Todo;
|
||||
const ym = try from("2024-12");
|
||||
try std.testing.expectEqual(@as(i32, 2024), ym.year());
|
||||
try std.testing.expectEqual(@as(u8, 12), ym.month());
|
||||
}
|
||||
test add {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test compare {
|
||||
{
|
||||
const ym1 = try init(2024, 12, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
try std.testing.expectEqual(@as(i8, 0), compare(ym1, ym2));
|
||||
}
|
||||
{
|
||||
const ym1 = try init(2024, 11, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
try std.testing.expectEqual(@as(i8, -1), compare(ym1, ym2));
|
||||
}
|
||||
|
||||
{
|
||||
const ym1 = try init(2025, 1, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
try std.testing.expectEqual(@as(i8, 1), compare(ym1, ym2));
|
||||
}
|
||||
|
||||
{
|
||||
const ym1 = try init(2024, 12, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
try std.testing.expect(ym1.equals(ym2));
|
||||
}
|
||||
}
|
||||
|
||||
test equals {
|
||||
if (true) return error.Todo;
|
||||
{
|
||||
const ym1 = try init(2024, 12, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
try std.testing.expect(ym1.equals(ym2));
|
||||
}
|
||||
|
||||
{
|
||||
const ym1 = try init(2024, 12, null);
|
||||
const ym2 = try init(2024, 11, null);
|
||||
try std.testing.expect(!ym1.equals(ym2));
|
||||
}
|
||||
}
|
||||
test since {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test add {
|
||||
const ym = try init(2024, 6, null);
|
||||
const duration = try Duration.from("P6M");
|
||||
const result = try ym.add(duration);
|
||||
try std.testing.expectEqual(@as(i32, 2024), result.year());
|
||||
try std.testing.expectEqual(@as(u8, 12), result.month());
|
||||
}
|
||||
|
||||
test subtract {
|
||||
if (true) return error.Todo;
|
||||
const ym = try init(2024, 12, null);
|
||||
const duration = try Duration.from("P6M");
|
||||
const result = try ym.subtract(duration);
|
||||
try std.testing.expectEqual(@as(i32, 2024), result.year());
|
||||
try std.testing.expectEqual(@as(u8, 6), result.month());
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test daysInMonth {
|
||||
const ym = try init(2024, 2, null);
|
||||
try std.testing.expectEqual(@as(u16, 29), ym.daysInMonth()); // 2024 is a leap year
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test daysInYear {
|
||||
const ym = try init(2024, 1, null);
|
||||
try std.testing.expectEqual(@as(u16, 366), ym.daysInYear()); // 2024 is a leap year
|
||||
}
|
||||
|
||||
test monthsInYear {
|
||||
const ym = try init(2024, 1, null);
|
||||
try std.testing.expectEqual(@as(u16, 12), ym.monthsInYear());
|
||||
}
|
||||
|
||||
test inLeapYear {
|
||||
const ym2024 = try init(2024, 1, null);
|
||||
const ym2023 = try init(2023, 1, null);
|
||||
try std.testing.expect(ym2024.inLeapYear());
|
||||
try std.testing.expect(!ym2023.inLeapYear());
|
||||
}
|
||||
|
||||
test toPlainDate {
|
||||
if (true) return error.Todo;
|
||||
const ym = try init(2024, 12, null);
|
||||
const date = try ym.toPlainDate(25);
|
||||
try std.testing.expectEqual(@as(i32, 2024), date.year());
|
||||
try std.testing.expectEqual(@as(u8, 12), date.month());
|
||||
try std.testing.expectEqual(@as(u8, 25), date.day());
|
||||
}
|
||||
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
const ym = try init(2024, 12, null);
|
||||
const str = try ym.toString(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
try std.testing.expect(str.len > 0);
|
||||
try std.testing.expect(std.mem.indexOf(u8, str, "2024") != null);
|
||||
}
|
||||
|
||||
test toJSON {
|
||||
const ym = try init(2024, 12, null);
|
||||
const str = try ym.toJSON(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
try std.testing.expect(str.len > 0);
|
||||
}
|
||||
|
||||
test since {
|
||||
const ym1 = try init(2024, 12, null);
|
||||
const ym2 = try init(2024, 6, null);
|
||||
const dur = try ym1.since(ym2, .{});
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 6), dur.months());
|
||||
}
|
||||
|
||||
test until {
|
||||
if (true) return error.Todo;
|
||||
const ym1 = try init(2024, 6, null);
|
||||
const ym2 = try init(2024, 12, null);
|
||||
const dur = try ym1.until(ym2, .{});
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 6), dur.months());
|
||||
}
|
||||
|
||||
test with {
|
||||
if (true) return error.Todo;
|
||||
// Test modifying year
|
||||
const ym1 = try init(2024, 6, null);
|
||||
const ym2 = try ym1.with(.{ .year = 2025 });
|
||||
try std.testing.expectEqual(@as(i32, 2025), ym2.year());
|
||||
try std.testing.expectEqual(@as(u8, 6), ym2.month());
|
||||
|
||||
// Test modifying month
|
||||
const ym3 = try init(2024, 6, null);
|
||||
const ym4 = try ym3.with(.{ .month = 12 });
|
||||
try std.testing.expectEqual(@as(i32, 2024), ym4.year());
|
||||
try std.testing.expectEqual(@as(u8, 12), ym4.month());
|
||||
|
||||
// Test modifying both
|
||||
const ym5 = try init(2024, 6, null);
|
||||
const ym6 = try ym5.with(.{ .year = 2025, .month = 1 });
|
||||
try std.testing.expectEqual(@as(i32, 2025), ym6.year());
|
||||
try std.testing.expectEqual(@as(u8, 1), ym6.month());
|
||||
}
|
||||
|
||||
test toLocaleString {
|
||||
const ym = try init(2024, 6, null);
|
||||
const str = try ym.toLocaleString(std.testing.allocator);
|
||||
defer std.testing.allocator.free(str);
|
||||
try std.testing.expect(str.len > 0);
|
||||
try std.testing.expect(std.mem.indexOf(u8, str, "2024") != null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
const std = @import("std");
|
||||
const abi = @import("abi.zig");
|
||||
const temporal_types = @import("temporal.zig");
|
||||
|
||||
const Instant = @import("Instant.zig");
|
||||
const PlainDate = @import("PlainDate.zig");
|
||||
|
|
@ -8,188 +10,582 @@ const Duration = @import("Duration.zig");
|
|||
|
||||
const ZonedDateTime = @This();
|
||||
|
||||
_inner: *abi.c.ZonedDateTime,
|
||||
calendar_id: []const u8,
|
||||
day: i64,
|
||||
day_of_week: i64,
|
||||
day_of_year: i64,
|
||||
days_in_month: i64,
|
||||
days_in_week: i64,
|
||||
days_in_year: i64,
|
||||
epoch_milliseconds: i64,
|
||||
epoch_nanoseconds: i128,
|
||||
era: []const u8,
|
||||
era_year: i64,
|
||||
hour: i64,
|
||||
hours_in_day: i64,
|
||||
in_leap_year: bool,
|
||||
microsecond: i64,
|
||||
millisecond: i64,
|
||||
minute: i64,
|
||||
month: i64,
|
||||
month_code: []const u8,
|
||||
months_in_year: i64,
|
||||
nanosecond: i64,
|
||||
offset: []const u8,
|
||||
offset_nanoseconds: i64,
|
||||
second: i64,
|
||||
time_zone_id: []const u8,
|
||||
week_of_year: i64,
|
||||
year: i64,
|
||||
year_of_week: i64,
|
||||
|
||||
pub fn init() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
// Import types from temporal.zig
|
||||
pub const Unit = temporal_types.Unit;
|
||||
pub const RoundingMode = temporal_types.RoundingMode;
|
||||
pub const Sign = temporal_types.Sign;
|
||||
pub const DifferenceSettings = temporal_types.DifferenceSettings;
|
||||
pub const RoundOptions = temporal_types.RoundingOptions;
|
||||
|
||||
pub const TimeZone = struct {
|
||||
_inner: abi.c.TimeZone,
|
||||
|
||||
pub fn init(id: []const u8) !TimeZone {
|
||||
const view = abi.toDiplomatStringView(id);
|
||||
const result = abi.c.temporal_rs_TimeZone_try_from_str(view);
|
||||
const tz = abi.success(result) orelse return error.TemporalError;
|
||||
return .{ ._inner = tz };
|
||||
}
|
||||
|
||||
fn toCApi(self: TimeZone) abi.c.TimeZone {
|
||||
return self._inner;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Disambiguation = enum {
|
||||
compatible,
|
||||
earlier,
|
||||
later,
|
||||
reject,
|
||||
|
||||
fn toCApi(self: Disambiguation) abi.c.Disambiguation {
|
||||
return switch (self) {
|
||||
.compatible => abi.c.Disambiguation_Compatible,
|
||||
.earlier => abi.c.Disambiguation_Earlier,
|
||||
.later => abi.c.Disambiguation_Later,
|
||||
.reject => abi.c.Disambiguation_Reject,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const OffsetDisambiguation = enum {
|
||||
use_offset,
|
||||
prefer_offset,
|
||||
ignore_offset,
|
||||
reject,
|
||||
|
||||
fn toCApi(self: OffsetDisambiguation) abi.c.OffsetDisambiguation {
|
||||
return switch (self) {
|
||||
.use_offset => abi.c.OffsetDisambiguation_Use,
|
||||
.prefer_offset => abi.c.OffsetDisambiguation_Prefer,
|
||||
.ignore_offset => abi.c.OffsetDisambiguation_Ignore,
|
||||
.reject => abi.c.OffsetDisambiguation_Reject,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const CalendarDisplay = enum {
|
||||
auto,
|
||||
always,
|
||||
never,
|
||||
critical,
|
||||
|
||||
fn toCApi(self: CalendarDisplay) abi.c.DisplayCalendar {
|
||||
return switch (self) {
|
||||
.auto => abi.c.DisplayCalendar_Auto,
|
||||
.always => abi.c.DisplayCalendar_Always,
|
||||
.never => abi.c.DisplayCalendar_Never,
|
||||
.critical => abi.c.DisplayCalendar_Critical,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const DisplayOffset = enum {
|
||||
auto,
|
||||
never,
|
||||
|
||||
fn toCApi(self: DisplayOffset) abi.c.DisplayOffset {
|
||||
return switch (self) {
|
||||
.auto => abi.c.DisplayOffset_Auto,
|
||||
.never => abi.c.DisplayOffset_Never,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const DisplayTimeZone = enum {
|
||||
auto,
|
||||
never,
|
||||
critical,
|
||||
|
||||
fn toCApi(self: DisplayTimeZone) abi.c.DisplayTimeZone {
|
||||
return switch (self) {
|
||||
.auto => abi.c.DisplayTimeZone_Auto,
|
||||
.never => abi.c.DisplayTimeZone_Never,
|
||||
.critical => abi.c.DisplayTimeZone_Critical,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ToStringOptions = struct {
|
||||
fractional_second_digits: ?u8 = null,
|
||||
smallest_unit: ?Unit = null,
|
||||
rounding_mode: ?RoundingMode = null,
|
||||
calendar_display: CalendarDisplay = .auto,
|
||||
offset_display: DisplayOffset = .auto,
|
||||
time_zone_name: DisplayTimeZone = .auto,
|
||||
};
|
||||
|
||||
/// Helper function to wrap a C API result into a ZonedDateTime
|
||||
fn wrapZonedDateTime(result: anytype) !ZonedDateTime {
|
||||
const ptr = (abi.success(result) orelse return error.TemporalError) orelse return error.TemporalError;
|
||||
const calendar_ptr = abi.c.temporal_rs_ZonedDateTime_calendar(ptr);
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.c_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
|
||||
pub fn compare() error{Todo}!i8 {
|
||||
return error.Todo;
|
||||
/// Create a ZonedDateTime from epoch nanoseconds
|
||||
pub fn init(epoch_ns: i128, time_zone: TimeZone) !ZonedDateTime {
|
||||
const ns_parts = abi.toI128Nanoseconds(epoch_ns);
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_from_epoch_nanoseconds(ns_parts, time_zone.toCApi()));
|
||||
}
|
||||
|
||||
pub fn from() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Create from epoch milliseconds
|
||||
pub fn fromEpochMilliseconds(epoch_ms: i64, time_zone: TimeZone) !ZonedDateTime {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_from_epoch_milliseconds(epoch_ms, time_zone.toCApi()));
|
||||
}
|
||||
|
||||
pub fn add() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Create from epoch nanoseconds
|
||||
pub fn fromEpochNanoseconds(epoch_ns: i128, time_zone: TimeZone) !ZonedDateTime {
|
||||
const ns_parts = abi.toI128Nanoseconds(epoch_ns);
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_from_epoch_nanoseconds(ns_parts, time_zone.toCApi()));
|
||||
}
|
||||
|
||||
pub fn equals() error{Todo}!bool {
|
||||
return error.Todo;
|
||||
/// Parse from string
|
||||
pub fn from(s: []const u8, time_zone: ?TimeZone, disambiguation: Disambiguation, offset_disambiguation: OffsetDisambiguation) !ZonedDateTime {
|
||||
_ = time_zone; // The time zone is parsed from the string
|
||||
const view = abi.toDiplomatStringView(s);
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_from_utf8(view, disambiguation.toCApi(), offset_disambiguation.toCApi()));
|
||||
}
|
||||
|
||||
pub fn getTimeZoneTransition() error{Todo}!?Instant {
|
||||
return error.Todo;
|
||||
/// Compare two ZonedDateTime instances
|
||||
pub fn compare(a: ZonedDateTime, b: ZonedDateTime) i8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_compare_instant(a._inner, b._inner);
|
||||
}
|
||||
|
||||
pub fn round() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Add a duration
|
||||
pub fn add(self: ZonedDateTime, duration: Duration) !ZonedDateTime {
|
||||
const overflow_opt = abi.toOption(abi.c.ArithmeticOverflow_option, null);
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_add(self._inner, duration._inner, overflow_opt));
|
||||
}
|
||||
|
||||
pub fn since() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
/// Check equality
|
||||
pub fn equals(self: ZonedDateTime, other: ZonedDateTime) bool {
|
||||
return abi.c.temporal_rs_ZonedDateTime_equals(self._inner, other._inner);
|
||||
}
|
||||
|
||||
pub fn startOfDay() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Get the time zone transition
|
||||
pub fn getTimeZoneTransition(self: ZonedDateTime, direction: enum { next, previous }) !?ZonedDateTime {
|
||||
const dir = switch (direction) {
|
||||
.next => abi.c.TransitionDirection_Next,
|
||||
.previous => abi.c.TransitionDirection_Previous,
|
||||
};
|
||||
const result = abi.c.temporal_rs_ZonedDateTime_get_time_zone_transition(self._inner, dir);
|
||||
const maybe_ptr = abi.success(result) orelse return error.TemporalError;
|
||||
if (maybe_ptr) |ptr| {
|
||||
const calendar_ptr = abi.c.temporal_rs_ZonedDateTime_calendar(ptr);
|
||||
const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr);
|
||||
const allocator = std.heap.c_allocator;
|
||||
const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601";
|
||||
return .{ ._inner = ptr, .calendar_id = cal_id };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn subtract() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Round to the given options
|
||||
pub fn round(self: ZonedDateTime, options: RoundOptions) !ZonedDateTime {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_round(self._inner, options.toCApi()));
|
||||
}
|
||||
|
||||
pub fn toInstant() error{Todo}!Instant {
|
||||
return error.Todo;
|
||||
/// Calculate duration since another ZonedDateTime
|
||||
pub fn since(self: ZonedDateTime, other: ZonedDateTime, settings: DifferenceSettings) !Duration {
|
||||
const ptr = abi.success(abi.c.temporal_rs_ZonedDateTime_since(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn toJSON() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
/// Get the start of the day
|
||||
pub fn startOfDay(self: ZonedDateTime) !ZonedDateTime {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_start_of_day(self._inner));
|
||||
}
|
||||
|
||||
pub fn toLocaleString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
/// Subtract a duration
|
||||
pub fn subtract(self: ZonedDateTime, duration: Duration) !ZonedDateTime {
|
||||
const overflow_opt = abi.toOption(abi.c.ArithmeticOverflow_option, null);
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_subtract(self._inner, duration._inner, overflow_opt));
|
||||
}
|
||||
|
||||
pub fn toPlainDate() error{Todo}!PlainDate {
|
||||
return error.Todo;
|
||||
/// Convert to Instant
|
||||
pub fn toInstant(self: ZonedDateTime) !Instant {
|
||||
const instant_ptr = abi.c.temporal_rs_ZonedDateTime_to_instant(self._inner) orelse return error.TemporalError;
|
||||
const epoch_ms = abi.c.temporal_rs_Instant_epoch_milliseconds(instant_ptr);
|
||||
const epoch_ns_parts = abi.c.temporal_rs_Instant_epoch_nanoseconds(instant_ptr);
|
||||
const epoch_ns = abi.fromI128Nanoseconds(epoch_ns_parts);
|
||||
return .{ ._inner = instant_ptr, .epoch_milliseconds = epoch_ms, .epoch_nanoseconds = epoch_ns };
|
||||
}
|
||||
|
||||
pub fn toPlainDateTime() error{Todo}!PlainDateTime {
|
||||
return error.Todo;
|
||||
/// Convert to JSON string (ISO 8601 format)
|
||||
pub fn toJSON(self: ZonedDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
return self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
pub fn toPlainTime() error{Todo}!PlainTime {
|
||||
return error.Todo;
|
||||
/// Convert to locale string (placeholder - returns ISO string)
|
||||
pub fn toLocaleString(self: ZonedDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
return self.toString(allocator, .{});
|
||||
}
|
||||
|
||||
pub fn toString() error{Todo}![]const u8 {
|
||||
return error.Todo;
|
||||
/// Convert to PlainDate
|
||||
pub fn toPlainDate(self: ZonedDateTime) !PlainDate {
|
||||
const ptr = abi.c.temporal_rs_ZonedDateTime_to_plain_date(self._inner) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn until() error{Todo}!Duration {
|
||||
return error.Todo;
|
||||
/// Convert to PlainDateTime
|
||||
pub fn toPlainDateTime(self: ZonedDateTime) !PlainDateTime {
|
||||
const ptr = abi.c.temporal_rs_ZonedDateTime_to_plain_datetime(self._inner) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
pub fn valueOf() error{Todo}!void {
|
||||
return error.Todo;
|
||||
/// Convert to PlainTime
|
||||
pub fn toPlainTime(self: ZonedDateTime) !PlainTime {
|
||||
const ptr = abi.c.temporal_rs_ZonedDateTime_to_plain_time(self._inner) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn with() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Convert to string with options
|
||||
pub fn toString(self: ZonedDateTime, allocator: std.mem.Allocator, opts: ToStringOptions) ![]u8 {
|
||||
const smallest = if (opts.smallest_unit) |u| abi.toUnitOption(u.toCApi()) else abi.toUnitOption(null);
|
||||
const rounding = if (opts.rounding_mode) |r| abi.toRoundingModeOption(r.toCApi()) else abi.toRoundingModeOption(null);
|
||||
|
||||
const precision: abi.c.Precision = if (opts.fractional_second_digits) |digits|
|
||||
.{ .is_minute = false, .precision = abi.toOption(abi.c.OptionU8, digits) }
|
||||
else
|
||||
.{ .is_minute = true, .precision = abi.toOption(abi.c.OptionU8, null) };
|
||||
|
||||
const rounding_opts = abi.c.ToStringRoundingOptions{
|
||||
.precision = precision,
|
||||
.smallest_unit = smallest,
|
||||
.rounding_mode = rounding,
|
||||
};
|
||||
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
|
||||
_ = abi.c.temporal_rs_ZonedDateTime_to_ixdtf_string(
|
||||
self._inner,
|
||||
opts.offset_display.toCApi(),
|
||||
opts.time_zone_name.toCApi(),
|
||||
opts.calendar_display.toCApi(),
|
||||
rounding_opts,
|
||||
&write.inner,
|
||||
);
|
||||
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn withCalendar() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Calculate duration until another ZonedDateTime
|
||||
pub fn until(self: ZonedDateTime, other: ZonedDateTime, settings: DifferenceSettings) !Duration {
|
||||
const ptr = abi.success(abi.c.temporal_rs_ZonedDateTime_until(self._inner, other._inner, settings.toCApi())) orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn withPlainTime() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// valueOf() is not supported for ZonedDateTime
|
||||
pub fn valueOf(_: ZonedDateTime) !void {
|
||||
return error.ValueOfNotSupported;
|
||||
}
|
||||
|
||||
pub fn withTimeZone() error{Todo}!ZonedDateTime {
|
||||
return error.Todo;
|
||||
/// Create a new ZonedDateTime with some fields changed
|
||||
pub fn with(self: ZonedDateTime, allocator: std.mem.Allocator, fields: anytype) !ZonedDateTime {
|
||||
_ = allocator;
|
||||
_ = fields;
|
||||
_ = self;
|
||||
return error.Todo; // Need PartialZonedDateTime mapping
|
||||
}
|
||||
|
||||
/// Create a new ZonedDateTime with a different calendar
|
||||
pub fn withCalendar(self: ZonedDateTime, calendar: []const u8) !ZonedDateTime {
|
||||
const cal_view = abi.toDiplomatStringView(calendar);
|
||||
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
|
||||
const cal_kind = abi.success(cal_result) orelse return error.TemporalError;
|
||||
const ptr = abi.c.temporal_rs_ZonedDateTime_with_calendar(self._inner, cal_kind);
|
||||
return .{ ._inner = ptr, .calendar_id = calendar };
|
||||
}
|
||||
|
||||
/// Create a new ZonedDateTime with a different time
|
||||
pub fn withPlainTime(self: ZonedDateTime, time: ?PlainTime) !ZonedDateTime {
|
||||
const time_ptr = if (time) |t| t._inner else null;
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_with_plain_time(self._inner, time_ptr));
|
||||
}
|
||||
|
||||
/// Create a new ZonedDateTime with a different time zone
|
||||
pub fn withTimeZone(self: ZonedDateTime, time_zone: TimeZone) !ZonedDateTime {
|
||||
return wrapZonedDateTime(abi.c.temporal_rs_ZonedDateTime_with_timezone(self._inner, time_zone.toCApi()));
|
||||
}
|
||||
|
||||
// Property accessors
|
||||
pub fn calendarId(self: ZonedDateTime) []const u8 {
|
||||
return self.calendar_id;
|
||||
}
|
||||
|
||||
pub fn day(self: ZonedDateTime) u8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_day(self._inner);
|
||||
}
|
||||
|
||||
pub fn dayOfWeek(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_day_of_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn dayOfYear(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_day_of_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInMonth(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_days_in_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInWeek(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_days_in_week(self._inner);
|
||||
}
|
||||
|
||||
pub fn daysInYear(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_days_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn epochMilliseconds(self: ZonedDateTime) i64 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_epoch_milliseconds(self._inner);
|
||||
}
|
||||
|
||||
pub fn epochNanoseconds(self: ZonedDateTime) i128 {
|
||||
const parts = abi.c.temporal_rs_ZonedDateTime_epoch_nanoseconds(self._inner);
|
||||
return abi.fromI128Nanoseconds(parts);
|
||||
}
|
||||
|
||||
pub fn era(self: ZonedDateTime, allocator: std.mem.Allocator) !?[]u8 {
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
abi.c.temporal_rs_ZonedDateTime_era(self._inner, &write.inner);
|
||||
const result = try write.toOwnedSlice();
|
||||
if (result.len == 0) {
|
||||
allocator.free(result);
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn eraYear(self: ZonedDateTime) ?i32 {
|
||||
const result = abi.c.temporal_rs_ZonedDateTime_era_year(self._inner);
|
||||
return abi.fromOption(result);
|
||||
}
|
||||
|
||||
pub fn hour(self: ZonedDateTime) u8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_hour(self._inner);
|
||||
}
|
||||
|
||||
pub fn hoursInDay(self: ZonedDateTime) !f64 {
|
||||
const result = abi.c.temporal_rs_ZonedDateTime_hours_in_day(self._inner);
|
||||
return abi.success(result) orelse error.TemporalError;
|
||||
}
|
||||
|
||||
pub fn inLeapYear(self: ZonedDateTime) bool {
|
||||
return abi.c.temporal_rs_ZonedDateTime_in_leap_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn microsecond(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_microsecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn millisecond(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_millisecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn minute(self: ZonedDateTime) u8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_minute(self._inner);
|
||||
}
|
||||
|
||||
pub fn month(self: ZonedDateTime) u8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_month(self._inner);
|
||||
}
|
||||
|
||||
pub fn monthCode(self: ZonedDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
abi.c.temporal_rs_ZonedDateTime_month_code(self._inner, &write.inner);
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn monthsInYear(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_months_in_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn nanosecond(self: ZonedDateTime) u16 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_nanosecond(self._inner);
|
||||
}
|
||||
|
||||
pub fn offset(self: ZonedDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
var write = abi.DiplomatWrite.init(allocator);
|
||||
defer write.deinit();
|
||||
const res = abi.c.temporal_rs_ZonedDateTime_offset(self._inner, &write.inner);
|
||||
_ = abi.success(res) orelse return error.TemporalError;
|
||||
return try write.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn offsetNanoseconds(self: ZonedDateTime) i64 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_offset_nanoseconds(self._inner);
|
||||
}
|
||||
|
||||
pub fn second(self: ZonedDateTime) u8 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_second(self._inner);
|
||||
}
|
||||
|
||||
pub fn timeZoneId(self: ZonedDateTime, allocator: std.mem.Allocator) ![]u8 {
|
||||
const tz = abi.c.temporal_rs_ZonedDateTime_timezone(self._inner);
|
||||
const view = abi.fromDiplomatStringView(tz.id);
|
||||
return try allocator.dupe(u8, view);
|
||||
}
|
||||
|
||||
pub fn weekOfYear(self: ZonedDateTime) ?u8 {
|
||||
const result = abi.c.temporal_rs_ZonedDateTime_week_of_year(self._inner);
|
||||
return abi.fromOption(result);
|
||||
}
|
||||
|
||||
pub fn year(self: ZonedDateTime) i32 {
|
||||
return abi.c.temporal_rs_ZonedDateTime_year(self._inner);
|
||||
}
|
||||
|
||||
pub fn yearOfWeek(self: ZonedDateTime) ?i32 {
|
||||
const result = abi.c.temporal_rs_ZonedDateTime_year_of_week(self._inner);
|
||||
return abi.fromOption(result);
|
||||
}
|
||||
|
||||
/// Clone this ZonedDateTime
|
||||
pub fn clone(self: ZonedDateTime) ZonedDateTime {
|
||||
const ptr = abi.c.temporal_rs_ZonedDateTime_clone(self._inner);
|
||||
return .{ ._inner = ptr, .calendar_id = self.calendar_id };
|
||||
}
|
||||
|
||||
/// Free the ZonedDateTime
|
||||
pub fn deinit(self: ZonedDateTime) void {
|
||||
abi.c.temporal_rs_ZonedDateTime_destroy(self._inner);
|
||||
if (self.calendar_id.len > 0) {
|
||||
std.heap.c_allocator.free(self.calendar_id);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Tests ---------------------
|
||||
test compare {
|
||||
if (true) return error.Todo;
|
||||
test init {
|
||||
const tz = try TimeZone.init("America/New_York");
|
||||
const zdt = try init(0, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 0), zdt.epochMilliseconds());
|
||||
}
|
||||
|
||||
test fromEpochMilliseconds {
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz); // 2021-01-01T00:00:00Z
|
||||
defer zdt.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 1609459200000), zdt.epochMilliseconds());
|
||||
}
|
||||
|
||||
test from {
|
||||
if (true) return error.Todo;
|
||||
const zdt = try from("2021-01-01T00:00:00+00:00[UTC]", null, .compatible, .reject);
|
||||
defer zdt.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i32, 2021), zdt.year());
|
||||
try std.testing.expectEqual(@as(u8, 1), zdt.month());
|
||||
try std.testing.expectEqual(@as(u8, 1), zdt.day());
|
||||
}
|
||||
test add {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test compare {
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt1 = try fromEpochMilliseconds(1000, tz);
|
||||
defer zdt1.deinit();
|
||||
const zdt2 = try fromEpochMilliseconds(2000, tz);
|
||||
defer zdt2.deinit();
|
||||
|
||||
try std.testing.expect(compare(zdt1, zdt2) < 0);
|
||||
try std.testing.expect(compare(zdt2, zdt1) > 0);
|
||||
try std.testing.expect(compare(zdt1, zdt1) == 0);
|
||||
}
|
||||
|
||||
test equals {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test getTimeZoneTransition {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test round {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test since {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test startOfDay {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test subtract {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt1 = try fromEpochMilliseconds(1000, tz);
|
||||
defer zdt1.deinit();
|
||||
const zdt2 = try fromEpochMilliseconds(1000, tz);
|
||||
defer zdt2.deinit();
|
||||
const zdt3 = try fromEpochMilliseconds(2000, tz);
|
||||
defer zdt3.deinit();
|
||||
|
||||
try std.testing.expect(zdt1.equals(zdt2));
|
||||
try std.testing.expect(!zdt1.equals(zdt3));
|
||||
}
|
||||
|
||||
test toInstant {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toJSON {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test toLocaleString {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
const instant = try zdt.toInstant();
|
||||
defer instant.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i64, 1609459200000), instant.epoch_milliseconds);
|
||||
}
|
||||
|
||||
test toPlainDate {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
const pd = try zdt.toPlainDate();
|
||||
defer pd.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i32, 2021), pd.year());
|
||||
try std.testing.expectEqual(@as(u8, 1), pd.month());
|
||||
try std.testing.expectEqual(@as(u8, 1), pd.day());
|
||||
}
|
||||
|
||||
test toPlainDateTime {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
var pdt = try zdt.toPlainDateTime();
|
||||
defer pdt.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i32, 2021), pdt.year());
|
||||
try std.testing.expectEqual(@as(u8, 1), pdt.month());
|
||||
try std.testing.expectEqual(@as(u8, 1), pdt.day());
|
||||
}
|
||||
|
||||
test toPlainTime {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
const pt = try zdt.toPlainTime();
|
||||
defer abi.c.temporal_rs_PlainTime_destroy(pt._inner);
|
||||
|
||||
try std.testing.expectEqual(@as(u8, 0), pt.hour());
|
||||
try std.testing.expectEqual(@as(u8, 0), pt.minute());
|
||||
}
|
||||
|
||||
test toString {
|
||||
if (true) return error.Todo;
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
const str = try zdt.toString(std.testing.allocator, .{});
|
||||
defer std.testing.allocator.free(str);
|
||||
|
||||
try std.testing.expect(str.len > 0);
|
||||
}
|
||||
test until {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test with {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test withCalendar {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test withPlainTime {
|
||||
if (true) return error.Todo;
|
||||
}
|
||||
test withTimeZone {
|
||||
if (true) return error.Todo;
|
||||
|
||||
test "props" {
|
||||
const tz = try TimeZone.init("UTC");
|
||||
const zdt = try fromEpochMilliseconds(1609459200000, tz);
|
||||
defer zdt.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i32, 2021), zdt.year());
|
||||
try std.testing.expectEqual(@as(u8, 1), zdt.month());
|
||||
try std.testing.expectEqual(@as(u8, 1), zdt.day());
|
||||
try std.testing.expectEqual(@as(u8, 0), zdt.hour());
|
||||
try std.testing.expectEqual(@as(u8, 0), zdt.minute());
|
||||
try std.testing.expectEqual(@as(u8, 0), zdt.second());
|
||||
try std.testing.expectEqual(@as(u16, 0), zdt.millisecond());
|
||||
}
|
||||
|
|
|
|||
67
src/root.zig
67
src/root.zig
|
|
@ -113,6 +113,7 @@ test Instant {
|
|||
|
||||
// Public types
|
||||
"ToStringOptions",
|
||||
"TimeZone",
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"Sign",
|
||||
|
|
@ -140,7 +141,8 @@ test Now {
|
|||
test PlainDate {
|
||||
const checks = .{
|
||||
// Constructor
|
||||
"init", // Temporal.PlainDate()
|
||||
"init",
|
||||
"initWithCalendar",
|
||||
|
||||
// Static methods
|
||||
"compare",
|
||||
|
|
@ -163,7 +165,7 @@ test PlainDate {
|
|||
"with",
|
||||
"withCalendar",
|
||||
|
||||
// Properties
|
||||
// Properties (now as methods)
|
||||
"calendarId",
|
||||
"day",
|
||||
"dayOfWeek",
|
||||
|
|
@ -180,6 +182,15 @@ test PlainDate {
|
|||
"weekOfYear",
|
||||
"year",
|
||||
"yearOfWeek",
|
||||
|
||||
// Public types
|
||||
"ToStringOptions",
|
||||
"CalendarDisplay",
|
||||
"ToZonedDateTimeOptions",
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"Sign",
|
||||
"DifferenceSettings",
|
||||
};
|
||||
|
||||
try assertDecls(PlainDate, checks);
|
||||
|
|
@ -189,10 +200,13 @@ test PlainDateTime {
|
|||
const checks = .{
|
||||
// Constructor
|
||||
"init", // Temporal.PlainDateTime()
|
||||
"initWithCalendar",
|
||||
|
||||
// Static methods
|
||||
"compare",
|
||||
"from",
|
||||
// "fromUtf8",
|
||||
// "fromUtf16",
|
||||
|
||||
// Instance methods
|
||||
"add",
|
||||
|
|
@ -235,6 +249,17 @@ test PlainDateTime {
|
|||
"weekOfYear",
|
||||
"year",
|
||||
"yearOfWeek",
|
||||
|
||||
// Public types
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"Sign",
|
||||
"CalendarDisplay",
|
||||
"DifferenceSettings",
|
||||
"RoundOptions",
|
||||
"ToStringOptions",
|
||||
"ToZonedDateTimeOptions",
|
||||
"WithOptions",
|
||||
};
|
||||
|
||||
try assertDecls(PlainDateTime, checks);
|
||||
|
|
@ -261,6 +286,11 @@ test PlainMonthDay {
|
|||
"calendarId",
|
||||
"day",
|
||||
"monthCode",
|
||||
|
||||
// Public types
|
||||
"CalendarDisplay",
|
||||
"ToStringOptions",
|
||||
"WithOptions",
|
||||
};
|
||||
|
||||
try assertDecls(PlainMonthDay, checks);
|
||||
|
|
@ -295,6 +325,13 @@ test PlainTime {
|
|||
"minute",
|
||||
"nanosecond",
|
||||
"second",
|
||||
|
||||
// Public types
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"DifferenceSettings",
|
||||
"RoundOptions",
|
||||
"WithOptions",
|
||||
};
|
||||
|
||||
try assertDecls(PlainTime, checks);
|
||||
|
|
@ -333,6 +370,15 @@ test PlainYearMonth {
|
|||
"monthCode",
|
||||
"monthsInYear",
|
||||
"year",
|
||||
|
||||
// Public types
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"CalendarDisplay",
|
||||
"DifferenceSettings",
|
||||
"RoundOptions",
|
||||
"ToStringOptions",
|
||||
"WithOptions",
|
||||
};
|
||||
|
||||
try assertDecls(PlainYearMonth, checks);
|
||||
|
|
@ -346,9 +392,12 @@ test ZonedDateTime {
|
|||
// Static methods
|
||||
"compare",
|
||||
"from",
|
||||
"fromEpochMilliseconds",
|
||||
"fromEpochNanoseconds",
|
||||
|
||||
// Instance methods
|
||||
"add",
|
||||
"clone",
|
||||
"equals",
|
||||
"getTimeZoneTransition",
|
||||
"round",
|
||||
|
|
@ -398,6 +447,20 @@ test ZonedDateTime {
|
|||
"weekOfYear",
|
||||
"year",
|
||||
"yearOfWeek",
|
||||
|
||||
// Public types
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"Sign",
|
||||
"DifferenceSettings",
|
||||
"RoundOptions",
|
||||
"TimeZone",
|
||||
"Disambiguation",
|
||||
"OffsetDisambiguation",
|
||||
"CalendarDisplay",
|
||||
"DisplayOffset",
|
||||
"DisplayTimeZone",
|
||||
"ToStringOptions",
|
||||
};
|
||||
|
||||
try assertDecls(ZonedDateTime, checks);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue