feat: add local timezone
This commit is contained in:
parent
ac36792fdc
commit
6d5dca2ffd
3 changed files with 254 additions and 94 deletions
|
|
@ -53,10 +53,10 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
|
||||||
if (io_optional) |io| {
|
if (io_optional) |io| {
|
||||||
const now_instant = try Temporal.Now.instant(io);
|
const now_instant = try Temporal.Now.instant(io);
|
||||||
defer now_instant.deinit();
|
defer now_instant.deinit();
|
||||||
const now_date = try Temporal.Now.plainDateISO(io);
|
const now_date = try Temporal.Now.plainDateISO(allocator, io, null);
|
||||||
defer now_date.deinit();
|
defer now_date.deinit();
|
||||||
const now_datetime = try Temporal.Now.plainDateTimeISO(io);
|
const now_datetime = try Temporal.Now.plainDateTimeISO(allocator, io, null);
|
||||||
const now_time = try Temporal.Now.plainTimeISO(io);
|
const now_time = try Temporal.Now.plainTimeISO(allocator, io, null);
|
||||||
std.log.info(
|
std.log.info(
|
||||||
\\Now
|
\\Now
|
||||||
\\ - instant: {s}
|
\\ - instant: {s}
|
||||||
|
|
@ -186,6 +186,8 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
|
||||||
try zdt.toString(allocator, .{}),
|
try zdt.toString(allocator, .{}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try runTimeZoneExamples(allocator, io_optional, null);
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// More complex Temporal API examples
|
// More complex Temporal API examples
|
||||||
// ----
|
// ----
|
||||||
|
|
@ -523,9 +525,9 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (io_optional) |io| {
|
if (io_optional) |io| {
|
||||||
const now_zdt = try Temporal.Now.zonedDateTimeISO(allocator, io);
|
const now_zdt = try Temporal.Now.zonedDateTimeISO(allocator, io, null);
|
||||||
defer now_zdt.deinit();
|
defer now_zdt.deinit();
|
||||||
const now_tz_id = try Temporal.Now.timeZoneId(allocator);
|
const now_tz_id = try Temporal.Now.timeZoneId(allocator, io);
|
||||||
defer allocator.free(now_tz_id);
|
defer allocator.free(now_tz_id);
|
||||||
std.log.info(
|
std.log.info(
|
||||||
\\Now Coverage
|
\\Now Coverage
|
||||||
|
|
@ -814,6 +816,102 @@ pub fn run(allocator: std.mem.Allocator, io_optional: ?std.Io) !void {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runTimeZoneExamples(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
io_optional: ?std.Io,
|
||||||
|
now_epoch_ms: ?i64,
|
||||||
|
) !void {
|
||||||
|
const epoch_ns: i128 = if (now_epoch_ms) |ms| @as(i128, ms) * 1_000_000 else 1_706_881_530_123_456_789;
|
||||||
|
|
||||||
|
const utc_tz = try Temporal.ZonedDateTime.TimeZone.init("UTC");
|
||||||
|
const ny_tz = try Temporal.ZonedDateTime.TimeZone.init("America/New_York");
|
||||||
|
const dhaka_tz = try Temporal.ZonedDateTime.TimeZone.init("Asia/Dhaka");
|
||||||
|
|
||||||
|
const zdt_utc = try Temporal.ZonedDateTime.fromEpochNanoseconds(epoch_ns, utc_tz);
|
||||||
|
defer zdt_utc.deinit();
|
||||||
|
const zdt_ny = try zdt_utc.withTimeZone(ny_tz);
|
||||||
|
defer zdt_ny.deinit();
|
||||||
|
const zdt_dhaka = try zdt_utc.withTimeZone(dhaka_tz);
|
||||||
|
defer zdt_dhaka.deinit();
|
||||||
|
|
||||||
|
const utc_id = try zdt_utc.timeZoneId(allocator);
|
||||||
|
defer allocator.free(utc_id);
|
||||||
|
const ny_id = try zdt_ny.timeZoneId(allocator);
|
||||||
|
defer allocator.free(ny_id);
|
||||||
|
const dhaka_id = try zdt_dhaka.timeZoneId(allocator);
|
||||||
|
defer allocator.free(dhaka_id);
|
||||||
|
|
||||||
|
std.log.info(
|
||||||
|
\\TimeZone
|
||||||
|
\\ - same instant in UTC ({s}): {s}
|
||||||
|
\\ - same instant in New York ({s}): {s}
|
||||||
|
\\ - same instant in Dhaka ({s}): {s}
|
||||||
|
\\ - UTC offset: {s}
|
||||||
|
\\ - New York offset: {s}
|
||||||
|
\\ - Dhaka offset: {s}
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
utc_id,
|
||||||
|
try zdt_utc.toString(allocator, .{}),
|
||||||
|
ny_id,
|
||||||
|
try zdt_ny.toString(allocator, .{}),
|
||||||
|
dhaka_id,
|
||||||
|
try zdt_dhaka.toString(allocator, .{}),
|
||||||
|
try zdt_utc.offset(allocator),
|
||||||
|
try zdt_ny.offset(allocator),
|
||||||
|
try zdt_dhaka.offset(allocator),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (io_optional) |io| {
|
||||||
|
const system_tz_id = try Temporal.Now.timeZoneId(allocator, io);
|
||||||
|
defer allocator.free(system_tz_id);
|
||||||
|
|
||||||
|
const now_zdt = try Temporal.Now.zonedDateTimeISO(allocator, io, null);
|
||||||
|
defer now_zdt.deinit();
|
||||||
|
const now_zdt_utc = try Temporal.Now.zonedDateTimeISO(allocator, io, "UTC");
|
||||||
|
defer now_zdt_utc.deinit();
|
||||||
|
const now_date = try Temporal.Now.plainDateISO(allocator, io, null);
|
||||||
|
defer now_date.deinit();
|
||||||
|
|
||||||
|
std.log.info(
|
||||||
|
\\TimeZone (system)
|
||||||
|
\\ - system timeZoneId(): {s}
|
||||||
|
\\ - Now.zonedDateTimeISO(): {s}
|
||||||
|
\\ - Now.zonedDateTimeISO("UTC"): {s}
|
||||||
|
\\ - Now.plainDateISO(): {s}
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
system_tz_id,
|
||||||
|
try now_zdt.toString(allocator, .{}),
|
||||||
|
try now_zdt_utc.toString(allocator, .{}),
|
||||||
|
try now_date.toString(allocator, .{}),
|
||||||
|
});
|
||||||
|
} else if (now_epoch_ms) |ms| {
|
||||||
|
const inst = try Temporal.Instant.fromEpochMilliseconds(ms);
|
||||||
|
defer inst.deinit();
|
||||||
|
|
||||||
|
const inst_dhaka_tz = try Temporal.Instant.TimeZone.init("Asia/Dhaka");
|
||||||
|
const inst_utc_tz = try Temporal.Instant.TimeZone.init("UTC");
|
||||||
|
const host_zdt = try inst.toZonedDateTimeISO(inst_dhaka_tz);
|
||||||
|
defer host_zdt.deinit();
|
||||||
|
const host_zdt_utc = try inst.toZonedDateTimeISO(inst_utc_tz);
|
||||||
|
defer host_zdt_utc.deinit();
|
||||||
|
|
||||||
|
std.log.info(
|
||||||
|
\\TimeZone (host epoch)
|
||||||
|
\\ - Instant.toZonedDateTimeISO("Asia/Dhaka"): {s}
|
||||||
|
\\ - Instant.toZonedDateTimeISO("UTC"): {s}
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
try host_zdt.toString(allocator, .{}),
|
||||||
|
try host_zdt_utc.toString(allocator, .{}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern fn consoleLog(ptr: [*]u8, len: u32) void;
|
extern fn consoleLog(ptr: [*]u8, len: u32) void;
|
||||||
pub fn logFn(comptime message_level: std.log.Level, comptime scope: @TypeOf(.enum_literal), comptime format: []const u8, args: anytype) void {
|
pub fn logFn(comptime message_level: std.log.Level, comptime scope: @TypeOf(.enum_literal), comptime format: []const u8, args: anytype) void {
|
||||||
if (builtin.os.tag == .freestanding) {
|
if (builtin.os.tag == .freestanding) {
|
||||||
|
|
|
||||||
172
src/Now.zig
172
src/Now.zig
|
|
@ -1,10 +1,11 @@
|
||||||
const builtin = @import("builtin");
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Instant = @import("Instant.zig");
|
const Instant = @import("Instant.zig");
|
||||||
const PlainDate = @import("PlainDate.zig");
|
const PlainDate = @import("PlainDate.zig");
|
||||||
const PlainDateTime = @import("PlainDateTime.zig");
|
const PlainDateTime = @import("PlainDateTime.zig");
|
||||||
const PlainTime = @import("PlainTime.zig");
|
const PlainTime = @import("PlainTime.zig");
|
||||||
|
const SystemTimeZone = @import("SystemTimeZone.zig");
|
||||||
const ZonedDateTime = @import("ZonedDateTime.zig");
|
const ZonedDateTime = @import("ZonedDateTime.zig");
|
||||||
|
const abi = @import("abi.zig");
|
||||||
|
|
||||||
/// # Temporal.Now
|
/// # Temporal.Now
|
||||||
///
|
///
|
||||||
|
|
@ -38,112 +39,84 @@ pub fn instant(io: std.Io) !Instant {
|
||||||
|
|
||||||
/// Returns the current date as a [`Temporal.PlainDate`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate) object, in the ISO 8601 calendar and the specified time zone.
|
/// Returns the current date as a [`Temporal.PlainDate`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate) object, in the ISO 8601 calendar and the specified time zone.
|
||||||
///
|
///
|
||||||
|
/// When `time_zone_like` is null, the system time zone is used.
|
||||||
|
///
|
||||||
/// See: [Temporal.Now.plainDateISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateISO)
|
/// See: [Temporal.Now.plainDateISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateISO)
|
||||||
pub fn plainDateISO(io: std.Io) !PlainDate {
|
pub fn plainDateISO(allocator: std.mem.Allocator, io: std.Io, time_zone_like: ?[]const u8) !PlainDate {
|
||||||
const now = currentParts(io);
|
const time_zone = try resolveTimeZone(allocator, io, time_zone_like);
|
||||||
return PlainDate.init(now.year, now.month, now.day);
|
const now = try instant(io);
|
||||||
|
defer now.deinit();
|
||||||
|
|
||||||
|
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDate_from_epoch_nanoseconds(
|
||||||
|
abi.toI128Nanoseconds(now.epochNanoseconds()),
|
||||||
|
abi.to.toTimeZone(time_zone),
|
||||||
|
))) orelse return abi.TemporalError.Generic;
|
||||||
|
return .{ ._inner = ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current date and time as a [`Temporal.PlainDateTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime) object, in the ISO 8601 calendar and the specified time zone.
|
/// Returns the current date and time as a [`Temporal.PlainDateTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime) object, in the ISO 8601 calendar and the specified time zone.
|
||||||
///
|
///
|
||||||
|
/// When `time_zone_like` is null, the system time zone is used.
|
||||||
|
///
|
||||||
/// See: [Temporal.Now.plainDateTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateTimeISO)
|
/// See: [Temporal.Now.plainDateTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateTimeISO)
|
||||||
pub fn plainDateTimeISO(io: std.Io) !PlainDateTime {
|
pub fn plainDateTimeISO(allocator: std.mem.Allocator, io: std.Io, time_zone_like: ?[]const u8) !PlainDateTime {
|
||||||
const now = currentParts(io);
|
const time_zone = try resolveTimeZone(allocator, io, time_zone_like);
|
||||||
return PlainDateTime.init(
|
const now = try instant(io);
|
||||||
now.year,
|
defer now.deinit();
|
||||||
now.month,
|
|
||||||
now.day,
|
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainDateTime_from_epoch_nanoseconds(
|
||||||
now.hour,
|
abi.toI128Nanoseconds(now.epochNanoseconds()),
|
||||||
now.minute,
|
abi.to.toTimeZone(time_zone),
|
||||||
now.second,
|
))) orelse return abi.TemporalError.Generic;
|
||||||
now.millisecond,
|
return .{ ._inner = ptr };
|
||||||
now.microsecond,
|
|
||||||
now.nanosecond,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current time as a [`Temporal.PlainTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime) object, in the specified time zone.
|
/// Returns the current time as a [`Temporal.PlainTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime) object, in the specified time zone.
|
||||||
///
|
///
|
||||||
|
/// When `time_zone_like` is null, the system time zone is used.
|
||||||
|
///
|
||||||
/// See: [Temporal.Now.plainTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainTimeISO)
|
/// See: [Temporal.Now.plainTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainTimeISO)
|
||||||
pub fn plainTimeISO(io: std.Io) !PlainTime {
|
pub fn plainTimeISO(allocator: std.mem.Allocator, io: std.Io, time_zone_like: ?[]const u8) !PlainTime {
|
||||||
const now = currentParts(io);
|
const time_zone = try resolveTimeZone(allocator, io, time_zone_like);
|
||||||
return PlainTime.init(
|
const now = try instant(io);
|
||||||
now.hour,
|
defer now.deinit();
|
||||||
now.minute,
|
|
||||||
now.second,
|
const ptr = (try abi.extractResult(abi.c.temporal_rs_PlainTime_from_epoch_nanoseconds(
|
||||||
now.millisecond,
|
abi.toI128Nanoseconds(now.epochNanoseconds()),
|
||||||
now.microsecond,
|
abi.to.toTimeZone(time_zone),
|
||||||
now.nanosecond,
|
))) orelse return abi.TemporalError.Generic;
|
||||||
);
|
return PlainTime{ ._inner = ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a time zone identifier representing the system's current time zone.
|
/// Returns a time zone identifier representing the system's current time zone.
|
||||||
///
|
///
|
||||||
/// The caller owns the returned slice and must free it with `allocator`.
|
/// The caller owns the returned slice and must free it with `allocator`.
|
||||||
///
|
///
|
||||||
/// TODO: Detect the system's actual IANA time zone instead of always returning
|
|
||||||
/// "UTC". This requires platform-specific logic:
|
|
||||||
/// - Linux/macOS: resolve the `/etc/localtime` symlink to `.../zoneinfo/<Area>/<City>`.
|
|
||||||
/// - Windows: read the time zone from the registry and map the Windows zone
|
|
||||||
/// name to an IANA identifier via the CLDR `windowsZones` table.
|
|
||||||
/// - WASI: no host time zone is available; "UTC" is the only honest answer.
|
|
||||||
///
|
|
||||||
/// See: [Temporal.Now.timeZoneId](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/timeZoneId)
|
/// See: [Temporal.Now.timeZoneId](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/timeZoneId)
|
||||||
pub fn timeZoneId(allocator: std.mem.Allocator) ![]const u8 {
|
pub fn timeZoneId(allocator: std.mem.Allocator, io: std.Io) ![]const u8 {
|
||||||
return allocator.dupe(u8, "UTC");
|
const time_zone = try SystemTimeZone.timeZone(allocator, io);
|
||||||
|
var write = abi.DiplomatWrite.init(allocator);
|
||||||
|
defer write.deinit();
|
||||||
|
|
||||||
|
abi.c.temporal_rs_TimeZone_identifier(time_zone._inner, &write.inner);
|
||||||
|
return try write.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current date and time as a [`Temporal.ZonedDateTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime) object, in the ISO 8601 calendar and the specified time zone.
|
/// Returns the current date and time as a [`Temporal.ZonedDateTime`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime) object, in the ISO 8601 calendar and the specified time zone.
|
||||||
///
|
///
|
||||||
|
/// When `time_zone_like` is null, the system time zone is used.
|
||||||
|
///
|
||||||
/// See: [Temporal.Now.zonedDateTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/zonedDateTimeISO)
|
/// See: [Temporal.Now.zonedDateTimeISO](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/zonedDateTimeISO)
|
||||||
pub fn zonedDateTimeISO(allocator: std.mem.Allocator, io: std.Io) !ZonedDateTime {
|
pub fn zonedDateTimeISO(allocator: std.mem.Allocator, io: std.Io, time_zone_like: ?[]const u8) !ZonedDateTime {
|
||||||
const now = try instant(io);
|
const now = try instant(io);
|
||||||
const tz_id = try timeZoneId(allocator);
|
defer now.deinit();
|
||||||
defer allocator.free(tz_id);
|
const time_zone = try resolveTimeZone(allocator, io, time_zone_like);
|
||||||
const time_zone = try ZonedDateTime.TimeZone.init(tz_id);
|
|
||||||
return ZonedDateTime.fromEpochNanoseconds(now.epochNanoseconds(), time_zone);
|
return ZonedDateTime.fromEpochNanoseconds(now.epochNanoseconds(), time_zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CurrentParts = struct {
|
fn resolveTimeZone(allocator: std.mem.Allocator, io: std.Io, time_zone_like: ?[]const u8) !ZonedDateTime.TimeZone {
|
||||||
year: i32,
|
if (time_zone_like) |id| return ZonedDateTime.TimeZone.init(id);
|
||||||
month: u8,
|
return SystemTimeZone.timeZone(allocator, io);
|
||||||
day: u8,
|
|
||||||
hour: u8,
|
|
||||||
minute: u8,
|
|
||||||
second: u8,
|
|
||||||
millisecond: u16,
|
|
||||||
microsecond: u16,
|
|
||||||
nanosecond: u16,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn currentParts(io: std.Io) CurrentParts {
|
|
||||||
const ns = std.Io.Timestamp.now(io, .real).nanoseconds;
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test instant {
|
test instant {
|
||||||
|
|
@ -155,41 +128,62 @@ test instant {
|
||||||
|
|
||||||
test plainDateISO {
|
test plainDateISO {
|
||||||
const io = std.testing.io;
|
const io = std.testing.io;
|
||||||
const date = try plainDateISO(io);
|
const date = try plainDateISO(std.testing.allocator, io, null);
|
||||||
|
defer date.deinit();
|
||||||
try std.testing.expect(date.year() >= 1970);
|
try std.testing.expect(date.year() >= 1970);
|
||||||
try std.testing.expect(date.month() >= 1 and date.month() <= 12);
|
try std.testing.expect(date.month() >= 1 and date.month() <= 12);
|
||||||
try std.testing.expect(date.day() >= 1 and date.day() <= 31);
|
try std.testing.expect(date.day() >= 1 and date.day() <= 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
test plainDateTimeISO {
|
test plainDateTimeISO {
|
||||||
const io = std.testing.io;
|
const io = std.testing.io;
|
||||||
const dt = try plainDateTimeISO(io);
|
var dt = try plainDateTimeISO(std.testing.allocator, io, null);
|
||||||
|
defer dt.deinit();
|
||||||
try std.testing.expect(dt.year() >= 1970);
|
try std.testing.expect(dt.year() >= 1970);
|
||||||
try std.testing.expect(dt.month() >= 1 and dt.month() <= 12);
|
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.day() >= 1 and dt.day() <= 31);
|
||||||
try std.testing.expect(dt.hour() < 24);
|
try std.testing.expect(dt.hour() < 24);
|
||||||
try std.testing.expect(dt.minute() < 60);
|
try std.testing.expect(dt.minute() < 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
test plainTimeISO {
|
test plainTimeISO {
|
||||||
const io = std.testing.io;
|
const io = std.testing.io;
|
||||||
const t = try plainTimeISO(io);
|
const t = try plainTimeISO(std.testing.allocator, io, null);
|
||||||
try std.testing.expect(t.hour() < 24);
|
try std.testing.expect(t.hour() < 24);
|
||||||
try std.testing.expect(t.minute() < 60);
|
try std.testing.expect(t.minute() < 60);
|
||||||
try std.testing.expect(t.second() < 60);
|
try std.testing.expect(t.second() < 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
test timeZoneId {
|
test timeZoneId {
|
||||||
const tz = try timeZoneId(std.testing.allocator);
|
const io = std.testing.io;
|
||||||
|
const tz = try timeZoneId(std.testing.allocator, io);
|
||||||
defer std.testing.allocator.free(tz);
|
defer std.testing.allocator.free(tz);
|
||||||
try std.testing.expectEqualStrings("UTC", tz);
|
try std.testing.expect(tz.len > 0);
|
||||||
|
_ = try ZonedDateTime.TimeZone.init(tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
test zonedDateTimeISO {
|
test zonedDateTimeISO {
|
||||||
const io = std.testing.io;
|
const io = std.testing.io;
|
||||||
const zdt = try zonedDateTimeISO(std.testing.allocator, io);
|
const zdt = try zonedDateTimeISO(std.testing.allocator, io, null);
|
||||||
defer zdt.deinit();
|
defer zdt.deinit();
|
||||||
|
|
||||||
std.log.info("{d}", .{zdt.epochNanoseconds()});
|
|
||||||
|
|
||||||
try std.testing.expect(zdt.epochNanoseconds() > 0);
|
try std.testing.expect(zdt.epochNanoseconds() > 0);
|
||||||
const tz = try zdt.timeZoneId(std.testing.allocator);
|
const tz = try zdt.timeZoneId(std.testing.allocator);
|
||||||
defer std.testing.allocator.free(tz);
|
defer std.testing.allocator.free(tz);
|
||||||
try std.testing.expectEqualStrings("UTC", tz);
|
const expected_tz = try timeZoneId(std.testing.allocator, io);
|
||||||
|
defer std.testing.allocator.free(expected_tz);
|
||||||
|
try std.testing.expectEqualStrings(expected_tz, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "plainDateISO with explicit time zone" {
|
||||||
|
const io = std.testing.io;
|
||||||
|
const date = try plainDateISO(std.testing.allocator, io, "UTC");
|
||||||
|
defer date.deinit();
|
||||||
|
|
||||||
|
const zdt = try zonedDateTimeISO(std.testing.allocator, io, "UTC");
|
||||||
|
defer zdt.deinit();
|
||||||
|
const plain = try zdt.toPlainDate();
|
||||||
|
defer plain.deinit();
|
||||||
|
|
||||||
|
try std.testing.expect(date.equals(plain));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
src/SystemTimeZone.zig
Normal file
68
src/SystemTimeZone.zig
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ZonedDateTime = @import("ZonedDateTime.zig");
|
||||||
|
|
||||||
|
const zoneinfo_marker = "zoneinfo/";
|
||||||
|
|
||||||
|
/// Returns the host environment's current time zone identifier.
|
||||||
|
///
|
||||||
|
/// Resolution order:
|
||||||
|
/// 1. `TZ` environment variable (when set and non-empty)
|
||||||
|
/// 2. Unix: `/etc/localtime` symlink target under `zoneinfo/`
|
||||||
|
/// 3. `"UTC"`
|
||||||
|
///
|
||||||
|
/// The caller owns the returned slice.
|
||||||
|
pub fn identifier(allocator: std.mem.Allocator, io: std.Io) ![]const u8 {
|
||||||
|
if (tzFromEnv(allocator)) |tz| return tz;
|
||||||
|
|
||||||
|
if (detectFromPlatform(allocator, io)) |tz| return tz;
|
||||||
|
|
||||||
|
return try allocator.dupe(u8, "UTC");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `TimeZone` for the host environment's current time zone.
|
||||||
|
pub fn timeZone(allocator: std.mem.Allocator, io: std.Io) !ZonedDateTime.TimeZone {
|
||||||
|
const id = try identifier(allocator, io);
|
||||||
|
defer allocator.free(id);
|
||||||
|
return ZonedDateTime.TimeZone.init(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tzFromEnv(allocator: std.mem.Allocator) ?[]const u8 {
|
||||||
|
if (!builtin.link_libc) return null;
|
||||||
|
|
||||||
|
const value = std.c.getenv("TZ") orelse return null;
|
||||||
|
const tz = std.mem.span(value);
|
||||||
|
if (tz.len == 0) return null;
|
||||||
|
|
||||||
|
return allocator.dupe(u8, tz) catch null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detectFromPlatform(allocator: std.mem.Allocator, io: std.Io) ?[]const u8 {
|
||||||
|
return switch (builtin.os.tag) {
|
||||||
|
.linux, .macos, .freebsd, .openbsd, .netbsd, .dragonfly => detectFromEtcLocaltime(allocator, io) catch null,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detectFromEtcLocaltime(allocator: std.mem.Allocator, io: std.Io) ![]const u8 {
|
||||||
|
var target_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
const len = std.Io.Dir.readLinkAbsolute(io, "/etc/localtime", &target_buf) catch return error.NotFound;
|
||||||
|
const target = target_buf[0..len];
|
||||||
|
|
||||||
|
const idx = std.mem.lastIndexOf(u8, target, zoneinfo_marker) orelse return error.NotFound;
|
||||||
|
return try allocator.dupe(u8, target[idx + zoneinfo_marker.len ..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
test identifier {
|
||||||
|
const io = std.testing.io;
|
||||||
|
const tz = try identifier(std.testing.allocator, io);
|
||||||
|
defer std.testing.allocator.free(tz);
|
||||||
|
try std.testing.expect(tz.len > 0);
|
||||||
|
_ = try ZonedDateTime.TimeZone.init(tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
test timeZone {
|
||||||
|
const io = std.testing.io;
|
||||||
|
_ = try timeZone(std.testing.allocator, io);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue