jaytwo.TimeExpression
0.1.0-beta-20251102202452
dotnet add package jaytwo.TimeExpression --version 0.1.0-beta-20251102202452
NuGet\Install-Package jaytwo.TimeExpression -Version 0.1.0-beta-20251102202452
<PackageReference Include="jaytwo.TimeExpression" Version="0.1.0-beta-20251102202452" />
<PackageVersion Include="jaytwo.TimeExpression" Version="0.1.0-beta-20251102202452" />
<PackageReference Include="jaytwo.TimeExpression" />
paket add jaytwo.TimeExpression --version 0.1.0-beta-20251102202452
#r "nuget: jaytwo.TimeExpression, 0.1.0-beta-20251102202452"
#:package jaytwo.TimeExpression@0.1.0-beta-20251102202452
#addin nuget:?package=jaytwo.TimeExpression&version=0.1.0-beta-20251102202452&prerelease
#tool nuget:?package=jaytwo.TimeExpression&version=0.1.0-beta-20251102202452&prerelease
jaytwo.TimeExpression
jaytwo.TimeExpression provides human-readable time formatting utilities.
π§ Overview
This library offers a few complementary formatting systems:
| Area | Description |
|---|---|
| Go-style durations | Human-readable and Go-compatible output like "1h2m3.5s", with options for non-canonical (but still Go-like) ASCII-only or compact forms. |
| Exact seconds | Culture-invariant decimal seconds with full tick precision (e.g. "1.2345678", never "1E-3"). |
| Exact milliseconds | Same as Exact seconds, but in milliseconds (tick precision). |
Itβs tiny, dependency-free, and great for logs, metrics, or debugging output.
β¨ Features
β Go-compatible duration syntax Output identical to Goβs
time.Duration.String()β compact, human-readable (1h2m3.5s,1.23ms,500Β΅s,100ns).βοΈ ASCII-safe output option Switch
Β΅s β usfor compatibility with HTTP headers, log transport, or any ASCII-only system.π§© Adaptive sub-second precision Compact mode automatically trims noisy fractional digits for cleaner log output (e.g.
9.9999999sβ9.99s).β± ExactSeconds formatting Provides culture-invariant decimal seconds, at full tick precision (7 decimal places), and never uses scientific notation. Ideal for metrics or structured logs where consistent machine-readable values matter.
β± ExactMilliseconds formatting Same idea as Exact Seconds, but in milliseconds (up to 4 decimals, still tick-precise).
π― Deterministic and dependency-free No runtime dependencies, no culture surprises, no rounding errors β just consistent, predictable output on every platform.
π§ͺ Tiny and test-friendly Fast, allocation-light, and fully deterministic, perfect for loggers, middleware, benchmarks, or performance tracing.
π¦ Installation
Add via NuGet:
PM> Install-Package jaytwo.TimeExpression
Or using the .NET CLI:
dotnet add package jaytwo.TimeExpression
π Usage
Go-style duration formatting
using jaytwo.TimeExpression.Golang;
var sampleTime = TimeSpan.FromMinutes(1.5);
var microseconds = TimeSpan.FromTicks(11); // 1.1 Β΅s
var oneTick = TimeSpan.FromTicks(1);
var almost10s = TimeSpan.FromSeconds(10) - oneTick;
var almost1m = TimeSpan.FromMinutes(1) - oneTick;
var almost1h = TimeSpan.FromHours(1) - oneTick;
var almost1d = TimeSpan.FromDays(1) - oneTick;
var almost1w = TimeSpan.FromDays(7) - oneTick;
// ToGoString (canonical Go behavior)
Console.WriteLine(GoDuration.ToGoString(TimeSpan.Zero)); // β 0s
Console.WriteLine(GoDuration.ToGoString(sampleTime)); // β 1m30s
Console.WriteLine(GoDuration.ToGoString(microseconds)); // β 1.1Β΅s
Console.WriteLine(GoDuration.ToGoString(almost10s)); // β 9.9999999s
Console.WriteLine(GoDuration.ToGoString(almost1m)); // β 59.9999999s
Console.WriteLine(GoDuration.ToGoString(almost1h)); // β 59m59.9999999s
Console.WriteLine(GoDuration.ToGoString(almost1d)); // β 23h59m59.9999999s
Console.WriteLine(GoDuration.ToGoString(almost1w)); // β 167h59m59.9999999s
// ToAsciiString (only affects microseconds: Β΅s β us)
Console.WriteLine(GoDuration.ToAsciiString(microseconds)); // β 1.1us
// ToCompactString (adaptive subsecond decimal precision)
Console.WriteLine(GoDuration.ToCompactString(almost10s)); // β 9.99s
Console.WriteLine(GoDuration.ToCompactString(almost1m)); // β 59.9s
Console.WriteLine(GoDuration.ToCompactString(almost1h)); // β 59m59s
Console.WriteLine(GoDuration.ToCompactString(almost1d)); // β 23h59m59s
Console.WriteLine(GoDuration.ToCompactString(almost1w)); // β 167h59m59s
Why does ASCII-safety matter? The first use of this package was adding human-readable response times to HTTP headers for easier debugging. Everything was working great until a sub-millisecond response time appeared, and suddenly the middleware crashed.
Turns out the
Β΅inΒ΅swas the problem (HTTP headers require ASCII-only). Fix: enableGoDurationFormatOptions.AsciiMicrosecondsto emitus.
Isn't Go's canonical implementation compact enough? While Goβs format is concise, the .NET
TimeSpanrepresentation includes full tick precision (7 decimal places).Remember, my primary use case is human-readable logs and metrics. For humans, this level of detail often adds unnecessary noise. Even multi-minute operations end up displaying sub-microsecond-level fractions.
I might care that something took
1.02msversus1.92ms, but probably not that it took12m23.59192sinstead of12m23.59102s.
Exact seconds
The ExactSeconds API provides culture-invariant decimal seconds, at full tick precision (7 decimal places), and never formats in scientific notation.
using jaytwo.TimeExpression.Seconds;
var twoMinutes = TimeSpan.FromMinutes(2);
var oneTick = TimeSpan.FromTicks(1);
var almost1m = TimeSpan.FromMinutes(1) - oneTick;
var almost1h = TimeSpan.FromHours(1) - oneTick;
var almost1d = TimeSpan.FromDays(1) - oneTick;
var almost1w = TimeSpan.FromDays(7) - oneTick;
// never scientific notation
Console.WriteLine(oneTick.TotalSeconds); // β 1E-7
Console.WriteLine(ExactSeconds.ToString(oneTick)); // β 0.0000001
// trims leading and trailing zeroes
Console.WriteLine(ExactSeconds.ToString(TimeSpan.Zero)); // β 0
Console.WriteLine(ExactSeconds.ToString(twoMinutes)); // β 120
// full tick precision
Console.WriteLine(ExactSeconds.ToString(almost1m)); // β 59.9999999
Console.WriteLine(ExactSeconds.ToString(almost1h)); // β 3599.9999999
Console.WriteLine(ExactSeconds.ToString(almost1d)); // β 86399.9999999
Console.WriteLine(ExactSeconds.ToString(almost1w)); // β 604799.9999999
This seems entirely unnecessary. And for most people, it probably is. But even in the machine-friendly parts of logs, I found it frustrating to track and/or validate full-precision values like
2.7E-4at a glance.
Exact milliseconds
using jaytwo.TimeExpression.Milliseconds;
var twoMinutes = TimeSpan.FromMinutes(2);
var oneTick = TimeSpan.FromTicks(1);
var almost1m = TimeSpan.FromMinutes(1) - oneTick;
var almost1h = TimeSpan.FromHours(1) - oneTick;
var almost1d = TimeSpan.FromDays(1) - oneTick;
var almost1w = TimeSpan.FromDays(7) - oneTick;
// never scientific notation
Console.WriteLine(oneTick.TotalMilliseconds); // β 0.0001
Console.WriteLine(ExactMilliseconds.ToString(oneTick)); // β 0.0001
// trims leading and trailing zeroes
Console.WriteLine(ExactMilliseconds.ToString(TimeSpan.Zero)); // β 0
Console.WriteLine(ExactMilliseconds.ToString(twoMinutes)); // β 120000
// full tick precision
Console.WriteLine(ExactMilliseconds.ToString(almost1m)); // β 59999.9999
Console.WriteLine(ExactMilliseconds.ToString(almost1h)); // β 3599999.9999
Console.WriteLine(ExactMilliseconds.ToString(almost1d)); // β 86399999.9999
Console.WriteLine(ExactMilliseconds.ToString(almost1w)); // β 604799999.9999
Why Exact Milliseconds? Many latency and UI timings are reasoned about in milliseconds, not seconds. While scientific notation is less of an issue with only 4 decimal places, but this gives the same benefits as the other API's in this package: tick precision, culture-invariant formatting, and predictable output.
π§© API Summary
| Type | Description |
|---|---|
GoDuration |
Static helper class exposing convenience methods: ToGoString(), ToAsciiString(), and ToCompactString(). |
GoDurationFormatter |
Core formatter implementing Go-style duration output with configurable GoDurationFormatOptions. |
GoDurationFormatOptions |
Bit flags controlling output format. |
ExactSeconds |
Static helper class exposing convenience method: ToString() |
SecondsFormatter |
Core formatter implementing low-level seconds formatting. |
ExactMilliseconds |
Static helper class exposing convenience method: ToString() |
MillisecondsFormatter |
Core formatter implementing low-level milliseconds formatting. |
Made with β€οΈ by Jake β Licensed under the MIT License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on jaytwo.TimeExpression:
| Package | Downloads |
|---|---|
|
jaytwo.DistributedLocks
Package Description |
|
|
jaytwo.AspNetCore.ResponseTime
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-beta-20251102202452 | 169 | 11/3/2025 |
| 0.1.0-beta-20251101211754 | 197 | 11/2/2025 |
| 0.1.0-beta-20251101163444 | 105 | 11/1/2025 |
| 0.1.0-beta-20251101160839 | 112 | 11/1/2025 |
| 0.1.0-beta-20251031003304 | 148 | 10/31/2025 |