wip
This commit is contained in:
parent
15fb429788
commit
40fc6a8d41
3 changed files with 668 additions and 113 deletions
136
flake.lock
136
flake.lock
|
@ -1,18 +1,19 @@
|
|||
{
|
||||
"nodes": {
|
||||
"bash": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697126158,
|
||||
"narHash": "sha256-XoRmgs8U78oVMVzk4riJpkmXaX1Pk2Ya/wYMmTYt2mA=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "443dc212854202ddf2bb3bf29ad6d6c1f8829ff6",
|
||||
"revCount": 11,
|
||||
"type": "git",
|
||||
"url": "https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git"
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git"
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
|
@ -20,11 +21,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -33,6 +34,54 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"zls",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694102001,
|
||||
"narHash": "sha256-vky6VPK1n1od6vXbqzOXnekrQpTL4hbPAwUhT5J9c9E=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "9e21c80adf67ebcb077d75bd5e7d724d21eeafd6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"langref": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"narHash": "sha256-mYdDCBdNEIeMbavdhSo8qXqW+3fqPC8BAich7W3umrI=",
|
||||
"type": "file",
|
||||
"url": "https://raw.githubusercontent.com/ziglang/zig/63bd2bff12992aef0ce23ae4b344e9cb5d65f05d/doc/langref.html.in"
|
||||
},
|
||||
"original": {
|
||||
"type": "file",
|
||||
"url": "https://raw.githubusercontent.com/ziglang/zig/63bd2bff12992aef0ce23ae4b344e9cb5d65f05d/doc/langref.html.in"
|
||||
}
|
||||
},
|
||||
"make-shell": {
|
||||
"locked": {
|
||||
"lastModified": 1634940815,
|
||||
|
@ -50,11 +99,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1697059129,
|
||||
"narHash": "sha256-9NJcFF9CEYPvHJ5ckE8kvINvI84SZZ87PvqMbH6pro0=",
|
||||
"lastModified": 1702312524,
|
||||
"narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5e4c2ada4fcd54b99d56d7bd62f384511a7e2593",
|
||||
"rev": "a9bf124c46ef298113270b1f84a164865987a91c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -65,10 +114,11 @@
|
|||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"bash": "bash",
|
||||
"flake-utils": "flake-utils",
|
||||
"make-shell": "make-shell",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"zig": "zig",
|
||||
"zls": "zls"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
|
@ -85,6 +135,56 @@
|
|||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zig": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1702772729,
|
||||
"narHash": "sha256-y29/F6sZHDXefE+e84DOVfU5rCoTPyH0q5QCrNxRG64=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "1a7d61cc7d3ec69ea418e373a4582bfc765469e3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zls": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"langref": "langref",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"zig-overlay": [
|
||||
"zig"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1702566669,
|
||||
"narHash": "sha256-z7Uq+hXr0CWA4uBZjg+t8FEQkCV0CtGnP0RCgzrjnxo=",
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"rev": "9476a1d47034954367be026b6609062aaefa16ba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "zigtools",
|
||||
"repo": "zls",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
57
flake.nix
57
flake.nix
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
description = "zig-ha";
|
||||
description = "zig-datetime";
|
||||
|
||||
inputs = {
|
||||
nixpkgs = {
|
||||
|
@ -8,45 +8,50 @@
|
|||
flake-utils = {
|
||||
url = "github:numtide/flake-utils";
|
||||
};
|
||||
bash = {
|
||||
url = "git+https://git.ocjtech.us/jeff/nixos-bash-prompt-builder.git";
|
||||
};
|
||||
make-shell = {
|
||||
url = "github:ursi/nix-make-shell";
|
||||
};
|
||||
zig = {
|
||||
url = "github:mitchellh/zig-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
zls = {
|
||||
url = "github:zigtools/zls";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.zig-overlay.follows = "zig";
|
||||
inputs.flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, bash, ... }@inputs:
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
zig,
|
||||
zls,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default =
|
||||
let
|
||||
project = "zig-datetime";
|
||||
prompt = (
|
||||
bash.build_ps1_prompt
|
||||
bash.ansi_normal_blue
|
||||
"${project} - ${bash.username}@${bash.hostname_short}: ${bash.current_working_directory}"
|
||||
"${project}:${bash.current_working_directory}"
|
||||
);
|
||||
make-shell = import inputs.make-shell {
|
||||
inherit system;
|
||||
pkgs = pkgs;
|
||||
};
|
||||
in
|
||||
in {
|
||||
devShells.default = let
|
||||
project = "zig-datetime";
|
||||
make-shell = import inputs.make-shell {
|
||||
inherit system;
|
||||
pkgs = pkgs;
|
||||
};
|
||||
in
|
||||
make-shell {
|
||||
packages = [
|
||||
pkgs.zon2nix
|
||||
pkgs.zig_0_11
|
||||
pkgs.zls
|
||||
zig.packages.${pkgs.system}.master
|
||||
zls.packages.${pkgs.system}.zls
|
||||
];
|
||||
env = {
|
||||
PS1 = prompt;
|
||||
name = project;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
588
src/main.zig
588
src/main.zig
|
@ -1,5 +1,93 @@
|
|||
const std = @import("std");
|
||||
|
||||
// https://www.nist.gov/pml/owm/metric-si-prefixes
|
||||
|
||||
pub const SIPrefix = enum(i8) {
|
||||
quetta = 30,
|
||||
ronna = 27,
|
||||
yotta = 24,
|
||||
zetta = 21,
|
||||
exa = 18,
|
||||
peta = 15,
|
||||
tera = 12,
|
||||
giga = 9,
|
||||
mega = 6,
|
||||
kilo = 3,
|
||||
hecto = 2,
|
||||
deka = 1,
|
||||
deci = -1,
|
||||
centi = -2,
|
||||
milli = -3,
|
||||
micro = -6,
|
||||
nano = -9,
|
||||
pico = -12,
|
||||
femto = -15,
|
||||
atto = -18,
|
||||
zepto = -21,
|
||||
yocto = -24,
|
||||
ronto = -27,
|
||||
quecto = -30,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn exponent(self: Self) i8 {
|
||||
return @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn symbol(self: Self) []const u8 {
|
||||
return switch (self) {
|
||||
.quetta => "Q",
|
||||
.ronna => "R",
|
||||
.yotta => "Y",
|
||||
.zetta => "Z",
|
||||
.exa => "E",
|
||||
.peta => "P",
|
||||
.tera => "T",
|
||||
.giga => "G",
|
||||
.mega => "M",
|
||||
.kilo => "k",
|
||||
.hecto => "h",
|
||||
.deka => "da",
|
||||
.deci => "d",
|
||||
.centi => "c",
|
||||
.milli => "m",
|
||||
.micro => "µ",
|
||||
.nano => "n",
|
||||
.pico => "p",
|
||||
.femto => "f",
|
||||
.atto => "a",
|
||||
.zepto => "z",
|
||||
.yocto => "y",
|
||||
.ronto => "r",
|
||||
.quecto => "q",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn convert(from: i8, to: i8, value: i128) struct { result: i128, remainder: i128 } {
|
||||
const exponent = from - to;
|
||||
if (exponent == 0) return value;
|
||||
if (exponent < 0) {
|
||||
const factor = std.math.pow(i128, 10, @abs(exponent));
|
||||
const remainder = @rem(value, factor);
|
||||
const result = @divTrunc(value, factor);
|
||||
return .{ result, remainder };
|
||||
}
|
||||
if (exponent > 0) {
|
||||
const factor = std.math.pow(i128, 10, exponent);
|
||||
return .{ factor * value, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// test "convert-1" {
|
||||
// const result = convert(.milli, .micro, 1);
|
||||
// try std.testing.expectEqual(i128, result.result, 1000);
|
||||
// try std.testing.expectEqual(i128, result.remainder, 0);
|
||||
// }
|
||||
// pub fn conversionFactor(from: i8, to: i8) !i30 {
|
||||
// if (from < to) return error.InvalidConversion;
|
||||
// if (from == to) return 1;
|
||||
// }
|
||||
pub const Day = u6;
|
||||
|
||||
pub const Month = enum(u4) {
|
||||
|
@ -179,6 +267,24 @@ pub fn writeTwelveHour(hour: Hour, case: enum { lower, upper }, writer: anytype)
|
|||
|
||||
pub const Nanosecond = u30;
|
||||
|
||||
fn readInt(text: []const u8, maxlen: usize) []const u8 {
|
||||
if (text.len == 0) return text[0..0];
|
||||
|
||||
for (0..@min(text.len, maxlen)) |i| {
|
||||
if (!std.ascii.isDigit(text[i])) return text[0 .. i - 1];
|
||||
}
|
||||
|
||||
return text[0..maxlen];
|
||||
}
|
||||
|
||||
fn readFrac(text: []const u8, length: usize) !Nanosecond {
|
||||
if (length == 0) return error.TooShort;
|
||||
if (length > 9) return error.TooLong;
|
||||
const v = readInt(text, length);
|
||||
if (v.len != length) return error.TooShort;
|
||||
return try std.fmt.parseInt(Nanosecond, v, 10) * try std.math.powi(Nanosecond, 10, 9 - @as(Nanosecond, @intCast(length)));
|
||||
}
|
||||
|
||||
// https://github.com/nektro/zig-time
|
||||
|
||||
pub const DateTime = struct {
|
||||
|
@ -193,7 +299,7 @@ pub const DateTime = struct {
|
|||
|
||||
const Self = @This();
|
||||
|
||||
pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
pub fn formatX(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
_ = options;
|
||||
|
||||
if (fmt.len == 0) @compileError("DateTime: format string can't be empty");
|
||||
|
@ -258,7 +364,7 @@ pub const DateTime = struct {
|
|||
if (self.year < 0) {
|
||||
try writer.writeAll("-");
|
||||
}
|
||||
try writer.print("{d:0>4}", .{std.math.absCast(self.year)});
|
||||
try writer.print("{d:0>4}", .{@abs(self.year)});
|
||||
},
|
||||
|
||||
.A => try writeTwelveHour(self.hour, .upper),
|
||||
|
@ -277,15 +383,24 @@ pub const DateTime = struct {
|
|||
.s => try writer.print("{d}", .{self.second}),
|
||||
.ss => try writer.print("{d:0>2}", .{self.second}),
|
||||
|
||||
.S => try writer.print("{d:>1}", .{self.nanosecond / tenthsPerNanoSecond}),
|
||||
.SS => try writer.print("{d:0>2}", .{self.nanosecond / hundredthsPerNanoSecond}),
|
||||
.SSS => try writer.print("{d:0>3}", .{self.nanosecond / milliSecondsPerNanoSecond}),
|
||||
.SSSS => try writer.print("{d:0>4}", .{self.nanosecond / (milliSecondsPerNanoSecond / 10)}),
|
||||
.SSSSS => try writer.print("{d:0>5}", .{self.nanosecond / (milliSecondsPerNanoSecond / 100)}),
|
||||
.SSSSSS => try writer.print("{d:0>6}", .{self.nanosecond / microSecondsPerNanoSecond}),
|
||||
.SSSSSSS => try writer.print("{d:0>7}", .{self.nanosecond / (microSecondsPerNanoSecond / 10)}),
|
||||
.SSSSSSSS => try writer.print("{d:0>8}", .{self.nanosecond / (microSecondsPerNanoSecond / 100)}),
|
||||
.SSSSSSSSS => try writer.print("{d:0>9}", .{self.nanosecond}),
|
||||
// .S => try writer.print("{d:>1}", .{self.nanosecond / tenthsPerNanoSecond}),
|
||||
// .SS => try writer.print("{d:0>2}", .{self.nanosecond / hundredthsPerNanoSecond}),
|
||||
// .SSS => try writer.print("{d:0>3}", .{self.nanosecond / milliSecondsPerNanoSecond}),
|
||||
// .SSSS => try writer.print("{d:0>4}", .{self.nanosecond / (milliSecondsPerNanoSecond / 10)}),
|
||||
// .SSSSS => try writer.print("{d:0>5}", .{self.nanosecond / (milliSecondsPerNanoSecond / 100)}),
|
||||
// .SSSSSS => try writer.print("{d:0>6}", .{self.nanosecond / microSecondsPerNanoSecond}),
|
||||
// .SSSSSSS => try writer.print("{d:0>7}", .{self.nanosecond / (microSecondsPerNanoSecond / 10)}),
|
||||
// .SSSSSSSS => try writer.print("{d:0>8}", .{self.nanosecond / (microSecondsPerNanoSecond / 100)}),
|
||||
// .SSSSSSSSS => try writer.print("{d:0>9}", .{self.nanosecond}),
|
||||
.S => try writer.print("{d:>1}", .{tag.cnv(self.nanosecond)}),
|
||||
.SS => try writer.print("{d:>2}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSS => try writer.print("{d:>3}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSS => try writer.print("{d:>4}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSSS => try writer.print("{d:>5}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSSSS => try writer.print("{d:>6}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSSSSS => try writer.print("{d:>7}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSSSSSS => try writer.print("{d:>8}", .{tag.cnv(self.nanosecond)}),
|
||||
.SSSSSSSSS => try writer.print("{d:>9}", .{tag.cnv(self.nanosecond)}),
|
||||
|
||||
// .z => try writer.writeAll(@tagName(self.timezone)),
|
||||
// .Z => try writer.writeAll("+00:00"),
|
||||
|
@ -313,10 +428,104 @@ pub const DateTime = struct {
|
|||
var list = std.ArrayList(u8).init(alloc);
|
||||
defer list.deinit();
|
||||
|
||||
try self.format(fmt, .{}, list.writer());
|
||||
try self.formatX(fmt, .{}, list.writer());
|
||||
return list.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn parse(comptime fmt: []const u8, value: []const u8) !Self {
|
||||
if (fmt.len == 0) @compileError("DateTime: format string can't be empty");
|
||||
|
||||
@setEvalBranchQuota(100000);
|
||||
|
||||
var date: Self = .{
|
||||
.nanosecond = 0,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
};
|
||||
|
||||
var left = value;
|
||||
|
||||
comptime var start = 0;
|
||||
comptime var end = 0;
|
||||
comptime var next: ?FormatSeq = null;
|
||||
inline for (fmt, 0..) |char, index| {
|
||||
end = index + 1;
|
||||
if (comptime std.meta.stringToEnum(FormatSeq, fmt[start..end])) |tag| {
|
||||
next = tag;
|
||||
if (index < fmt.len - 1) continue;
|
||||
}
|
||||
if (next) |tag| {
|
||||
switch (tag) {
|
||||
.YY => {
|
||||
const year = readInt(left, 2);
|
||||
date.year = try std.fmt.parseInt(Year, year, 10) + 2000;
|
||||
left = left[year.len..];
|
||||
date.weekday = DayOfWeek.dayOfWeek(date.year, date.month, date.day);
|
||||
},
|
||||
.YYYY, .YYY => {
|
||||
const year = readInt(left, 4);
|
||||
date.year = try std.fmt.parseInt(Year, year, 10);
|
||||
left = left[year.len..];
|
||||
date.weekday = DayOfWeek.dayOfWeek(date.year, date.month, date.day);
|
||||
},
|
||||
.SSSSSSSSS => {
|
||||
date.nanosecond = try readFrac(left, 9);
|
||||
left = left[9..];
|
||||
},
|
||||
.SSSSSSSS => {
|
||||
date.nanosecond = try readFrac(left, 8);
|
||||
left = left[8..];
|
||||
},
|
||||
.SSSSSSS => {
|
||||
date.nanosecond = try readFrac(left, 7);
|
||||
left = left[7..];
|
||||
},
|
||||
.SSSSSS => {
|
||||
date.nanosecond = try readFrac(left, 6);
|
||||
left = left[6..];
|
||||
},
|
||||
.SSSSS => {
|
||||
date.nanosecond = try readFrac(left, 5);
|
||||
left = left[5..];
|
||||
},
|
||||
.SSSS => {
|
||||
date.nanosecond = try readFrac(left, 4);
|
||||
left = left[4..];
|
||||
},
|
||||
.SSS => {
|
||||
date.nanosecond = try readFrac(left, 3);
|
||||
left = left[3..];
|
||||
},
|
||||
.SS => {
|
||||
date.nanosecond = try readFrac(left, 2);
|
||||
left = left[2..];
|
||||
},
|
||||
.S => {
|
||||
date.nanosecond = try readFrac(left, 1);
|
||||
left = left[1..];
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
next = null;
|
||||
start = index;
|
||||
}
|
||||
switch (char) {
|
||||
',', ' ', ':', '-', '.', 'T', 'W' => {
|
||||
start = index + 1;
|
||||
continue;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
const FormatSeq = enum {
|
||||
M, // 1 2 ... 11 12 (month, numeric)
|
||||
Mo, // 1st 2nd ... 11th 12th (month, numeric ordinal)
|
||||
|
@ -379,6 +588,37 @@ pub const DateTime = struct {
|
|||
// ZZ, // -0700 -0600 ... +0600 +0700
|
||||
// x, // unix milli
|
||||
// X, // unix
|
||||
|
||||
pub fn cnv(comptime tag: FormatSeq, value: u30) u30 {
|
||||
const name = comptime @tagName(tag);
|
||||
comptime var count: i8 = undefined;
|
||||
inline for (name, 1..) |c, i| {
|
||||
if (c != 'S') @compileError(name ++ " is not a fractional second format sequence");
|
||||
count = i;
|
||||
}
|
||||
if (count > 9) @compileError("fractional seconds smaller than nanoseconds are not supported");
|
||||
if (count == 9) return value;
|
||||
const exponent = 9 - count;
|
||||
const factor = std.math.pow(u30, 10, exponent);
|
||||
return @as(u30, @divTrunc(value, factor));
|
||||
}
|
||||
|
||||
test "cnv" {
|
||||
const cases = [_]struct { tag: FormatSeq, expected: u30, value: u30 }{
|
||||
.{ .tag = .S, .expected = 1, .value = 123456789 },
|
||||
.{ .tag = .SS, .expected = 12, .value = 123456789 },
|
||||
.{ .tag = .SSS, .expected = 123, .value = 123456789 },
|
||||
.{ .tag = .SSSS, .expected = 1234, .value = 123456789 },
|
||||
.{ .tag = .SSSSS, .expected = 12345, .value = 123456789 },
|
||||
.{ .tag = .SSSSSS, .expected = 123456, .value = 123456789 },
|
||||
.{ .tag = .SSSSSSS, .expected = 1234567, .value = 123456789 },
|
||||
.{ .tag = .SSSSSSSS, .expected = 12345678, .value = 123456789 },
|
||||
.{ .tag = .SSSSSSSSS, .expected = 123456789, .value = 123456789 },
|
||||
};
|
||||
inline for (cases) |case| {
|
||||
try std.testing.expectEqual(case.expected, cnv(case.tag, case.value));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn dayOfThisYear(self: Self) u9 {
|
||||
|
@ -386,12 +626,193 @@ pub const DateTime = struct {
|
|||
}
|
||||
};
|
||||
|
||||
test "parseTest" {
|
||||
const cases = [_]struct { value: []const u8, fmt: []const u8, expected: DateTime }{
|
||||
.{
|
||||
.value = "1970",
|
||||
.fmt = "YYYY",
|
||||
.expected = .{
|
||||
.nanosecond = 0,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "2001",
|
||||
.fmt = "YYYY",
|
||||
.expected = .{
|
||||
.nanosecond = 0,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 2001,
|
||||
.weekday = .Mon,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "70",
|
||||
.fmt = "YY",
|
||||
.expected = .{
|
||||
.nanosecond = 0,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 2070,
|
||||
.weekday = .Wed,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "123456789",
|
||||
.fmt = "SSSSSSSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123456789,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "12345678",
|
||||
.fmt = "SSSSSSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123456780,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "1234567",
|
||||
.fmt = "SSSSSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123456700,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "123456",
|
||||
.fmt = "SSSSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123456000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "12345",
|
||||
.fmt = "SSSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123450000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "1234",
|
||||
.fmt = "SSSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123400000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "123",
|
||||
.fmt = "SSS",
|
||||
.expected = .{
|
||||
.nanosecond = 123000000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "12",
|
||||
.fmt = "SS",
|
||||
.expected = .{
|
||||
.nanosecond = 120000000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.value = "1",
|
||||
.fmt = "S",
|
||||
.expected = .{
|
||||
.nanosecond = 100000000,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
.hour = 0,
|
||||
.day = 1,
|
||||
.month = .Jan,
|
||||
.year = 1970,
|
||||
.weekday = .Thu,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
inline for (cases) |case| {
|
||||
const result = try DateTime.parse(case.fmt, case.value);
|
||||
std.testing.expectEqual(case.expected, result) catch |err| {
|
||||
std.debug.print("\n{s} {s} {} {}\n", .{ case.value, case.fmt, case.expected, result });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
test "formatTest" {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
const allocator = fba.allocator();
|
||||
|
||||
const epoch = DateTime{
|
||||
const test_date = DateTime{
|
||||
.nanosecond = 123456789,
|
||||
.second = 0,
|
||||
.minute = 0,
|
||||
|
@ -401,49 +822,50 @@ test "formatTest" {
|
|||
.year = 1970,
|
||||
.weekday = .Fri,
|
||||
};
|
||||
|
||||
const cases = [_]struct { datetime: DateTime, fmt: []const u8, result: []const u8 }{
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSSSSSSS",
|
||||
.result = "1970-01-01T00:00:00.123456789",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSSSSSS",
|
||||
.result = "1970-01-01T00:00:00.12345678",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSSSSS",
|
||||
.result = "1970-01-01T00:00:00.1234567",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSSSS",
|
||||
.result = "1970-01-01T00:00:00.123456",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSSS",
|
||||
.result = "1970-01-01T00:00:00.12345",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSSS",
|
||||
.result = "1970-01-01T00:00:00.1234",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SSS",
|
||||
.result = "1970-01-01T00:00:00.123",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.SS",
|
||||
.result = "1970-01-01T00:00:00.12",
|
||||
},
|
||||
.{
|
||||
.datetime = epoch,
|
||||
.datetime = test_date,
|
||||
.fmt = "YYYY-MM-DDTHH:mm:ss.S",
|
||||
.result = "1970-01-01T00:00:00.1",
|
||||
},
|
||||
|
@ -453,7 +875,7 @@ test "formatTest" {
|
|||
var array = std.ArrayList(u8).init(allocator);
|
||||
defer array.deinit();
|
||||
|
||||
try case.datetime.format(case.fmt, .{}, array.writer());
|
||||
try case.datetime.formatX(case.fmt, .{}, array.writer());
|
||||
|
||||
std.testing.expect(std.mem.eql(u8, array.items, case.result)) catch |err| {
|
||||
std.debug.print("{s} {s} {s}\n", .{
|
||||
|
@ -554,7 +976,7 @@ pub const Instant = struct {
|
|||
.minute = minute,
|
||||
.second = second,
|
||||
.nanosecond = nanosecond,
|
||||
.weekday = weekdayFromDays(days),
|
||||
.weekday = DayOfWeek.weekdayFromDays(days),
|
||||
};
|
||||
}
|
||||
// pub fn format(self: @This()) []const u8 {
|
||||
|
@ -807,6 +1229,17 @@ const DayOfWeek = enum(u3) {
|
|||
pub fn isoWeekdayNumber(self: Self) u3 {
|
||||
return if (self == .Sun) 7 else @intFromEnum(self);
|
||||
}
|
||||
|
||||
pub fn weekdayFromDays(days: i32) DayOfWeek {
|
||||
return @as(
|
||||
DayOfWeek,
|
||||
@enumFromInt(if (days >= -4) @rem(days + 4, 7) else @rem(days + 5, 7) + 6),
|
||||
);
|
||||
}
|
||||
pub fn dayOfWeek(year: Year, month: Month, day: Day) Self {
|
||||
const days = daysFromCivil(year, month, day);
|
||||
return Self.weekdayFromDays(days);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn weekdayDifference(start: DayOfWeek, end: DayOfWeek) u3 {
|
||||
|
@ -836,13 +1269,6 @@ test "weekdayDifference" {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn weekdayFromDays(z: i32) DayOfWeek {
|
||||
return @as(
|
||||
DayOfWeek,
|
||||
@enumFromInt(if (z >= -4) @rem(z + 4, 7) else @rem(z + 5, 7) + 6),
|
||||
);
|
||||
}
|
||||
|
||||
test "weekdayFromDays" {
|
||||
const tests = [_]struct { days: i32, result: DayOfWeek }{
|
||||
.{
|
||||
|
@ -864,7 +1290,7 @@ test "weekdayFromDays" {
|
|||
};
|
||||
|
||||
for (tests) |case| {
|
||||
const result = weekdayFromDays(case.days);
|
||||
const result = DayOfWeek.weekdayFromDays(case.days);
|
||||
try std.testing.expect(std.meta.eql(case.result, result));
|
||||
}
|
||||
}
|
||||
|
@ -1000,7 +1426,7 @@ test "testPrintOrdinalSuperscript" {
|
|||
inline for (cases) |case| {
|
||||
var buffer: [16]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buffer);
|
||||
var writer = stream.writer();
|
||||
const writer = stream.writer();
|
||||
|
||||
try printOrdinalSuperscript(writer, case.number);
|
||||
std.testing.expect(std.mem.eql(u8, stream.getWritten(), case.result)) catch |err| {
|
||||
|
@ -1015,7 +1441,7 @@ fn printLongName(writer: anytype, index: u16, names: []const []const u8) !void {
|
|||
}
|
||||
|
||||
fn wrap(val: u16, at: u16) u16 {
|
||||
var tmp = val % at;
|
||||
const tmp = val % at;
|
||||
return if (tmp == 0) at else tmp;
|
||||
}
|
||||
|
||||
|
@ -1023,8 +1449,32 @@ test "asDateTime" {
|
|||
_ = Instant.utc().asDateTime();
|
||||
}
|
||||
|
||||
fn isLeap(year: i32) bool {
|
||||
return @rem(year, 4) == 0 and (@rem(year, 100) != 0 or @rem(year, 400) == 0);
|
||||
pub fn isLeap(year: Year) bool {
|
||||
// taken from https://github.com/ziglang/zig/pull/18451
|
||||
|
||||
// In the western Gregorian Calendar leap a year is
|
||||
// a multiple of 4, excluding multiples of 100, and
|
||||
// adding multiples of 400. In code:
|
||||
//
|
||||
// if (@mod(year, 4) != 0)
|
||||
// return false;
|
||||
// if (@mod(year, 100) != 0)
|
||||
// return true;
|
||||
// return (0 == @mod(year, 400));
|
||||
|
||||
// The following is equivalent to the above
|
||||
// but uses bitwise operations when testing
|
||||
// for divisibility, masking with 3 as test
|
||||
// for multiples of 4 and with 15 as a test
|
||||
// for multiples of 16. Multiples of 16 and
|
||||
// 100 are, conveniently, multiples of 400.
|
||||
|
||||
const mask: Year = switch (year % 100) {
|
||||
0 => 0b1111,
|
||||
else => 0b11,
|
||||
};
|
||||
return 0 == year & mask;
|
||||
// return @rem(year, 4) == 0 and (@rem(year, 100) != 0 or @rem(year, 400) == 0);
|
||||
}
|
||||
|
||||
test "isLeap" {
|
||||
|
@ -1034,36 +1484,36 @@ test "isLeap" {
|
|||
try std.testing.expectEqual(true, isLeap(2400));
|
||||
}
|
||||
|
||||
test "bigTest" {
|
||||
const ystart = -1000000;
|
||||
var prev_z: i32 = daysFromCivil(ystart, .Jan, 1) - 1;
|
||||
try std.testing.expect(prev_z < 0);
|
||||
var prev_wd = weekdayFromDays(prev_z);
|
||||
try std.testing.expect(0 <= @intFromEnum(prev_wd) and @intFromEnum(prev_wd) <= 6);
|
||||
var y: Year = ystart;
|
||||
while (y <= -ystart) {
|
||||
for ([_]Month{ .Jan, .Feb, .Mar, .Apr, .May, .Jun, .Jul, .Aug, .Sep, .Oct, .Nov, .Dec }) |m| {
|
||||
var d: Day = 1;
|
||||
const e = m.lastDay(y);
|
||||
while (d <= e) {
|
||||
// std.debug.print("{d} {d} {d}\n", .{ y, @intFromEnum(m), d });
|
||||
const z = daysFromCivil(y, m, d);
|
||||
// std.debug.print("{d} {d}\n", .{ prev_z, z });
|
||||
try std.testing.expect(prev_z < z);
|
||||
try std.testing.expect(z == prev_z + 1);
|
||||
const date = civilFromDays(z);
|
||||
try std.testing.expect(y == date.year);
|
||||
try std.testing.expect(m == date.month);
|
||||
try std.testing.expect(d == date.day);
|
||||
const wd = weekdayFromDays(z);
|
||||
try std.testing.expect(0 <= @intFromEnum(wd) and @intFromEnum(wd) <= 6);
|
||||
try std.testing.expect(wd == prev_wd.next());
|
||||
try std.testing.expect(prev_wd == wd.prev());
|
||||
prev_z = z;
|
||||
prev_wd = wd;
|
||||
d += 1;
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
// test "bigTest" {
|
||||
// const ystart = -1000000;
|
||||
// var prev_z: i32 = daysFromCivil(ystart, .Jan, 1) - 1;
|
||||
// try std.testing.expect(prev_z < 0);
|
||||
// var prev_wd = DayOfWeek.weekdayFromDays(prev_z);
|
||||
// try std.testing.expect(0 <= @intFromEnum(prev_wd) and @intFromEnum(prev_wd) <= 6);
|
||||
// var y: Year = ystart;
|
||||
// while (y <= -ystart) {
|
||||
// for ([_]Month{ .Jan, .Feb, .Mar, .Apr, .May, .Jun, .Jul, .Aug, .Sep, .Oct, .Nov, .Dec }) |m| {
|
||||
// var d: Day = 1;
|
||||
// const e = m.lastDay(y);
|
||||
// while (d <= e) {
|
||||
// // std.debug.print("{d} {d} {d}\n", .{ y, @intFromEnum(m), d });
|
||||
// const z = daysFromCivil(y, m, d);
|
||||
// // std.debug.print("{d} {d}\n", .{ prev_z, z });
|
||||
// try std.testing.expect(prev_z < z);
|
||||
// try std.testing.expect(z == prev_z + 1);
|
||||
// const date = civilFromDays(z);
|
||||
// try std.testing.expect(y == date.year);
|
||||
// try std.testing.expect(m == date.month);
|
||||
// try std.testing.expect(d == date.day);
|
||||
// const wd = DayOfWeek.weekdayFromDays(z);
|
||||
// try std.testing.expect(0 <= @intFromEnum(wd) and @intFromEnum(wd) <= 6);
|
||||
// try std.testing.expect(wd == prev_wd.next());
|
||||
// try std.testing.expect(prev_wd == wd.prev());
|
||||
// prev_z = z;
|
||||
// prev_wd = wd;
|
||||
// d += 1;
|
||||
// }
|
||||
// }
|
||||
// y += 1;
|
||||
// }
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue