authorgravatar for jdknezek@gmail.comJonathan Knezek <jdknezek@gmail.com> 2021-01-11 18:15:56-06:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-11 19:15:56-05:00
logfc10c9c4ce40570d0abeda764f1bd76b3e79af61
treefeb1949f3a4360bda1775fedd1b22ca0d6292157
parent025f1559a023d6a1c783d4441eb1cb897797b74a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Add std.fmt.formatDuration and std.fmt.duration (#7297)

`formatDuration` works on a writer, and `duration` wraps a u64 to allow pleasant injection into format strings.

1 files changed, 97 insertions(+), 0 deletions(-)

lib/std/fmt.zig+97
...@@ -1119,6 +1119,103 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, op...@@ -1119,6 +1119,103 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, op
1119 return fbs.pos;1119 return fbs.pos;
1120}1120}
11211121
1122/// Formats a number of nanoseconds according to its magnitude:
1123///
1124/// - #ns
1125/// - [#y][#w][#d][#h][#m]#[.###][u|m]s
1126pub fn formatDuration(ns: u64, writer: anytype) !void {
1127 var ns_remaining = ns;
1128 inline for (.{
1129 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
1130 .{ .ns = std.time.ns_per_week, .sep = 'w' },
1131 .{ .ns = std.time.ns_per_day, .sep = 'd' },
1132 .{ .ns = std.time.ns_per_hour, .sep = 'h' },
1133 .{ .ns = std.time.ns_per_min, .sep = 'm' },
1134 }) |unit| {
1135 if (ns_remaining >= unit.ns) {
1136 const units = ns_remaining / unit.ns;
1137 try formatInt(units, 10, false, .{}, writer);
1138 try writer.writeByte(unit.sep);
1139 ns_remaining -= units * unit.ns;
1140 if (ns_remaining == 0) return;
1141 }
1142 }
1143
1144 inline for (.{
1145 .{ .ns = std.time.ns_per_s, .sep = "s" },
1146 .{ .ns = std.time.ns_per_ms, .sep = "ms" },
1147 .{ .ns = std.time.ns_per_us, .sep = "us" },
1148 }) |unit| {
1149 const kunits = ns_remaining * 1000 / unit.ns;
1150 if (kunits >= 1000) {
1151 try formatInt(kunits / 1000, 10, false, .{}, writer);
1152 if (kunits > 1000) {
1153 // Write up to 3 decimal places
1154 const frac = kunits % 1000;
1155 var buf = [_]u8{ '.', 0, 0, 0 };
1156 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
1157 var end: usize = 4;
1158 while (end > 1) : (end -= 1) {
1159 if (buf[end - 1] != '0') break;
1160 }
1161 try writer.writeAll(buf[0..end]);
1162 }
1163 try writer.writeAll(unit.sep);
1164 return;
1165 }
1166 }
1167
1168 try formatInt(ns, 10, false, .{}, writer);
1169 try writer.writeAll("ns");
1170 return;
1171}
1172
1173test "formatDuration" {
1174 var buf: [24]u8 = undefined;
1175 inline for (.{
1176 .{ .s = "0ns", .d = 0 },
1177 .{ .s = "1ns", .d = 1 },
1178 .{ .s = "999ns", .d = std.time.ns_per_us - 1 },
1179 .{ .s = "1us", .d = std.time.ns_per_us },
1180 .{ .s = "1.45us", .d = 1450 },
1181 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
1182 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
1183 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
1184 .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },
1185 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
1186 .{ .s = "1s", .d = std.time.ns_per_s },
1187 .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },
1188 .{ .s = "1m", .d = std.time.ns_per_min },
1189 .{ .s = "1h", .d = std.time.ns_per_hour },
1190 .{ .s = "1d", .d = std.time.ns_per_day },
1191 .{ .s = "1w", .d = std.time.ns_per_week },
1192 .{ .s = "1y", .d = 365 * std.time.ns_per_day },
1193 .{ .s = "1y52w23h59m59.999s", .d = 730 * std.time.ns_per_day - 1 }, // 365d = 52w1d
1194 .{ .s = "1y1h1.001s", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms },
1195 .{ .s = "1y1h1s", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us },
1196 .{ .s = "1y1h999.999us", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1 },
1197 .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms },
1198 .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1 },
1199 }) |tc| {
1200 const slice = try bufPrint(&buf, "{}", .{duration(tc.d)});
1201 std.testing.expectEqualStrings(tc.s, slice);
1202 }
1203}
1204
1205/// Wraps a `u64` to format with `formatDuration`.
1206const Duration = struct {
1207 ns: u64,
1208
1209 pub fn format(self: Duration, comptime fmt: []const u8, options: FormatOptions, writer: anytype) !void {
1210 return formatDuration(self.ns, writer);
1211 }
1212};
1213
1214/// Formats a number of nanoseconds according to its magnitude. See `formatDuration`.
1215pub fn duration(ns: u64) Duration {
1216 return Duration{ .ns = ns };
1217}
1218
1122pub const ParseIntError = error{1219pub const ParseIntError = error{
1123 /// The result cannot fit in the type specified1220 /// The result cannot fit in the type specified
1124 Overflow,1221 Overflow,