Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std: add generic date.Date, time.Time, and date_time.DateTime #19549

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 17 additions & 21 deletions lib/compiler/aro/aro/Compilation.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const std = @import("std");
const Allocator = mem.Allocator;
const assert = std.debug.assert;
const EpochSeconds = std.time.epoch.EpochSeconds;
const mem = std.mem;
const Interner = @import("../backend.zig").Interner;
const Builtins = @import("Builtins.zig");
Expand Down Expand Up @@ -195,38 +194,35 @@ fn getTimestamp(comp: *Compilation) !u47 {
}

fn generateDateAndTime(w: anytype, timestamp: u47) !void {
const epoch_seconds = EpochSeconds{ .secs = timestamp };
const epoch_day = epoch_seconds.getEpochDay();
const day_seconds = epoch_seconds.getDaySeconds();
const year_day = epoch_day.calculateYearDay();
const month_day = year_day.calculateMonthDay();
const DateTime = std.date_time.DateTime;
const dt = DateTime.fromEpoch(timestamp);

const month_names = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
std.debug.assert(std.time.epoch.Month.jan.numeric() == 1);
std.debug.assert(DateTime.Date.Month.jan.numeric() == 1);

const month_name = month_names[month_day.month.numeric() - 1];
const month_name = month_names[dt.date.month.numeric() - 1];
try w.print("#define __DATE__ \"{s} {d: >2} {d}\"\n", .{
month_name,
month_day.day_index + 1,
year_day.year,
dt.date.day,
dt.date.year,
});
try w.print("#define __TIME__ \"{d:0>2}:{d:0>2}:{d:0>2}\"\n", .{
day_seconds.getHoursIntoDay(),
day_seconds.getMinutesIntoHour(),
day_seconds.getSecondsIntoMinute(),
dt.time.hour,
dt.time.minute,
dt.time.second,
});

const day_names = [_][]const u8{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
// days since Thu Oct 1 1970
const day_name = day_names[@intCast((epoch_day.day + 3) % 7)];
const day_name = day_names[std.math.comptimeMod(dt.date.toEpoch() + 3, day_names.len)];
try w.print("#define __TIMESTAMP__ \"{s} {s} {d: >2} {d:0>2}:{d:0>2}:{d:0>2} {d}\"\n", .{
day_name,
month_name,
month_day.day_index + 1,
day_seconds.getHoursIntoDay(),
day_seconds.getMinutesIntoHour(),
day_seconds.getSecondsIntoMinute(),
year_day.year,
dt.date.day,
dt.time.hour,
dt.time.minute,
dt.time.second,
dt.date.year,
});
}

Expand Down Expand Up @@ -534,8 +530,8 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi

if (system_defines_mode == .include_system_defines) {
try buf.appendSlice(
\\#define __VERSION__ "Aro
++ @import("../backend.zig").version_str ++ "\"\n" ++
\\#define __VERSION__ "Aro
++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++
\\#define __Aro__
\\
);
Expand Down
115 changes: 37 additions & 78 deletions lib/std/crypto/Certificate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ pub const Parsed = struct {
};

pub const Validity = struct {
not_before: u64,
not_after: u64,
not_before: DateTime.EpochSubseconds,
not_after: DateTime.EpochSubseconds,
};

pub const Slice = der.Element.Slice;
Expand Down Expand Up @@ -542,8 +542,9 @@ pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
pub const ParseTimeError = error{ CertificateTimeInvalid, CertificateFieldHasWrongDataType };

/// Returns number of seconds since epoch.
pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!DateTime.EpochSubseconds {
const bytes = cert.contents(elem);

switch (elem.identifier.tag) {
.utc_time => {
// Example: "YYMMDD000000Z"
Expand All @@ -552,14 +553,18 @@ pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
if (bytes[12] != 'Z')
return error.CertificateTimeInvalid;

return Date.toSeconds(.{
.year = @as(u16, 2000) + try parseTimeDigits(bytes[0..2], 0, 99),
.month = try parseTimeDigits(bytes[2..4], 1, 12),
.day = try parseTimeDigits(bytes[4..6], 1, 31),
.hour = try parseTimeDigits(bytes[6..8], 0, 23),
.minute = try parseTimeDigits(bytes[8..10], 0, 59),
.second = try parseTimeDigits(bytes[10..12], 0, 59),
});
return (DateTime{
.date = .{
.year = @as(DateTime.Date.Year, 2000) + try parseTimeDigits(bytes[0..2], 0, 99),
.month = @enumFromInt(try parseTimeDigits(bytes[2..4], 1, 12)),
.day = try parseTimeDigits(bytes[4..6], 1, 31),
},
.time = .{
.hour = try parseTimeDigits(bytes[6..8], 0, 23),
.minute = try parseTimeDigits(bytes[8..10], 0, 59),
.second = try parseTimeDigits(bytes[10..12], 0, 59),
},
}).toEpoch();
},
.generalized_time => {
// Examples:
Expand All @@ -568,67 +573,24 @@ pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
// "19920722132100.3Z"
if (bytes.len < 15)
return error.CertificateTimeInvalid;
return Date.toSeconds(.{
.year = try parseYear4(bytes[0..4]),
.month = try parseTimeDigits(bytes[4..6], 1, 12),
.day = try parseTimeDigits(bytes[6..8], 1, 31),
.hour = try parseTimeDigits(bytes[8..10], 0, 23),
.minute = try parseTimeDigits(bytes[10..12], 0, 59),
.second = try parseTimeDigits(bytes[12..14], 0, 59),
});
return (DateTime{
.date = .{
.year = try parseYear4(bytes[0..4]),
.month = @enumFromInt(try parseTimeDigits(bytes[4..6], 1, 12)),
.day = try parseTimeDigits(bytes[6..8], 1, 31),
},
.time = .{
.hour = try parseTimeDigits(bytes[8..10], 0, 23),
.minute = try parseTimeDigits(bytes[10..12], 0, 59),
.second = try parseTimeDigits(bytes[12..14], 0, 59),
},
}).toEpoch();
},
else => return error.CertificateFieldHasWrongDataType,
}
}

const Date = struct {
/// example: 1999
year: u16,
/// range: 1 to 12
month: u8,
/// range: 1 to 31
day: u8,
/// range: 0 to 59
hour: u8,
/// range: 0 to 59
minute: u8,
/// range: 0 to 59
second: u8,

/// Convert to number of seconds since epoch.
pub fn toSeconds(date: Date) u64 {
var sec: u64 = 0;

{
var year: u16 = 1970;
while (year < date.year) : (year += 1) {
const days: u64 = std.time.epoch.getDaysInYear(year);
sec += days * std.time.epoch.secs_per_day;
}
}

{
const is_leap = std.time.epoch.isLeapYear(date.year);
var month: u4 = 1;
while (month < date.month) : (month += 1) {
const days: u64 = std.time.epoch.getDaysInMonth(
@as(std.time.epoch.YearLeapKind, @enumFromInt(@intFromBool(is_leap))),
@as(std.time.epoch.Month, @enumFromInt(month)),
);
sec += days * std.time.epoch.secs_per_day;
}
}

sec += (date.day - 1) * @as(u64, std.time.epoch.secs_per_day);
sec += date.hour * @as(u64, 60 * 60);
sec += date.minute * @as(u64, 60);
sec += date.second;

return sec;
}
};

pub fn parseTimeDigits(text: *const [2]u8, min: u8, max: u8) !u8 {
pub fn parseTimeDigits(text: *const [2]u8, min: comptime_int, max: comptime_int) !std.math.IntFittingRange(min, max) {
const result = if (use_vectors) result: {
const nn: @Vector(2, u16) = .{ text[0], text[1] };
const zero: @Vector(2, u16) = .{ '0', '0' };
Expand All @@ -652,22 +614,18 @@ test parseTimeDigits {
try expectError(error.CertificateTimeInvalid, parseTimeDigits("Di", 0, 99));
}

pub fn parseYear4(text: *const [4]u8) !u16 {
const result = if (use_vectors) result: {
const nnnn: @Vector(4, u32) = .{ text[0], text[1], text[2], text[3] };
const zero: @Vector(4, u32) = .{ '0', '0', '0', '0' };
const mmmm: @Vector(4, u32) = .{ 1000, 100, 10, 1 };
break :result @reduce(.Add, (nnnn -% zero) *% mmmm);
} else std.fmt.parseInt(u16, text, 10) catch return error.CertificateTimeInvalid;
pub fn parseYear4(text: *const [4]u8) !DateTime.Date.Year {
const result = std.fmt.parseInt(DateTime.Date.Year, text, 10) catch
return error.CertificateTimeInvalid;
if (result > 9999) return error.CertificateTimeInvalid;
return @truncate(result);
return result;
}

test parseYear4 {
const expectEqual = std.testing.expectEqual;
try expectEqual(@as(u16, 0), try parseYear4("0000"));
try expectEqual(@as(u16, 9999), try parseYear4("9999"));
try expectEqual(@as(u16, 1988), try parseYear4("1988"));
try expectEqual(0, try parseYear4("0000"));
try expectEqual(9999, try parseYear4("9999"));
try expectEqual(1988, try parseYear4("1988"));

const expectError = std.testing.expectError;
try expectError(error.CertificateTimeInvalid, parseYear4("999b"));
Expand Down Expand Up @@ -859,6 +817,7 @@ fn verifyEd25519(
const std = @import("../std.zig");
const crypto = std.crypto;
const mem = std.mem;
const DateTime = std.date_time.DateTime;
const Certificate = @This();

pub const der = struct {
Expand Down
14 changes: 14 additions & 0 deletions lib/std/date.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub const gregorian = @import("./date/gregorian.zig");
pub const epoch = @import("./date/epoch.zig");

/// A Gregorian Date using days since `1970-01-01` for its epoch methods.
///
/// Supports dates between years -32_768 and 32_768.
pub const Date = gregorian.Date(i16, epoch.posix);
pub const Month = Date.Month;
pub const Day = Date.Day;
pub const Weekday = Date.Weekday;

test {
_ = gregorian;
}
45 changes: 45 additions & 0 deletions lib/std/date/epoch.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Days since 1970-01-01 for various calendar systems.
//!
//! 1970-01-01 is Zig's chosen epoch for timestamp functions.

// To build this list yourself using `date`:
// $ echo $(( $(date -d "YYYY-mm-dd UTC" +%s) / (60*60*24) ))

/// 1970-01-01
pub const posix = 0;
/// 1980-01-01
pub const dos = 3_652;
/// 2001-01-01
pub const ios = 11_323;
/// 1858-11-17
pub const openvms = -40_587;
/// 1900-01-01
pub const zos = -25_567;
/// 1601-01-01
pub const windows = -134_774;
/// 1978-01-01
pub const amiga = 2_922;
/// 1967-12-31
pub const pickos = -732;
/// 1980-01-06
pub const gps = 3_657;
/// 0001-01-01
pub const clr = -719_164;
/// 1582-10-15
pub const uefi = -141_427;
/// 1900-01-01
pub const efi = -25_567;

pub const unix = posix;
pub const android = posix;
pub const os2 = dos;
pub const bios = dos;
pub const vfat = dos;
pub const ntfs = windows;
pub const ntp = zos;
pub const jbase = pickos;
pub const aros = amiga;
pub const morphos = amiga;
pub const brew = gps;
pub const atsc = gps;
pub const go = clr;
Loading