diff --git a/src/timezone.zig b/src/timezone.zig index be16555..a6fda6e 100644 --- a/src/timezone.zig +++ b/src/timezone.zig @@ -117,7 +117,7 @@ pub const Posix = struct { const julian = try std.fmt.parseInt(u9, str[1..], 10); return .{ .julian = .{ .day = julian } }; }, - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => |_| { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { const julian = try std.fmt.parseInt(u9, str, 10); return .{ .julian_leap = .{ .day = julian } }; }, @@ -522,7 +522,7 @@ pub const TZInfo = struct { }, }; - pub fn parse(allocator: std.mem.Allocator, reader: *std.io.Reader) !TZInfo { + pub fn parse(allocator: std.mem.Allocator, reader: *std.Io.Reader) !TZInfo { var legacy_header = try reader.takeStruct(Header, .big); // handles endianness for us if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader; if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3') return error.BadVersion; @@ -545,7 +545,7 @@ pub const TZInfo = struct { } } - fn parseBlock(allocator: std.mem.Allocator, reader: *std.io.Reader, header: Header, legacy: bool) !TZInfo { + fn parseBlock(allocator: std.mem.Allocator, reader: *std.Io.Reader, header: Header, legacy: bool) !TZInfo { if (header.counts.isstdcnt != 0 and header.counts.isstdcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isstdcnt [...] MUST either be zero or equal to "typecnt" if (header.counts.isutcnt != 0 and header.counts.isutcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isutcnt [...] MUST either be zero or equal to "typecnt" if (header.counts.typecnt == 0) return error.Malformed; // rfc8536: typecnt [...] MUST NOT be zero @@ -861,7 +861,7 @@ pub const Windows = struct { const days_from_epoch = @divFloor(timestamp, s_per_day); // first_of_month is the weekday on the first of the month - const first_of_month = zeit.weekdayFromDays(days_from_epoch - time.wDay + 1); + const first_of_month = zeit.weekdayFromDays(@intCast(days_from_epoch - time.wDay + 1)); // In the start transition month if (time.wMonth == start.wMonth) { diff --git a/src/zeit.zig b/src/zeit.zig index 7e89060..66eaf1c 100644 --- a/src/zeit.zig +++ b/src/zeit.zig @@ -13,6 +13,11 @@ pub const Nanoseconds = i128; pub const Milliseconds = i128; pub const Seconds = i64; +pub const EnvConfig = struct { + tz: ?[]const u8 = null, + tzdir: ?[]const u8 = null, +}; + const ns_per_us = std.time.ns_per_us; const ns_per_ms = std.time.ns_per_ms; const ns_per_s = std.time.ns_per_s; @@ -30,23 +35,21 @@ pub const utc: TimeZone = .{ .fixed = .{ .is_dst = false, } }; -pub fn local(alloc: std.mem.Allocator, maybe_env: ?*const std.process.EnvMap) !TimeZone { +pub fn local(alloc: std.mem.Allocator, io: std.Io, env: EnvConfig) !TimeZone { switch (builtin.os.tag) { .windows => { const win = try timezone.Windows.local(alloc); return .{ .windows = win }; }, else => { - if (maybe_env) |env| { - if (env.get("TZ")) |tz| { - return localFromEnv(alloc, tz, env); - } + if (env.tz) |tz| { + return localFromEnv(alloc, io, tz, env); } - const f = try std.fs.cwd().openFile("/etc/localtime", .{}); - defer f.close(); + const f = try std.Io.Dir.cwd().openFile(io, "/etc/localtime", .{}); + defer f.close(io); var io_buffer: [2048]u8 = undefined; - var reader = f.reader(&io_buffer); + var reader = f.reader(io, &io_buffer); return .{ .tzinfo = try timezone.TZInfo.parse(alloc, &reader.interface) }; }, } @@ -59,8 +62,9 @@ pub fn local(alloc: std.mem.Allocator, maybe_env: ?*const std.process.EnvMap) !T // 3. A relative path, prefixed with ':' fn localFromEnv( alloc: std.mem.Allocator, + io: std.Io, tz: []const u8, - env: *const std.process.EnvMap, + env: EnvConfig, ) !TimeZone { assert(tz.len != 0); // TZ is empty string @@ -69,23 +73,24 @@ fn localFromEnv( assert(tz.len > 1); // TZ not long enough if (tz[1] == '/') { - const f = try std.fs.cwd().openFile(tz[1..], .{}); - defer f.close(); + const f = try std.Io.Dir.cwd().openFile(io, tz[1..], .{}); + defer f.close(io); var io_buffer: [1024]u8 = undefined; - var reader = f.reader(&io_buffer); + var reader = f.reader(io, &io_buffer); return .{ .tzinfo = try timezone.TZInfo.parse(alloc, &reader.interface) }; } if (std.meta.stringToEnum(Location, tz[1..])) |loc| - return loadTimeZone(alloc, loc, env) + return loadTimeZone(alloc, io, loc, env) else return error.UnknownLocation; } pub fn loadTimeZone( alloc: std.mem.Allocator, + io: std.Io, loc: Location, - maybe_env: ?*const std.process.EnvMap, + env: EnvConfig, ) !TimeZone { switch (builtin.os.tag) { .windows => { @@ -95,13 +100,11 @@ pub fn loadTimeZone( else => {}, } - var dir: std.fs.Dir = blk: { + var dir: std.Io.Dir = blk: { // If we have an env and a TZDIR, use that - if (maybe_env) |env| { - if (env.get("TZDIR")) |tzdir| { - const dir = try std.fs.openDirAbsolute(tzdir, .{}); - break :blk dir; - } + if (env.tzdir) |tzdir| { + const d = try std.Io.Dir.cwd().openDir(io, tzdir, .{}); + break :blk d; } // Otherwise check well-known locations const zone_dirs = [_][]const u8{ @@ -112,16 +115,16 @@ pub fn loadTimeZone( "/etc/zoneinfo/", }; for (zone_dirs) |zone_dir| { - const dir = std.fs.openDirAbsolute(zone_dir, .{}) catch continue; - break :blk dir; + const d = std.Io.Dir.cwd().openDir(io, zone_dir, .{}) catch continue; + break :blk d; } else return error.FileNotFound; }; - defer dir.close(); - const f = try dir.openFile(loc.asText(), .{}); - defer f.close(); + defer dir.close(io); + const f = try dir.openFile(io, loc.asText(), .{}); + defer f.close(io); var io_buffer: [2048]u8 = undefined; - var reader = f.reader(&io_buffer); + var reader = f.reader(io, &io_buffer); return .{ .tzinfo = try timezone.TZInfo.parse(alloc, &reader.interface) }; } @@ -136,6 +139,7 @@ pub const Instant = struct { pub const Config = struct { source: Source = .now, timezone: *const TimeZone = &utc, + io: ?std.Io = null, }; /// possible sources to create an Instant @@ -272,7 +276,7 @@ pub const Instant = struct { /// create a new Instant pub fn instant(cfg: Instant.Config) !Instant { const ts: Nanoseconds = switch (cfg.source) { - .now => std.time.nanoTimestamp(), + .now => (try std.Io.Clock.now(.real, cfg.io orelse return error.NeedIo)).toNanoseconds(), .unix_timestamp => |unix| @as(i128, unix) * ns_per_s, .unix_nano => |nano| nano, .time => |time| time.instant().timestamp, @@ -300,10 +304,7 @@ pub fn instant(cfg: Instant.Config) !Instant { } test "instant" { - const original = Instant{ - .timestamp = std.time.nanoTimestamp(), - .timezone = &utc, - }; + const original = try instant(.{ .io = std.testing.io }); const time = original.time(); const round_trip = time.instant(); try std.testing.expectEqual(original.timestamp, round_trip.timestamp); @@ -1256,8 +1257,30 @@ pub const Time = struct { } } + pub const FmtKind = enum { gofmt, strftime }; + pub fn timeFmt(self: Time, kind: FmtKind, fmt_str: []const u8) TimeFmt { + return .{ + .time = self, + .kind = kind, + .str = fmt_str, + }; + } + + const TimeFmt = struct { + time: Time, + kind: FmtKind, + str: []const u8, + + pub fn format(self: TimeFmt, writer: *std.Io.Writer) !void { + switch (self.kind) { + .gofmt => self.time.gofmt(writer, self.str) catch return error.WriteFailed, + .strftime => self.time.strftime(writer, self.str) catch return error.WriteFailed, + } + } + }; + /// Format time using strftime(3) specified, eg %Y-%m-%dT%H:%M:%S - pub fn strftime(self: Time, writer: anytype, fmt: []const u8) !void { + pub fn strftime(self: Time, writer: *std.Io.Writer, fmt: []const u8) !void { const inst = self.instant(); var i: usize = 0; while (i < fmt.len) { @@ -1425,7 +1448,7 @@ pub const Time = struct { } /// Format using golang magic date format. - pub fn gofmt(self: Time, writer: anytype, fmt: []const u8) !void { + pub fn gofmt(self: Time, writer: *std.Io.Writer, fmt: []const u8) !void { var i: usize = 0; while (i < fmt.len) : (i += 1) { const b = fmt[i]; @@ -1644,7 +1667,7 @@ pub const Time = struct { ); try writer.writeAll(str[0..@min(n, str.len)]); if (n > str.len) - try writer.writeByteNTimes('0', n - str.len); + try writer.splatByteAll('0', n - str.len); }, '9' => { var n: usize = 0; @@ -1881,59 +1904,57 @@ test "fmtStrftime" { const epoch = try instant(.{ .source = .{ .unix_timestamp = 0 } }); const time = epoch.time(); - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); + var writer = std.Io.Writer.fixed(&buf); - try std.testing.expectError(error.InvalidFormat, time.strftime(writer, "no trailing lone percent %")); + try std.testing.expectError(error.InvalidFormat, time.strftime(&writer, "no trailing lone percent %")); - fbs.reset(); - try time.strftime(writer, "%%"); - try std.testing.expectEqualStrings("%", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%%"); + try std.testing.expectEqualStrings("%", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%a %A %b %B %c %C"); - try std.testing.expectEqualStrings("Thu Thursday Jan January Thu Jan 1 00:00:00 1970 19", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%a %A %b %B %c %C"); + try std.testing.expectEqualStrings("Thu Thursday Jan January Thu Jan 1 00:00:00 1970 19", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%d %D %e %F %h"); - try std.testing.expectEqualStrings("01 01/01/70 1 1970-01-01 Jan", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%d %D %e %F %h"); + try std.testing.expectEqualStrings("01 01/01/70 1 1970-01-01 Jan", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%H %I %j %k %l %m %M"); - try std.testing.expectEqualStrings("00 12 001 0 12 01 00", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%H %I %j %k %l %m %M"); + try std.testing.expectEqualStrings("00 12 001 0 12 01 00", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%p %P %r %R %s %S"); - try std.testing.expectEqualStrings("AM am 12:00:00 AM 00:00 0 00", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%p %P %r %R %s %S"); + try std.testing.expectEqualStrings("AM am 12:00:00 AM 00:00 0 00", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%T %u"); - try std.testing.expectEqualStrings("00:00:00 4", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%T %u"); + try std.testing.expectEqualStrings("00:00:00 4", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%U"); - try std.testing.expectEqualStrings("00", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%U"); + try std.testing.expectEqualStrings("00", writer.buffered()); - fbs.reset(); + writer.end = 0; const d2 = (try time.instant().add(.{ .days = 3 })).time(); - try d2.strftime(writer, "%U"); - try std.testing.expectEqualStrings("01", fbs.getWritten()); + try d2.strftime(&writer, "%U"); + try std.testing.expectEqualStrings("01", writer.buffered()); - fbs.reset(); - try time.strftime(writer, "%w %W %x %X %y %Y %z %Z"); - try std.testing.expectEqualStrings("4 00 01/01/70 00:00:00 70 1970 +0000 UTC", fbs.getWritten()); + writer.end = 0; + try time.strftime(&writer, "%w %W %x %X %y %Y %z %Z"); + try std.testing.expectEqualStrings("4 00 01/01/70 00:00:00 70 1970 +0000 UTC", writer.buffered()); - fbs.reset(); + writer.end = 0; var d3 = time; d3.offset = -3600; - try d3.strftime(writer, "%z"); - try std.testing.expectEqualStrings("-0100", fbs.getWritten()); + try d3.strftime(&writer, "%z"); + try std.testing.expectEqualStrings("-0100", writer.buffered()); } test "gofmt" { var buf: [128]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); + var writer = std.Io.Writer.fixed(&buf); const time: Time = .{ .year = 1970, @@ -1942,17 +1963,17 @@ test "gofmt" { .designation = "UTC", }; - fbs.reset(); - try time.gofmt(writer, "Jan January J 01 02 03 04 05 06 002 Jan"); - try std.testing.expectEqualStrings("Feb February J 02 03 12 00 00 70 034 Feb", fbs.getWritten()); + writer.end = 0; + try time.gofmt(&writer, "Jan January J 01 02 03 04 05 06 002 Jan"); + try std.testing.expectEqualStrings("Feb February J 02 03 12 00 00 70 034 Feb", writer.buffered()); - fbs.reset(); - try time.gofmt(writer, "Mon Monday MST M 1 15 2 2006 _2 __2 Mon"); - try std.testing.expectEqualStrings("Tue Tuesday UTC M 2 00 3 1970 3 34 Tue", fbs.getWritten()); + writer.end = 0; + try time.gofmt(&writer, "Mon Monday MST M 1 15 2 2006 _2 __2 Mon"); + try std.testing.expectEqualStrings("Tue Tuesday UTC M 2 00 3 1970 3 34 Tue", writer.buffered()); - fbs.reset(); - try time.gofmt(writer, "3 4 5"); - try std.testing.expectEqualStrings("12 0 0", fbs.getWritten()); + writer.end = 0; + try time.gofmt(&writer, "3 4 5"); + try std.testing.expectEqualStrings("12 0 0", writer.buffered()); const time2: Time = .{ .offset = 3661, // 1 hour, 1 minute, 1 second @@ -1961,37 +1982,104 @@ test "gofmt" { .nanosecond = 789, }; - fbs.reset(); - try time2.gofmt(writer, "-070000 -07:00:00 -0700 -07:00 -07 -00"); - try std.testing.expectEqualStrings("+010101 +01:01:01 +0101 +01:01 +01 -00", fbs.getWritten()); + writer.end = 0; + try time2.gofmt(&writer, "-070000 -07:00:00 -0700 -07:00 -07 -00"); + try std.testing.expectEqualStrings("+010101 +01:01:01 +0101 +01:01 +01 -00", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00"); + try std.testing.expectEqualStrings("+010101 +01:01:01 +0101 +01:01 +01 Z00", writer.buffered()); + + writer.end = 0; + try time.gofmt(&writer, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00"); + try std.testing.expectEqualStrings("Z Z Z Z Z Z00", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "frac ."); + try std.testing.expectEqualStrings("frac .", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "frac .000000000"); + try std.testing.expectEqualStrings("frac .123456789", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "frac .999999999"); + try std.testing.expectEqualStrings("frac .123456789", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "frac .000000000000"); + try std.testing.expectEqualStrings("frac .123456789000", writer.buffered()); + + writer.end = 0; + try time2.gofmt(&writer, "frac .0000000"); + try std.testing.expectEqualStrings("frac .1234567", writer.buffered()); + + const time3: Time = .{ + .offset = 3661, // 1 hour, 1 minute, 1 second + .millisecond = 123, + .microsecond = 456, + }; + + writer.end = 0; + try time3.gofmt(&writer, "frac .999999999"); + try std.testing.expectEqualStrings("frac .123456", writer.buffered()); +} + +test "Time.timeFmt" { + const time: Time = .{ + .year = 1970, + .month = .feb, + .day = 3, + .designation = "UTC", + }; + try std.testing.expectFmt( + "Feb February J 02 03 12 00 00 70 034 Feb", + "{f}", + .{time.timeFmt(.gofmt, "Jan January J 01 02 03 04 05 06 002 Jan")}, + ); - fbs.reset(); - try time2.gofmt(writer, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00"); - try std.testing.expectEqualStrings("+010101 +01:01:01 +0101 +01:01 +01 Z00", fbs.getWritten()); + try std.testing.expectFmt( + "Tue Tuesday UTC M 2 00 3 1970 3 34 Tue", + "{f}", + .{time.timeFmt(.gofmt, "Mon Monday MST M 1 15 2 2006 _2 __2 Mon")}, + ); - fbs.reset(); - try time.gofmt(writer, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00"); - try std.testing.expectEqualStrings("Z Z Z Z Z Z00", fbs.getWritten()); + try std.testing.expectFmt( + "12 0 0", + "{f}", + .{time.timeFmt(.gofmt, "3 4 5")}, + ); - fbs.reset(); - try time2.gofmt(writer, "frac ."); - try std.testing.expectEqualStrings("frac .", fbs.getWritten()); + const time2: Time = .{ + .offset = 3661, // 1 hour, 1 minute, 1 second + .millisecond = 123, + .microsecond = 456, + .nanosecond = 789, + }; - fbs.reset(); - try time2.gofmt(writer, "frac .000000000"); - try std.testing.expectEqualStrings("frac .123456789", fbs.getWritten()); + try std.testing.expectFmt( + "+010101 +01:01:01 +0101 +01:01 +01 -00", + "{f}", + .{time2.timeFmt(.gofmt, "-070000 -07:00:00 -0700 -07:00 -07 -00")}, + ); - fbs.reset(); - try time2.gofmt(writer, "frac .999999999"); - try std.testing.expectEqualStrings("frac .123456789", fbs.getWritten()); + try std.testing.expectFmt( + "+010101 +01:01:01 +0101 +01:01 +01 Z00", + "{f}", + .{time2.timeFmt(.gofmt, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00")}, + ); - fbs.reset(); - try time2.gofmt(writer, "frac .000000000000"); - try std.testing.expectEqualStrings("frac .123456789000", fbs.getWritten()); + try std.testing.expectFmt( + "Z Z Z Z Z Z00", + "{f}", + .{time.timeFmt(.gofmt, "Z070000 Z07:00:00 Z0700 Z07:00 Z07 Z00")}, + ); - fbs.reset(); - try time2.gofmt(writer, "frac .0000000"); - try std.testing.expectEqualStrings("frac .1234567", fbs.getWritten()); + try std.testing.expectFmt( + "frac .", + "{f}", + .{time2.timeFmt(.gofmt, "frac .")}, + ); const time3: Time = .{ .offset = 3661, // 1 hour, 1 minute, 1 second @@ -1999,24 +2087,52 @@ test "gofmt" { .microsecond = 456, }; - fbs.reset(); - try time3.gofmt(writer, "frac .999999999"); - try std.testing.expectEqualStrings("frac .123456", fbs.getWritten()); + try std.testing.expectFmt( + "frac .123456", + "{f}", + .{time3.timeFmt(.gofmt, "frac .999999999")}, + ); + + const epoch = try instant(.{ .source = .{ .unix_timestamp = 0 } }); + const time4 = epoch.time(); + + var buf: [128]u8 = undefined; + var writer = std.Io.Writer.fixed(&buf); + try std.testing.expectError(error.WriteFailed, writer.print( + "{f}", + .{time4.timeFmt(.strftime, "no trailing lone percent %")}, + )); + + try std.testing.expectFmt( + "%", + "{f}", + .{time4.timeFmt(.strftime, "%%")}, + ); + + try std.testing.expectFmt( + "Thu Thursday Jan January Thu Jan 1 00:00:00 1970 19", + "{f}", + .{time4.timeFmt(.strftime, "%a %A %b %B %c %C")}, + ); + + try std.testing.expectFmt( + "01 01/01/70 1 1970-01-01 Jan", + "{f}", + .{time4.timeFmt(.strftime, "%d %D %e %F %h")}, + ); } test Instant { const zeit = @This(); const alloc = std.testing.allocator; - var env = try std.process.getEnvMap(alloc); - defer env.deinit(); // Get an instant in time. The default gets "now" in UTC - const now = try instant(.{}); + const now = try instant(.{ .io = std.testing.io }); - // Load our local timezone. This needs an allocator. Optionally pass in a - // *const std.process.EnvMap to support TZ and TZDIR environment variables - const local_tz = try zeit.local(alloc, &env); + // Load our local timezone. This needs an allocator. Optionally pass in an + // EnvConfig to support TZ and TZDIR environment variables + const local_tz = try zeit.local(alloc, std.testing.io, .{}); defer local_tz.deinit(); // Convert our instant to a new timezone @@ -2041,17 +2157,18 @@ test Instant { // .offset = -18000, // } - const anywriter = std.io.null_writer.any(); + var discard_buf: [256]u8 = undefined; + var discarding: std.Io.Writer.Discarding = .init(&discard_buf); // Format using strftime specifier. Format strings are not required to be comptime - try dt.strftime(anywriter, "%Y-%m-%d %H:%M:%S %Z"); + try dt.strftime(&discarding.writer, "%Y-%m-%d %H:%M:%S %Z"); // Or...golang magic date specifiers. Format strings are not required to be comptime - try dt.gofmt(anywriter, "2006-01-02 15:04:05 MST"); + try dt.gofmt(&discarding.writer, "2006-01-02 15:04:05 MST"); // Load an arbitrary location using IANA location syntax. The location name // comes from an enum which will automatically map IANA location names to - // Windows names, as needed. Pass an optional EnvMap to support TZDIR - const vienna = try zeit.loadTimeZone(alloc, .@"Europe/Vienna", &env); + // Windows names, as needed. Pass an optional EnvConfig to support TZDIR + const vienna = try zeit.loadTimeZone(alloc, std.testing.io, .@"Europe/Vienna", .{}); defer vienna.deinit(); // Parse an Instant from an ISO8601 or RFC3339 string @@ -2071,11 +2188,9 @@ test Instant { test "github.com/rockorager/zeit/issues/15" { // https://github.com/rockorager/zeit/issues/15 const timestamp = 1732838300; - const tz = try loadTimeZone(std.testing.allocator, .@"Europe/Berlin", null); + const tz = try loadTimeZone(std.testing.allocator, std.testing.io, .@"Europe/Berlin", .{}); defer tz.deinit(); const inst = try instant(.{ .source = .{ .unix_timestamp = timestamp }, .timezone = &tz }); - var list = std.ArrayList(u8).empty; - defer list.deinit(std.testing.allocator); const time = inst.time(); try std.testing.expectEqual(timestamp, time.instant().unixTimestamp()); @@ -2087,12 +2202,14 @@ test "github.com/rockorager/zeit/issues/15" { try std.testing.expectEqual(58, time.minute); try std.testing.expectEqual(20, time.second); - try time.strftime(list.writer(std.testing.allocator), "%a %A %u"); - try std.testing.expectEqualStrings("Fri Friday 5", list.items); + var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); + try time.strftime(&aw.writer, "%a %A %u"); + try std.testing.expectEqualStrings("Fri Friday 5", aw.writer.buffered()); - list.clearRetainingCapacity(); - try time.gofmt(list.writer(std.testing.allocator), "Mon Monday"); - try std.testing.expectEqualStrings("Fri Friday", list.items); + aw.writer.end = 0; + try time.gofmt(&aw.writer, "Mon Monday"); + try std.testing.expectEqualStrings("Fri Friday", aw.writer.buffered()); } test "github.com/rockorager/zeit/issues/27" { @@ -2100,13 +2217,13 @@ test "github.com/rockorager/zeit/issues/27" { const timestamp = 1745414170; const inst = try instant(.{ .source = .{ .unix_timestamp = timestamp } }); - var list: std.ArrayList(u8) = .empty; - defer list.deinit(std.testing.allocator); + var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); const time = inst.time(); - try time.gofmt(list.writer(std.testing.allocator), "02.01.2006"); - try std.testing.expectEqualStrings("23.04.2025", list.items); + try time.gofmt(&aw.writer, "02.01.2006"); + try std.testing.expectEqualStrings("23.04.2025", aw.writer.buffered()); } test "github.com/rockorager/zeit/issues/24" { @@ -2114,17 +2231,17 @@ test "github.com/rockorager/zeit/issues/24" { const timestamp = 1745414170; const inst = try instant(.{ .source = .{ .unix_timestamp = timestamp } }); - var list: std.ArrayList(u8) = .empty; - defer list.deinit(std.testing.allocator); + var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); const time = inst.time(); - try time.gofmt(list.writer(std.testing.allocator), "3pm MST"); - try std.testing.expectEqualStrings("1pm UTC", list.items); - list.clearRetainingCapacity(); + try time.gofmt(&aw.writer, "3pm MST"); + try std.testing.expectEqualStrings("1pm UTC", aw.writer.buffered()); - try time.gofmt(list.writer(std.testing.allocator), "3p MST"); - try std.testing.expectEqualStrings("1p UTC", list.items); + aw.writer.end = 0; + try time.gofmt(&aw.writer, "3p MST"); + try std.testing.expectEqualStrings("1p UTC", aw.writer.buffered()); } test "github.com/rockorager/zeit/issues/26" { @@ -2132,17 +2249,17 @@ test "github.com/rockorager/zeit/issues/26" { const timestamp = 1745414170; const inst = try instant(.{ .source = .{ .unix_timestamp = timestamp } }); - var list: std.ArrayList(u8) = .empty; - defer list.deinit(std.testing.allocator); + var aw: std.Io.Writer.Allocating = .init(std.testing.allocator); + defer aw.deinit(); const time = inst.time(); - try time.gofmt(list.writer(std.testing.allocator), "02nd"); - try std.testing.expectEqualStrings("23rd", list.items); - list.clearRetainingCapacity(); + try time.gofmt(&aw.writer, "02nd"); + try std.testing.expectEqualStrings("23rd", aw.writer.buffered()); + aw.writer.end = 0; - try time.gofmt(list.writer(std.testing.allocator), "02ND"); - try std.testing.expectEqualStrings("23RD", list.items); + try time.gofmt(&aw.writer, "02ND"); + try std.testing.expectEqualStrings("23RD", aw.writer.buffered()); } test "bufPrintRFC3339Nano" {