Initial commit

This commit is contained in:
Nathan Fisher 2023-06-12 22:59:12 -04:00
commit 2029bf3778
3 changed files with 245 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
zig-out/
zig-cache/
tags
tags.temp
tags.lock

17
build.zig Normal file
View File

@ -0,0 +1,17 @@
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const lib = b.addStaticLibrary("zigDateTime", "src/main.zig");
lib.setBuildMode(mode);
lib.install();
const main_tests = b.addTest("src/main.zig");
main_tests.setBuildMode(mode);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
}

223
src/main.zig Normal file
View File

@ -0,0 +1,223 @@
const std = @import("std");
const testing = std.testing;
pub const SECONDS_PER_MINUTE = 60;
pub const SECONDS_PER_HOUR = 60 * 60;
pub const SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;
pub const YearTag = enum(u1) {
normal,
leap,
fn new(year: u31) YearTag {
return if (year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)) .leap else .normal;
}
};
pub const Year = union(YearTag) {
normal: u31,
leap: u31,
const Self = @This();
pub fn new(year: u31) Self {
return switch (YearTag.new(year)) {
.normal => Self{ .normal = year },
.leap => Self{ .leap = year },
};
}
pub fn days(self: Self) u16 {
return switch (self) {
.normal => 365,
.leap => 366,
};
}
pub fn seconds(self: Self) i64 {
return self.days * SECONDS_PER_DAY;
}
pub fn get(self: Self) u31 {
return switch (self) {
.normal => |year| year,
.leap => |year| year,
};
}
pub fn next(self: Self) Self {
return Self.new(self.get() + 1);
}
pub fn previous(self: Self) Self {
return Self.new(self.get() - 1);
}
};
test "new year" {
try testing.expectEqual(Year.new(2023), Year{ .normal = 2023 });
try testing.expectEqual(Year.new(2024), Year{ .leap = 2024 });
}
test "get year" {
try testing.expectEqual(Year.new(2023).get(), 2023);
try testing.expectEqual(Year.new(2024).get(), 2024);
}
test "next year" {
try testing.expectEqual(Year.new(2023).next(), Year{ .leap = 2024 });
}
test "last year" {
try testing.expectEqual(Year.new(2024).previous(), Year{ .normal = 2023 });
}
pub const Month = enum(u4) {
january = 1,
february = 2,
march = 3,
april = 4,
may = 5,
june = 6,
july = 7,
august = 8,
september = 9,
october = 10,
november = 11,
december = 12,
const Self = @This();
pub fn days(self: Self, year: Year) u5 {
return switch (@enumToInt(self)) {
1, 3, 5, 7, 8, 10, 12 => 31,
2 => switch (year) {
.normal => 28,
.leap => 29,
},
else => 30,
};
}
pub fn seconds(self: Self) u32 {
return self.days() * SECONDS_PER_DAY;
}
pub fn next(self: Self) ?Self {
const num = @enumToInt(self);
return if (num < 12) @intToEnum(Self, num + 1) else null;
}
pub fn previous(self: Self) ?Self {
const num = @enumToInt(self);
return if (num > 1) @intToEnum(Self, num - 1) else null;
}
};
test "get days in month" {
const year = Year.new(2023);
const month = Month.february;
try testing.expectEqual(month.days(year), 28);
}
test "next month" {
try testing.expectEqual(Month.june.next(), .july);
try testing.expectEqual(Month.december.next(), null);
}
test "last month" {
try testing.expectEqual(Month.june.previous(), .may);
try testing.expectEqual(Month.january.previous(), null);
}
pub const TimeZoneTag = enum(u1) {
utc,
offset,
};
pub const TimeZone = union(TimeZoneTag) {
utc: void,
offset: i8,
const Self = @This();
pub fn new(offset: ?i8) ?Self {
return if (offset) |ofs| blk: {
if (ofs < -12 or ofs > 12) {
break :blk null;
} else if (ofs == 0) {
break :blk .utc;
} else {
break :blk Self{ .offset = ofs };
}
} else .utc;
}
};
test "new timezone" {
const tz = TimeZone.new(-5).?;
try testing.expectEqual(@as(TimeZoneTag, tz), .offset);
switch (tz) {
.offset => |ofs| try testing.expectEqual(ofs, -5),
else => unreachable,
}
}
test "new timezone utc" {
const tz0 = TimeZone.new(null).?;
const tz1 = TimeZone.new(0).?;
try testing.expectEqual(@as(TimeZoneTag, tz0), .utc);
try testing.expectEqual(@as(TimeZoneTag, tz1), .utc);
}
pub const DateTime = struct {
year: Year,
month: Month,
day: u8,
hour: ?u5,
minute: ?u6,
second: ?u6,
nanos: ?u32,
tz: TimeZone,
const Self = @This();
pub fn getYear(self: Self) u32 {
return self.year.get();
}
pub fn getOffset(self: Self) ?i8 {
return switch (self.tz) {
.utc => null,
.offset => |ofs| ofs,
};
}
};
test "get year" {
const dt = DateTime{
.year = Year.new(2023),
.month = .june,
.day = 12,
.hour = 1,
.minute = 5,
.second = 14,
.nanos = null,
.tz = TimeZone.new(-5).?,
};
try testing.expectEqual(dt.getYear(), 2023);
}
test "get offset" {
const dt = DateTime{
.year = Year.new(2023),
.month = .june,
.day = 12,
.hour = 1,
.minute = 5,
.second = 14,
.nanos = null,
.tz = TimeZone.new(-5).?,
};
try testing.expectEqual(dt.getOffset().?, -5);
}