226 lines
6.6 KiB
Zig
226 lines
6.6 KiB
Zig
const std = @import("std");
|
|
pub const Year = @import("year.zig").Year;
|
|
pub const Month = @import("month.zig").Month;
|
|
pub const tz = @import("timezone.zig");
|
|
pub const Offset = tz.Offset;
|
|
pub const TimeZone = tz.TimeZone;
|
|
|
|
pub const SECONDS_PER_MINUTE = 60;
|
|
pub const SECONDS_PER_HOUR = 60 * 60;
|
|
pub const SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;
|
|
|
|
pub const WeekDay = enum(u3) {
|
|
thursday = 0,
|
|
friday,
|
|
saturday,
|
|
sunday,
|
|
monday,
|
|
tuesday,
|
|
wednesday,
|
|
};
|
|
|
|
pub const Comparison = enum {
|
|
gt,
|
|
lt,
|
|
eq,
|
|
};
|
|
|
|
pub const DateTime = struct {
|
|
year: Year,
|
|
month: Month,
|
|
day: u8,
|
|
hour: ?u5,
|
|
minute: ?u6,
|
|
second: ?u6,
|
|
tz: TimeZone,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn getYear(self: Self) i32 {
|
|
return self.year.get();
|
|
}
|
|
|
|
pub fn getOffset(self: Self) ?Offset {
|
|
return switch (self.tz) {
|
|
.utc => null,
|
|
.offset => |ofs| ofs,
|
|
};
|
|
}
|
|
|
|
pub fn toTimestamp(self: Self) i64 {
|
|
var seconds: i64 = 0;
|
|
if (self.year.get() < 1970) {
|
|
var year = Year.new(1970);
|
|
while (year.get() != self.year.get()) {
|
|
year = year.previous();
|
|
seconds -= year.seconds();
|
|
}
|
|
} else if (self.year.get() > 1970) {
|
|
var year = Year.new(1970);
|
|
while (year.get() != self.year.get()) {
|
|
seconds += year.seconds();
|
|
year = year.next();
|
|
}
|
|
}
|
|
var month = Month.january;
|
|
while (month != self.month) {
|
|
seconds += month.seconds(self.year);
|
|
month = month.next().?;
|
|
}
|
|
// The days begin numbering with 1, so on the 5th we have had four full
|
|
// days plus some remainder. So we take self.days - 1 for our calculation
|
|
seconds += @as(i64, self.day - 1) * SECONDS_PER_DAY;
|
|
if (self.hour) |h| {
|
|
seconds += @as(i64, h) * 3600;
|
|
}
|
|
if (self.minute) |m| {
|
|
seconds += @as(i64, m) * 60;
|
|
}
|
|
if (self.second) |s| {
|
|
seconds += s;
|
|
}
|
|
if (self.getOffset()) |ofs| seconds -= ofs.asSeconds();
|
|
return seconds;
|
|
}
|
|
|
|
pub fn fromTimestamp(ts: i64) Self {
|
|
if (ts < 0) {
|
|
var seconds: i64 = 0;
|
|
var year = Year.new(-1);
|
|
while (seconds > -year.seconds()) {
|
|
seconds -= year.seconds();
|
|
year = year.previous();
|
|
}
|
|
var month: ?Month = Month.december;
|
|
while (month != null) {
|
|
if (month) |m| {
|
|
if (seconds > m.seconds(year)) break;
|
|
seconds -= m.seconds(year);
|
|
month = m.previous();
|
|
}
|
|
}
|
|
var day = month.?.days(year);
|
|
while (day > 0 and seconds < -SECONDS_PER_DAY) {
|
|
seconds -= SECONDS_PER_DAY;
|
|
day -= 1;
|
|
}
|
|
var hours: u5 = 23;
|
|
while (hours >= 0 and seconds < -SECONDS_PER_HOUR) {
|
|
seconds -= SECONDS_PER_HOUR;
|
|
hours -= 1;
|
|
}
|
|
var minutes: u6 = 60;
|
|
while (minutes >= 0 and seconds < -60) {
|
|
seconds -= 60;
|
|
minutes -= 1;
|
|
}
|
|
const second = @as(u6, @intCast(seconds + 60));
|
|
return Self{
|
|
.year = year,
|
|
.month = month.?,
|
|
.day = day,
|
|
.hour = hours,
|
|
.minute = minutes,
|
|
.second = @as(u6, second),
|
|
.tz = .utc,
|
|
};
|
|
} else if (ts > 0) {
|
|
var seconds = ts;
|
|
var year = Year.new(1970);
|
|
while (year.seconds() < seconds) {
|
|
seconds -= year.seconds();
|
|
year = year.next();
|
|
}
|
|
var month = Month.january;
|
|
while (month.seconds(year) < seconds) {
|
|
seconds -= month.seconds(year);
|
|
month = month.next().?;
|
|
}
|
|
const day = @divTrunc(seconds, SECONDS_PER_DAY) + 1;
|
|
seconds = @rem(seconds, SECONDS_PER_DAY);
|
|
const hour = @divTrunc(seconds, SECONDS_PER_HOUR);
|
|
seconds = @rem(seconds, SECONDS_PER_HOUR);
|
|
const minute = @divTrunc(seconds, 60);
|
|
seconds = @rem(seconds, 60);
|
|
return Self{
|
|
.year = year,
|
|
.month = month,
|
|
.day = @as(u8, @intCast(day)),
|
|
.hour = @as(u5, @intCast(hour)),
|
|
.minute = @as(u6, @intCast(minute)),
|
|
.second = @as(u6, @intCast(seconds)),
|
|
.tz = .utc,
|
|
};
|
|
} else {
|
|
return Self{
|
|
.year = Year.new(1970),
|
|
.month = .january,
|
|
.day = 1,
|
|
.hour = 0,
|
|
.minute = 0,
|
|
.second = 0,
|
|
.tz = .utc,
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn now() Self {
|
|
return Self.fromTimestamp(std.time.timestamp());
|
|
}
|
|
|
|
pub fn weekday(self: Self) WeekDay {
|
|
const ts = self.toTimestamp();
|
|
const days = @divTrunc(ts, SECONDS_PER_DAY);
|
|
return @as(WeekDay, @enumFromInt(@rem(days, 7)));
|
|
}
|
|
|
|
pub fn compare(self: Self, other: Self) Comparison {
|
|
const a = self.toTimestamp();
|
|
const b = other.toTimestamp();
|
|
return if (a > b) .gt else if (a < b) .lt else .eq;
|
|
}
|
|
|
|
pub fn format(
|
|
self: Self,
|
|
comptime fmt: []const u8,
|
|
options: std.fmt.FormatOptions,
|
|
writer: anytype,
|
|
) !void {
|
|
_ = fmt;
|
|
_ = options;
|
|
|
|
try writer.print("{s}-{d:0>2}-{d:0>2}", .{
|
|
self.year, @intFromEnum(self.month), self.day,
|
|
});
|
|
if (self.hour) |h| {
|
|
try writer.print("T{d:0>2}", .{h});
|
|
if (self.minute) |m| {
|
|
try writer.print(":{d:0>2}", .{m});
|
|
if (self.second) |s| {
|
|
try writer.print(":{d:0>2}", .{s});
|
|
}
|
|
}
|
|
}
|
|
try writer.print("{s}", .{self.tz});
|
|
}
|
|
|
|
pub fn format_basic(
|
|
self: Self,
|
|
writer: anytype,
|
|
) !void {
|
|
try writer.print("{s}{d:0>2}{d:0>2}", .{
|
|
self.year, @intFromEnum(self.month), self.day,
|
|
});
|
|
if (self.hour) |h| {
|
|
try writer.print("T{d:0>2}", .{h});
|
|
if (self.minute) |m| {
|
|
try writer.print("{d:0>2}", .{m});
|
|
if (self.second) |s| {
|
|
try writer.print("{d:0>2}", .{s});
|
|
}
|
|
}
|
|
}
|
|
try self.tz.format_basic(writer);
|
|
}
|
|
};
|