handle times before 1970
This commit is contained in:
parent
49f3f48aa8
commit
fc8a73a9c4
93
src/date.zig
93
src/date.zig
|
@ -83,31 +83,75 @@ pub fn parseIso8601Timestamp(data: []const u8) !i64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dateTimeToTimestamp(datetime: DateTime) !i64 {
|
fn dateTimeToTimestamp(datetime: DateTime) !i64 {
|
||||||
if (datetime.month > 12 or
|
const epoch = DateTime{
|
||||||
datetime.day > 31 or
|
.year = 1970,
|
||||||
datetime.hour >= 24 or
|
.month = 1,
|
||||||
datetime.minute >= 60 or
|
.day = 1,
|
||||||
datetime.second >= 60) return error.DateTimeOutOfRange;
|
.hour = 0,
|
||||||
const epoch_year = 1970;
|
.minute = 0,
|
||||||
if (datetime.year < epoch_year) return error.DatesBeforeEpochNotImplemented;
|
.second = 0,
|
||||||
const leap_years_between = leapYearsBetween(epoch_year, datetime.year);
|
};
|
||||||
|
return secondsBetween(epoch, datetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateTimeToTimestampError = error{
|
||||||
|
DateTimeOutOfRange,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn secondsBetween(start: DateTime, end: DateTime) DateTimeToTimestampError!i64 {
|
||||||
|
try validateDatetime(start);
|
||||||
|
try validateDatetime(end);
|
||||||
|
if (end.year < start.year) return -1 * try secondsBetween(end, start);
|
||||||
|
if (start.month != 1 or
|
||||||
|
start.day != 1 or
|
||||||
|
start.hour != 0 or
|
||||||
|
start.minute != 0 or
|
||||||
|
start.second != 0)
|
||||||
|
{
|
||||||
|
const seconds_into_start_year = secondsFromBeginningOfYear(
|
||||||
|
start.year,
|
||||||
|
start.month,
|
||||||
|
start.day,
|
||||||
|
start.hour,
|
||||||
|
start.minute,
|
||||||
|
start.second,
|
||||||
|
);
|
||||||
|
const new_start = DateTime{
|
||||||
|
.year = start.year,
|
||||||
|
.month = 1,
|
||||||
|
.day = 1,
|
||||||
|
.hour = 0,
|
||||||
|
.minute = 0,
|
||||||
|
.second = 0,
|
||||||
|
};
|
||||||
|
return (try secondsBetween(new_start, end)) - seconds_into_start_year;
|
||||||
|
}
|
||||||
|
const leap_years_between = leapYearsBetween(start.year, end.year);
|
||||||
var add_days: u1 = 0;
|
var add_days: u1 = 0;
|
||||||
const years_diff = std.math.absCast(@as(i17, datetime.year) - @as(i17, epoch_year));
|
const years_diff = end.year - start.year;
|
||||||
std.log.debug("Years from epoch: {d}, Leap years: {d}", .{ years_diff, leap_years_between });
|
std.log.debug("Years from epoch: {d}, Leap years: {d}", .{ years_diff, leap_years_between });
|
||||||
var days_diff: i32 = (years_diff * DAYS_PER_YEAR) + leap_years_between + add_days;
|
var days_diff: i32 = (years_diff * DAYS_PER_YEAR) + leap_years_between + add_days;
|
||||||
std.log.debug("Days with leap year, without month: {d}", .{days_diff});
|
std.log.debug("Days with leap year, without month: {d}", .{days_diff});
|
||||||
|
|
||||||
const seconds_into_year = secondsFromBeginningOfYear(
|
const seconds_into_year = secondsFromBeginningOfYear(
|
||||||
datetime.year,
|
end.year,
|
||||||
datetime.month,
|
end.month,
|
||||||
datetime.day,
|
end.day,
|
||||||
datetime.hour,
|
end.hour,
|
||||||
datetime.minute,
|
end.minute,
|
||||||
datetime.second,
|
end.second,
|
||||||
);
|
);
|
||||||
return (days_diff * SECONDS_PER_DAY) + @as(i64, seconds_into_year);
|
return (days_diff * SECONDS_PER_DAY) + @as(i64, seconds_into_year);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validateDatetime(dt: DateTime) !void {
|
||||||
|
if (dt.month > 12 or
|
||||||
|
dt.day > 31 or
|
||||||
|
dt.hour >= 24 or
|
||||||
|
dt.minute >= 60 or
|
||||||
|
dt.second >= 60) return error.DateTimeOutOfRange;
|
||||||
|
}
|
||||||
|
|
||||||
fn secondsFromBeginningOfYear(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) u32 {
|
fn secondsFromBeginningOfYear(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) u32 {
|
||||||
const current_year_is_leap_year = isLeapYear(year);
|
const current_year_is_leap_year = isLeapYear(year);
|
||||||
const leap_year_days_per_month: [12]u5 = .{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
const leap_year_days_per_month: [12]u5 = .{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||||
|
@ -201,8 +245,6 @@ test "Convert timestamp to datetime" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Convert datetime to timestamp" {
|
test "Convert datetime to timestamp" {
|
||||||
std.testing.log_level = .debug;
|
|
||||||
std.log.debug("\n", .{});
|
|
||||||
try std.testing.expectEqual(@as(i64, 1598607147), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 8, .day = 28, .hour = 9, .minute = 32, .second = 27 }));
|
try std.testing.expectEqual(@as(i64, 1598607147), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 8, .day = 28, .hour = 9, .minute = 32, .second = 27 }));
|
||||||
try std.testing.expectEqual(@as(i64, 1604207167), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 11, .day = 1, .hour = 5, .minute = 6, .second = 7 }));
|
try std.testing.expectEqual(@as(i64, 1604207167), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 11, .day = 1, .hour = 5, .minute = 6, .second = 7 }));
|
||||||
try std.testing.expectEqual(@as(i64, 1440938160), try dateTimeToTimestamp(DateTime{ .year = 2015, .month = 08, .day = 30, .hour = 12, .minute = 36, .second = 00 }));
|
try std.testing.expectEqual(@as(i64, 1440938160), try dateTimeToTimestamp(DateTime{ .year = 2015, .month = 08, .day = 30, .hour = 12, .minute = 36, .second = 00 }));
|
||||||
|
@ -213,17 +255,6 @@ test "Convert ISO8601 string to timestamp" {
|
||||||
try std.testing.expectEqual(@as(i64, 1604207167), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 11, .day = 1, .hour = 5, .minute = 6, .second = 7 }));
|
try std.testing.expectEqual(@as(i64, 1604207167), try dateTimeToTimestamp(DateTime{ .year = 2020, .month = 11, .day = 1, .hour = 5, .minute = 6, .second = 7 }));
|
||||||
try std.testing.expectEqual(@as(i64, 1440938160), try dateTimeToTimestamp(DateTime{ .year = 2015, .month = 08, .day = 30, .hour = 12, .minute = 36, .second = 00 }));
|
try std.testing.expectEqual(@as(i64, 1440938160), try dateTimeToTimestamp(DateTime{ .year = 2015, .month = 08, .day = 30, .hour = 12, .minute = 36, .second = 00 }));
|
||||||
}
|
}
|
||||||
// TODO: I think before epoch, the best approach is to flip the epoch and the
|
test "Convert datetime to timestamp before 1970" {
|
||||||
// input date, calculate the answer, then flip signs. However, this requires
|
try std.testing.expectEqual(@as(i64, -449392815), try dateTimeToTimestamp(DateTime{ .year = 1955, .month = 10, .day = 05, .hour = 16, .minute = 39, .second = 45 }));
|
||||||
// re-designing the algorithm to start from something other than midnight
|
}
|
||||||
// January 1st. This will require an overhaul, so for now, we'll leave
|
|
||||||
// this unimplemented.
|
|
||||||
//
|
|
||||||
// test "Convert datetime to timestamp before 1970" {
|
|
||||||
// std.testing.log_level = .debug;
|
|
||||||
// std.log.debug("\n", .{});
|
|
||||||
// try std.testing.expectEqual(@as(i64, -449392815000), try dateTimeToTimestamp(DateTime{ .year = 1955, .month = 10, .day = 05, .hour = 16, .minute = 39, .second = 45 }));
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// 1955.1 - .1 + x = 1970
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user