authorgravatar for hemisputnik@proton.mehemisputnik <hemisputnik@proton.me> 2026-02-27 01:19:20+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-28 02:46:04+01:00
log99229ceb55be717aa1995f1a0105bc6b96a910f6
treefa3d122d0d429fd002e097d553de0c508267b764
parent8c70fd0a57936cca236a000a17364cbbf7a09673

std.Io.{Writer|Duration}: move duration formatting to a format method

Remove the `{D}` format specifier. It is moved into `std.Io.Duration` as a format method. Migration plan: ```diff -writer.print("{D}", .{ns}); +writer.print("{f}", .{std.Io.Duration{ .nanoseconds = ns }}); ``` All instances where `{D}` was used have been changed to use `std.Io.Duration` and `{f}`. Fixes #31281

4 files changed, 166 insertions(+), 237 deletions(-)

lib/build-web/time_report.zig+39-39
...@@ -42,10 +42,10 @@ pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {...@@ -42,10 +42,10 @@ pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
42 if (msg.step_idx >= step_list.*.len) @panic("malformed GenericResult message");42 if (msg.step_idx >= step_list.*.len) @panic("malformed GenericResult message");
43 const inner_html = try std.fmt.allocPrint(gpa,43 const inner_html = try std.fmt.allocPrint(gpa,
44 \\<code slot="step-name">{[step_name]f}</code>44 \\<code slot="step-name">{[step_name]f}</code>
45 \\<span slot="stat-total-time">{[stat_total_time]D}</span>45 \\<span slot="stat-total-time">{[stat_total_time]f}</span>
46 , .{46 , .{
47 .step_name = fmtEscapeHtml(step_list.*[msg.step_idx].name),47 .step_name = fmtEscapeHtml(step_list.*[msg.step_idx].name),
48 .stat_total_time = msg.ns_total,48 .stat_total_time = std.Io.Duration{ .nanoseconds = msg.ns_total },
49 });49 });
50 defer gpa.free(inner_html);50 defer gpa.free(inner_html);
51 js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len);51 js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len);
...@@ -139,16 +139,16 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v...@@ -139,16 +139,16 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v
139 \\<span slot="stat-imported-files">{[stat_imported_files]d}</span>139 \\<span slot="stat-imported-files">{[stat_imported_files]d}</span>
140 \\<span slot="stat-generic-instances">{[stat_generic_instances]d}</span>140 \\<span slot="stat-generic-instances">{[stat_generic_instances]d}</span>
141 \\<span slot="stat-inline-calls">{[stat_inline_calls]d}</span>141 \\<span slot="stat-inline-calls">{[stat_inline_calls]d}</span>
142 \\<span slot="stat-compilation-time">{[stat_compilation_time]D}</span>142 \\<span slot="stat-compilation-time">{[stat_compilation_time]f}</span>
143 \\<span slot="cpu-time-parse">{[cpu_time_parse]D}</span>143 \\<span slot="cpu-time-parse">{[cpu_time_parse]f}</span>
144 \\<span slot="cpu-time-astgen">{[cpu_time_astgen]D}</span>144 \\<span slot="cpu-time-astgen">{[cpu_time_astgen]f}</span>
145 \\<span slot="cpu-time-sema">{[cpu_time_sema]D}</span>145 \\<span slot="cpu-time-sema">{[cpu_time_sema]f}</span>
146 \\<span slot="cpu-time-codegen">{[cpu_time_codegen]D}</span>146 \\<span slot="cpu-time-codegen">{[cpu_time_codegen]f}</span>
147 \\<span slot="cpu-time-link">{[cpu_time_link]D}</span>147 \\<span slot="cpu-time-link">{[cpu_time_link]f}</span>
148 \\<span slot="real-time-files">{[real_time_files]D}</span>148 \\<span slot="real-time-files">{[real_time_files]f}</span>
149 \\<span slot="real-time-decls">{[real_time_decls]D}</span>149 \\<span slot="real-time-decls">{[real_time_decls]f}</span>
150 \\<span slot="real-time-llvm-emit">{[real_time_llvm_emit]D}</span>150 \\<span slot="real-time-llvm-emit">{[real_time_llvm_emit]f}</span>
151 \\<span slot="real-time-link-flush">{[real_time_link_flush]D}</span>151 \\<span slot="real-time-link-flush">{[real_time_link_flush]f}</span>
152 \\<pre slot="llvm-pass-timings"><code>{[llvm_pass_timings]f}</code></pre>152 \\<pre slot="llvm-pass-timings"><code>{[llvm_pass_timings]f}</code></pre>
153 \\153 \\
154 , .{154 , .{
...@@ -157,17 +157,17 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v...@@ -157,17 +157,17 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v
157 .stat_imported_files = stats.n_imported_files,157 .stat_imported_files = stats.n_imported_files,
158 .stat_generic_instances = stats.n_generic_instances,158 .stat_generic_instances = stats.n_generic_instances,
159 .stat_inline_calls = stats.n_inline_calls,159 .stat_inline_calls = stats.n_inline_calls,
160 .stat_compilation_time = hdr.ns_total,160 .stat_compilation_time = std.Io.Duration{ .nanoseconds = hdr.ns_total },
161161
162 .cpu_time_parse = stats.cpu_ns_parse,162 .cpu_time_parse = std.Io.Duration{ .nanoseconds = stats.cpu_ns_parse },
163 .cpu_time_astgen = stats.cpu_ns_astgen,163 .cpu_time_astgen = std.Io.Duration{ .nanoseconds = stats.cpu_ns_astgen },
164 .cpu_time_sema = stats.cpu_ns_sema,164 .cpu_time_sema = std.Io.Duration{ .nanoseconds = stats.cpu_ns_sema },
165 .cpu_time_codegen = stats.cpu_ns_codegen,165 .cpu_time_codegen = std.Io.Duration{ .nanoseconds = stats.cpu_ns_codegen },
166 .cpu_time_link = stats.cpu_ns_link,166 .cpu_time_link = std.Io.Duration{ .nanoseconds = stats.cpu_ns_link },
167 .real_time_files = stats.real_ns_files,167 .real_time_files = std.Io.Duration{ .nanoseconds = stats.real_ns_files },
168 .real_time_decls = stats.real_ns_decls,168 .real_time_decls = std.Io.Duration{ .nanoseconds = stats.real_ns_decls },
169 .real_time_llvm_emit = stats.real_ns_llvm_emit,169 .real_time_llvm_emit = std.Io.Duration{ .nanoseconds = stats.real_ns_llvm_emit },
170 .real_time_link_flush = stats.real_ns_link_flush,170 .real_time_link_flush = std.Io.Duration{ .nanoseconds = stats.real_ns_link_flush },
171171
172 .llvm_pass_timings = fmtEscapeHtml(llvm_pass_timings),172 .llvm_pass_timings = fmtEscapeHtml(llvm_pass_timings),
173 });173 });
...@@ -180,18 +180,18 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v...@@ -180,18 +180,18 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v
180 try file_table_html.writer.print(180 try file_table_html.writer.print(
181 \\<tr>181 \\<tr>
182 \\ <th scope="row"><code>{f}</code></th>182 \\ <th scope="row"><code>{f}</code></th>
183 \\ <td>{D}</td>183 \\ <td>{f}</td>
184 \\ <td>{D}</td>184 \\ <td>{f}</td>
185 \\ <td>{D}</td>185 \\ <td>{f}</td>
186 \\ <td>{D}</td>186 \\ <td>{f}</td>
187 \\</tr>187 \\</tr>
188 \\188 \\
189 , .{189 , .{
190 fmtEscapeHtml(file.name),190 fmtEscapeHtml(file.name),
191 file.ns_sema,191 std.Io.Duration{ .nanoseconds = file.ns_sema },
192 file.ns_codegen,192 std.Io.Duration{ .nanoseconds = file.ns_codegen },
193 file.ns_link,193 std.Io.Duration{ .nanoseconds = file.ns_link },
194 file.ns_sema + file.ns_codegen + file.ns_link,194 std.Io.Duration{ .nanoseconds = file.ns_sema + file.ns_codegen + file.ns_link },
195 });195 });
196 }196 }
197 if (slowest_files.len > max_table_rows) {197 if (slowest_files.len > max_table_rows) {
...@@ -210,20 +210,20 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v...@@ -210,20 +210,20 @@ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!v
210 \\ <th scope="row"><code>{f}</code></th>210 \\ <th scope="row"><code>{f}</code></th>
211 \\ <th scope="row"><code>{f}</code></th>211 \\ <th scope="row"><code>{f}</code></th>
212 \\ <td>{d}</td>212 \\ <td>{d}</td>
213 \\ <td>{D}</td>213 \\ <td>{f}</td>
214 \\ <td>{D}</td>214 \\ <td>{f}</td>
215 \\ <td>{D}</td>215 \\ <td>{f}</td>
216 \\ <td>{D}</td>216 \\ <td>{f}</td>
217 \\</tr>217 \\</tr>
218 \\218 \\
219 , .{219 , .{
220 fmtEscapeHtml(decl.file_name),220 fmtEscapeHtml(decl.file_name),
221 fmtEscapeHtml(decl.name),221 fmtEscapeHtml(decl.name),
222 decl.sema_count,222 decl.sema_count,
223 decl.ns_sema,223 std.Io.Duration{ .nanoseconds = decl.ns_sema },
224 decl.ns_codegen,224 std.Io.Duration{ .nanoseconds = decl.ns_codegen },
225 decl.ns_link,225 std.Io.Duration{ .nanoseconds = decl.ns_link },
226 decl.ns_sema + decl.ns_codegen + decl.ns_link,226 std.Io.Duration{ .nanoseconds = decl.ns_sema + decl.ns_codegen + decl.ns_link },
227 });227 });
228 }228 }
229 if (slowest_decls.len > max_table_rows) {229 if (slowest_decls.len > max_table_rows) {
...@@ -265,7 +265,7 @@ pub fn runTestResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {...@@ -265,7 +265,7 @@ pub fn runTestResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
265 if (test_ns == std.math.maxInt(u64)) {265 if (test_ns == std.math.maxInt(u64)) {
266 try table_html.appendSlice(gpa, "<td class=\"empty-cell\"></td>"); // didn't run266 try table_html.appendSlice(gpa, "<td class=\"empty-cell\"></td>"); // didn't run
267 } else {267 } else {
268 try table_html.print(gpa, "<td>{D}</td>", .{test_ns});268 try table_html.print(gpa, "<td>{f}</td>", .{std.Io.Duration{ .nanoseconds = test_ns }});
269 }269 }
270 try table_html.appendSlice(gpa, "</tr>\n");270 try table_html.appendSlice(gpa, "</tr>\n");
271 }271 }
lib/std/Build/Step/Run.zig+3-3
...@@ -1787,9 +1787,9 @@ fn evalZigTest(...@@ -1787,9 +1787,9 @@ fn evalZigTest(
1787 // the next test.1787 // the next test.
1788 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;1788 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;
1789 test_results.timeout_count += 1;1789 test_results.timeout_count += 1;
1790 try run.step.addError("'{s}' timed out after {D}{s}{s}", .{1790 try run.step.addError("'{s}' timed out after {f}{s}{s}", .{
1791 test_metadata.?.testName(test_index),1791 test_metadata.?.testName(test_index),
1792 timeout.ns_elapsed,1792 Io.Duration{ .nanoseconds = timeout.ns_elapsed },
1793 if (stderr.len != 0) " with stderr:\n" else "",1793 if (stderr.len != 0) " with stderr:\n" else "",
1794 std.mem.trim(u8, stderr, "\n"),1794 std.mem.trim(u8, stderr, "\n"),
1795 });1795 });
...@@ -1799,7 +1799,7 @@ fn evalZigTest(...@@ -1799,7 +1799,7 @@ fn evalZigTest(
1799 run.step.result_stderr = try arena.dupe(u8, stderr);1799 run.step.result_stderr = try arena.dupe(u8, stderr);
1800 // The individual unit test results in `results` are irrelevant: the test runner1800 // The individual unit test results in `results` are irrelevant: the test runner
1801 // is broken! Fail immediately without populating `s.test_results`.1801 // is broken! Fail immediately without populating `s.test_results`.
1802 return run.step.fail("test runner failed to respond for {D}", .{timeout.ns_elapsed});1802 return run.step.fail("test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
1803 },1803 },
1804 }1804 }
1805 comptime unreachable;1805 comptime unreachable;
lib/std/Io.zig+124
...@@ -940,6 +940,130 @@ pub const Duration = struct {...@@ -940,6 +940,130 @@ pub const Duration = struct {
940 pub fn toNanoseconds(d: Duration) i96 {940 pub fn toNanoseconds(d: Duration) i96 {
941 return d.nanoseconds;941 return d.nanoseconds;
942 }942 }
943
944 /// Write number of nanoseconds according to its signed magnitude:
945 /// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`
946 pub fn format(duration: Duration, w: *Writer) Writer.Error!void {
947 if (duration.nanoseconds < 0) try w.writeByte('-');
948 return formatUnsigned(w, @abs(duration.nanoseconds));
949 }
950
951 fn formatUnsigned(w: *Writer, ns: u96) Writer.Error!void {
952 var ns_remaining = ns;
953 inline for (.{
954 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
955 .{ .ns = std.time.ns_per_week, .sep = 'w' },
956 .{ .ns = std.time.ns_per_day, .sep = 'd' },
957 .{ .ns = std.time.ns_per_hour, .sep = 'h' },
958 .{ .ns = std.time.ns_per_min, .sep = 'm' },
959 }) |unit| {
960 if (ns_remaining >= unit.ns) {
961 const units = ns_remaining / unit.ns;
962 try w.printInt(units, 10, .lower, .{});
963 try w.writeByte(unit.sep);
964 ns_remaining -= units * unit.ns;
965 if (ns_remaining == 0) return;
966 }
967 }
968
969 inline for (.{
970 .{ .ns = std.time.ns_per_s, .sep = "s" },
971 .{ .ns = std.time.ns_per_ms, .sep = "ms" },
972 .{ .ns = std.time.ns_per_us, .sep = "us" },
973 }) |unit| {
974 const kunits = ns_remaining * 1000 / unit.ns;
975 if (kunits >= 1000) {
976 try w.printInt(kunits / 1000, 10, .lower, .{});
977 const frac = kunits % 1000;
978 if (frac > 0) {
979 // Write up to 3 decimal places
980 var decimal_buf = [_]u8{ '.', 0, 0, 0 };
981 var inner: Writer = .fixed(decimal_buf[1..]);
982 inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable;
983 var end: usize = 4;
984 while (end > 1) : (end -= 1) {
985 if (decimal_buf[end - 1] != '0') break;
986 }
987 try w.writeAll(decimal_buf[0..end]);
988 }
989 return w.writeAll(unit.sep);
990 }
991 }
992
993 try w.printInt(ns_remaining, 10, .lower, .{});
994 try w.writeAll("ns");
995 }
996
997 test format {
998 try testFormat("0ns", 0);
999 try testFormat("1ns", 1);
1000 try testFormat("-1ns", -(1));
1001 try testFormat("999ns", std.time.ns_per_us - 1);
1002 try testFormat("-999ns", -(std.time.ns_per_us - 1));
1003 try testFormat("1us", std.time.ns_per_us);
1004 try testFormat("-1us", -(std.time.ns_per_us));
1005 try testFormat("1.45us", 1450);
1006 try testFormat("-1.45us", -(1450));
1007 try testFormat("1.5us", 3 * std.time.ns_per_us / 2);
1008 try testFormat("-1.5us", -(3 * std.time.ns_per_us / 2));
1009 try testFormat("14.5us", 14500);
1010 try testFormat("-14.5us", -(14500));
1011 try testFormat("145us", 145000);
1012 try testFormat("-145us", -(145000));
1013 try testFormat("999.999us", std.time.ns_per_ms - 1);
1014 try testFormat("-999.999us", -(std.time.ns_per_ms - 1));
1015 try testFormat("1ms", std.time.ns_per_ms + 1);
1016 try testFormat("-1ms", -(std.time.ns_per_ms + 1));
1017 try testFormat("1.5ms", 3 * std.time.ns_per_ms / 2);
1018 try testFormat("-1.5ms", -(3 * std.time.ns_per_ms / 2));
1019 try testFormat("1.11ms", 1110000);
1020 try testFormat("-1.11ms", -(1110000));
1021 try testFormat("1.111ms", 1111000);
1022 try testFormat("-1.111ms", -(1111000));
1023 try testFormat("1.111ms", 1111100);
1024 try testFormat("-1.111ms", -(1111100));
1025 try testFormat("999.999ms", std.time.ns_per_s - 1);
1026 try testFormat("-999.999ms", -(std.time.ns_per_s - 1));
1027 try testFormat("1s", std.time.ns_per_s);
1028 try testFormat("-1s", -(std.time.ns_per_s));
1029 try testFormat("59.999s", std.time.ns_per_min - 1);
1030 try testFormat("-59.999s", -(std.time.ns_per_min - 1));
1031 try testFormat("1m", std.time.ns_per_min);
1032 try testFormat("-1m", -(std.time.ns_per_min));
1033 try testFormat("1h", std.time.ns_per_hour);
1034 try testFormat("-1h", -(std.time.ns_per_hour));
1035 try testFormat("1d", std.time.ns_per_day);
1036 try testFormat("-1d", -(std.time.ns_per_day));
1037 try testFormat("1w", std.time.ns_per_week);
1038 try testFormat("-1w", -(std.time.ns_per_week));
1039 try testFormat("1y", 365 * std.time.ns_per_day);
1040 try testFormat("-1y", -(365 * std.time.ns_per_day));
1041 try testFormat("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
1042 try testFormat("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
1043 try testFormat("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
1044 try testFormat("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
1045 try testFormat("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
1046 try testFormat("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
1047 try testFormat("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
1048 try testFormat("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
1049 try testFormat("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
1050 try testFormat("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
1051 try testFormat("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
1052 try testFormat("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
1053 try testFormat("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
1054 try testFormat("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
1055 try testFormat("292y24w3d23h47m16.854s", std.math.maxInt(i64));
1056 try testFormat("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
1057 try testFormat("-292y24w3d23h47m16.854s", std.math.minInt(i64));
1058 }
1059
1060 fn testFormat(expected: []const u8, input: i96) !void {
1061 // worst case: "-XXXXXXXXXXXXXyXXwXXdXXhXXmXX.XXXs".len = 34
1062 var buf: [34]u8 = undefined;
1063 var w: Writer = .fixed(&buf);
1064 try w.print("{f}", .{Duration{ .nanoseconds = input }});
1065 try std.testing.expectEqualStrings(expected, w.buffered());
1066 }
943};1067};
9441068
945/// Declares under what conditions an operation should return `error.Timeout`.1069/// Declares under what conditions an operation should return `error.Timeout`.
lib/std/Io/Writer.zig-195
...@@ -1155,11 +1155,6 @@ pub fn printValue(...@@ -1155,11 +1155,6 @@ pub fn printValue(
1155 .@"struct" => return value.formatByteSize(w, .decimal),1155 .@"struct" => return value.formatByteSize(w, .decimal),
1156 else => invalidFmtError(fmt, value),1156 else => invalidFmtError(fmt, value),
1157 },1157 },
1158 'D' => switch (@typeInfo(T)) {
1159 .int, .comptime_int => return w.printDuration(value, options),
1160 .@"struct" => return value.formatDuration(w),
1161 else => invalidFmtError(fmt, value),
1162 },
1163 'e' => switch (@typeInfo(T)) {1158 'e' => switch (@typeInfo(T)) {
1164 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),1159 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
1165 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),1160 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
...@@ -1792,77 +1787,6 @@ pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn {...@@ -1792,77 +1787,6 @@ pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn {
1792 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");1787 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
1793}1788}
17941789
1795pub fn printDurationSigned(w: *Writer, ns: i64) Error!void {
1796 if (ns < 0) try w.writeByte('-');
1797 return w.printDurationUnsigned(@abs(ns));
1798}
1799
1800pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void {
1801 var ns_remaining = ns;
1802 inline for (.{
1803 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
1804 .{ .ns = std.time.ns_per_week, .sep = 'w' },
1805 .{ .ns = std.time.ns_per_day, .sep = 'd' },
1806 .{ .ns = std.time.ns_per_hour, .sep = 'h' },
1807 .{ .ns = std.time.ns_per_min, .sep = 'm' },
1808 }) |unit| {
1809 if (ns_remaining >= unit.ns) {
1810 const units = ns_remaining / unit.ns;
1811 try w.printInt(units, 10, .lower, .{});
1812 try w.writeByte(unit.sep);
1813 ns_remaining -= units * unit.ns;
1814 if (ns_remaining == 0) return;
1815 }
1816 }
1817
1818 inline for (.{
1819 .{ .ns = std.time.ns_per_s, .sep = "s" },
1820 .{ .ns = std.time.ns_per_ms, .sep = "ms" },
1821 .{ .ns = std.time.ns_per_us, .sep = "us" },
1822 }) |unit| {
1823 const kunits = ns_remaining * 1000 / unit.ns;
1824 if (kunits >= 1000) {
1825 try w.printInt(kunits / 1000, 10, .lower, .{});
1826 const frac = kunits % 1000;
1827 if (frac > 0) {
1828 // Write up to 3 decimal places
1829 var decimal_buf = [_]u8{ '.', 0, 0, 0 };
1830 var inner: Writer = .fixed(decimal_buf[1..]);
1831 inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable;
1832 var end: usize = 4;
1833 while (end > 1) : (end -= 1) {
1834 if (decimal_buf[end - 1] != '0') break;
1835 }
1836 try w.writeAll(decimal_buf[0..end]);
1837 }
1838 return w.writeAll(unit.sep);
1839 }
1840 }
1841
1842 try w.printInt(ns_remaining, 10, .lower, .{});
1843 try w.writeAll("ns");
1844}
1845
1846/// Writes number of nanoseconds according to its signed magnitude:
1847/// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`
1848/// `nanoseconds` must be an integer that coerces into `u64` or `i64`.
1849pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void {
1850 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
1851 var buf: [24]u8 = undefined;
1852 var sub_writer: Writer = .fixed(&buf);
1853 if (@TypeOf(nanoseconds) == comptime_int) {
1854 if (nanoseconds >= 0) {
1855 sub_writer.printDurationUnsigned(nanoseconds) catch unreachable;
1856 } else {
1857 sub_writer.printDurationSigned(nanoseconds) catch unreachable;
1858 }
1859 } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) {
1860 .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable,
1861 .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable,
1862 }
1863 return w.alignBufferOptions(sub_writer.buffered(), options);
1864}
1865
1866pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void {1790pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void {
1867 const charset = switch (case) {1791 const charset = switch (case) {
1868 .upper => "0123456789ABCDEF",1792 .upper => "0123456789ABCDEF",
...@@ -2129,125 +2053,6 @@ test "printValue max_depth" {...@@ -2129,125 +2053,6 @@ test "printValue max_depth" {
2129 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());2053 try testing.expectEqualStrings("{ 1, 2, 3, 4 }", w.buffered());
2130}2054}
21312055
2132test printDuration {
2133 try testDurationCase("0ns", 0);
2134 try testDurationCase("1ns", 1);
2135 try testDurationCase("999ns", std.time.ns_per_us - 1);
2136 try testDurationCase("1us", std.time.ns_per_us);
2137 try testDurationCase("1.45us", 1450);
2138 try testDurationCase("1.5us", 3 * std.time.ns_per_us / 2);
2139 try testDurationCase("14.5us", 14500);
2140 try testDurationCase("145us", 145000);
2141 try testDurationCase("999.999us", std.time.ns_per_ms - 1);
2142 try testDurationCase("1ms", std.time.ns_per_ms + 1);
2143 try testDurationCase("1.5ms", 3 * std.time.ns_per_ms / 2);
2144 try testDurationCase("1.11ms", 1110000);
2145 try testDurationCase("1.111ms", 1111000);
2146 try testDurationCase("1.111ms", 1111100);
2147 try testDurationCase("999.999ms", std.time.ns_per_s - 1);
2148 try testDurationCase("1s", std.time.ns_per_s);
2149 try testDurationCase("59.999s", std.time.ns_per_min - 1);
2150 try testDurationCase("1m", std.time.ns_per_min);
2151 try testDurationCase("1h", std.time.ns_per_hour);
2152 try testDurationCase("1d", std.time.ns_per_day);
2153 try testDurationCase("1w", std.time.ns_per_week);
2154 try testDurationCase("1y", 365 * std.time.ns_per_day);
2155 try testDurationCase("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1
2156 try testDurationCase("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
2157 try testDurationCase("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
2158 try testDurationCase("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
2159 try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
2160 try testDurationCase("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
2161 try testDurationCase("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
2162 try testDurationCase("584y49w23h34m33.709s", std.math.maxInt(u64));
2163
2164 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
2165 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
2166 try testing.expectFmt(" 999ns ", "{D:^10}", .{std.time.ns_per_us - 1});
2167}
2168
2169test printDurationSigned {
2170 try testDurationCaseSigned("0ns", 0);
2171 try testDurationCaseSigned("1ns", 1);
2172 try testDurationCaseSigned("-1ns", -(1));
2173 try testDurationCaseSigned("999ns", std.time.ns_per_us - 1);
2174 try testDurationCaseSigned("-999ns", -(std.time.ns_per_us - 1));
2175 try testDurationCaseSigned("1us", std.time.ns_per_us);
2176 try testDurationCaseSigned("-1us", -(std.time.ns_per_us));
2177 try testDurationCaseSigned("1.45us", 1450);
2178 try testDurationCaseSigned("-1.45us", -(1450));
2179 try testDurationCaseSigned("1.5us", 3 * std.time.ns_per_us / 2);
2180 try testDurationCaseSigned("-1.5us", -(3 * std.time.ns_per_us / 2));
2181 try testDurationCaseSigned("14.5us", 14500);
2182 try testDurationCaseSigned("-14.5us", -(14500));
2183 try testDurationCaseSigned("145us", 145000);
2184 try testDurationCaseSigned("-145us", -(145000));
2185 try testDurationCaseSigned("999.999us", std.time.ns_per_ms - 1);
2186 try testDurationCaseSigned("-999.999us", -(std.time.ns_per_ms - 1));
2187 try testDurationCaseSigned("1ms", std.time.ns_per_ms + 1);
2188 try testDurationCaseSigned("-1ms", -(std.time.ns_per_ms + 1));
2189 try testDurationCaseSigned("1.5ms", 3 * std.time.ns_per_ms / 2);
2190 try testDurationCaseSigned("-1.5ms", -(3 * std.time.ns_per_ms / 2));
2191 try testDurationCaseSigned("1.11ms", 1110000);
2192 try testDurationCaseSigned("-1.11ms", -(1110000));
2193 try testDurationCaseSigned("1.111ms", 1111000);
2194 try testDurationCaseSigned("-1.111ms", -(1111000));
2195 try testDurationCaseSigned("1.111ms", 1111100);
2196 try testDurationCaseSigned("-1.111ms", -(1111100));
2197 try testDurationCaseSigned("999.999ms", std.time.ns_per_s - 1);
2198 try testDurationCaseSigned("-999.999ms", -(std.time.ns_per_s - 1));
2199 try testDurationCaseSigned("1s", std.time.ns_per_s);
2200 try testDurationCaseSigned("-1s", -(std.time.ns_per_s));
2201 try testDurationCaseSigned("59.999s", std.time.ns_per_min - 1);
2202 try testDurationCaseSigned("-59.999s", -(std.time.ns_per_min - 1));
2203 try testDurationCaseSigned("1m", std.time.ns_per_min);
2204 try testDurationCaseSigned("-1m", -(std.time.ns_per_min));
2205 try testDurationCaseSigned("1h", std.time.ns_per_hour);
2206 try testDurationCaseSigned("-1h", -(std.time.ns_per_hour));
2207 try testDurationCaseSigned("1d", std.time.ns_per_day);
2208 try testDurationCaseSigned("-1d", -(std.time.ns_per_day));
2209 try testDurationCaseSigned("1w", std.time.ns_per_week);
2210 try testDurationCaseSigned("-1w", -(std.time.ns_per_week));
2211 try testDurationCaseSigned("1y", 365 * std.time.ns_per_day);
2212 try testDurationCaseSigned("-1y", -(365 * std.time.ns_per_day));
2213 try testDurationCaseSigned("1y52w23h59m59.999s", 730 * std.time.ns_per_day - 1); // 365d = 52w1d
2214 try testDurationCaseSigned("-1y52w23h59m59.999s", -(730 * std.time.ns_per_day - 1)); // 365d = 52w1d
2215 try testDurationCaseSigned("1y1h1.001s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms);
2216 try testDurationCaseSigned("-1y1h1.001s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + std.time.ns_per_ms));
2217 try testDurationCaseSigned("1y1h1s", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us);
2218 try testDurationCaseSigned("-1y1h1s", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_s + 999 * std.time.ns_per_us));
2219 try testDurationCaseSigned("1y1h999.999us", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1);
2220 try testDurationCaseSigned("-1y1h999.999us", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1));
2221 try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms);
2222 try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms));
2223 try testDurationCaseSigned("1y1h1ms", 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1);
2224 try testDurationCaseSigned("-1y1h1ms", -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1));
2225 try testDurationCaseSigned("1y1m999ns", 365 * std.time.ns_per_day + std.time.ns_per_min + 999);
2226 try testDurationCaseSigned("-1y1m999ns", -(365 * std.time.ns_per_day + std.time.ns_per_min + 999));
2227 try testDurationCaseSigned("292y24w3d23h47m16.854s", std.math.maxInt(i64));
2228 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64) + 1);
2229 try testDurationCaseSigned("-292y24w3d23h47m16.854s", std.math.minInt(i64));
2230
2231 try testing.expectFmt("=======0ns", "{D:=>10}", .{0});
2232 try testing.expectFmt("1ns=======", "{D:=<10}", .{1});
2233 try testing.expectFmt("-1ns======", "{D:=<10}", .{-(1)});
2234 try testing.expectFmt(" -999ns ", "{D:^10}", .{-(std.time.ns_per_us - 1)});
2235}
2236
2237fn testDurationCase(expected: []const u8, input: u64) !void {
2238 var buf: [24]u8 = undefined;
2239 var w: Writer = .fixed(&buf);
2240 try w.printDurationUnsigned(input);
2241 try testing.expectEqualStrings(expected, w.buffered());
2242}
2243
2244fn testDurationCaseSigned(expected: []const u8, input: i64) !void {
2245 var buf: [24]u8 = undefined;
2246 var w: Writer = .fixed(&buf);
2247 try w.printDurationSigned(input);
2248 try testing.expectEqualStrings(expected, w.buffered());
2249}
2250
2251test printInt {2056test printInt {
2252 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});2057 try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{});
22532058