zig-chrono/src/main.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);
}
};