Skip to content

Instantly share code, notes, and snippets.

@randyburden
Last active February 14, 2023 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save randyburden/8186395 to your computer and use it in GitHub Desktop.
Save randyburden/8186395 to your computer and use it in GitHub Desktop.
TimeSpan helper and extension method for creating a formatted string of a given TimeSpan. Supports up to microsecond resolution. Also includes unit tests.
/* JavaScript-equivalent version of the C# method to get a formatted time */
function getFormattedTime(elapsedMilliseconds, abbreviate, includeSeconds, includeMilliseconds) {
if (abbreviate == undefined) {
abbreviate = true;
}
if (includeSeconds == undefined) {
includeSeconds = true;
}
if (includeMilliseconds == undefined) {
includeMilliseconds = true;
}
const SECONDS = 1000;
const MINUTES = SECONDS * 60;
const HOURS = MINUTES * 60;
const DAYS = HOURS * 24;
const days = parseInt(Math.floor(elapsedMilliseconds / DAYS), 10);
const hours = parseInt(Math.floor((elapsedMilliseconds - (days * DAYS)) / HOURS), 10);
const minutes = parseInt(Math.floor((elapsedMilliseconds - (days * DAYS) - (hours * HOURS)) / MINUTES), 10);
const seconds = parseInt(Math.floor((elapsedMilliseconds - (days * DAYS) - (hours * HOURS) - (minutes * MINUTES)) / SECONDS), 10);
const milliseconds = parseInt(Math.floor(elapsedMilliseconds - (days * DAYS) - (hours * HOURS) - (minutes * MINUTES) - (seconds * SECONDS)), 10);
var elapsedTime = '';
if (days > 0) {
elapsedTime += `${days} ${(abbreviate ? 'd' : days === 1 ? 'day' : 'days')}`;
}
if (hours > 0) {
elapsedTime += ` ${hours} ${(abbreviate ? 'hr' : hours === 1 ? 'hour' : 'hours')}`;
}
if (minutes > 0) {
elapsedTime += ` ${minutes} ${(abbreviate ? 'min' : minutes === 1 ? 'minute' : 'minutes')}`;
}
if (includeSeconds && seconds > 0) {
elapsedTime += ` ${seconds} ${(abbreviate ? 'sec' : seconds === 1 ? 'second' : 'seconds')}`;
}
if (includeSeconds && includeMilliseconds && milliseconds > 0) {
elapsedTime += ` ${milliseconds} ${(abbreviate ? 'ms' : milliseconds === 1 ? 'millisecond' : 'milliseconds')}`;
}
const result = elapsedTime.trim();
if (!result) {
if (includeSeconds && includeMilliseconds) {
return abbreviate ? '0 ms' : '0 milliseconds';
}
if (includeSeconds) {
return abbreviate ? '0 sec' : '0 seconds';
}
return abbreviate ? '0 min' : '0 minutes';
}
return result;
};
using System;
using System.Text;
namespace Helpers
{
public static class TimeSpanHelper
{
/// <summary>
/// Gets a formatted string of the given <see cref="TimeSpan"/>. Supports up to microsecond resolution.
///
/// <para>
/// Examples:
/// <list type="bullet">
/// <item><description>Abbreviated: 1 d 6 hr 52 min 34 sec 556 ms</description></item>
/// <item><description>Not abbreviated: 1 day 6 hours 52 min 34 sec 556 ms</description></item>
/// </list>
/// </para>
/// </summary>
/// <param name="timeSpan">Duration to format.</param>
/// <param name="abbreviate">Indicates whether to abbreviate units of time. E.g. min or minutes. Defaults to <see langword="true" />.</param>
/// <param name="includeSeconds">Indicates whether to include seconds. Defaults to <see langword="true" />. If <see langword="false" /> then milliseconds and microseconds are not included. /></param>
/// <param name="includeMilliseconds">Indicates whether to include milliseconds. Defaults to <see langword="true" />. If <see langword="false" /> then microseconds are not included.</param>
/// <param name="includeMicroseconds">Indicates whether to include microseconds. Microseconds will only be displayed if duration is less than 1 millisecond. Defaults to <see langword="true" />.</param>
/// <returns>A string with a customized output of the elapsed time.</returns>
public static string ToFormattedTime(
this TimeSpan timeSpan,
bool abbreviate = true,
bool includeSeconds = true,
bool includeMilliseconds = true,
bool includeMicroseconds = true)
{
return GetFormattedTime(timeSpan, abbreviate, includeSeconds, includeMilliseconds, includeMicroseconds);
}
/// <summary>
/// Gets a formatted string of the given <see cref="TimeSpan"/>. Supports up to microsecond resolution.
///
/// <para>
/// Examples:
/// <list type="bullet">
/// <item><description>Abbreviated: 1 d 6 hr 52 min 34 sec 556 ms</description></item>
/// <item><description>Not abbreviated: 1 day 6 hours 52 min 34 sec 556 ms</description></item>
/// </list>
/// </para>
/// </summary>
/// <param name="timeSpan">Duration to format.</param>
/// <param name="abbreviate">Indicates whether to abbreviate units of time. E.g. min or minutes. Defaults to <see langword="true" />.</param>
/// <param name="includeSeconds">Indicates whether to include seconds. Defaults to <see langword="true" />. If <see langword="false" /> then milliseconds and microseconds are not included. /></param>
/// <param name="includeMilliseconds">Indicates whether to include milliseconds. Defaults to <see langword="true" />. If <see langword="false" /> then microseconds are not included.</param>
/// <param name="includeMicroseconds">Indicates whether to include microseconds. Microseconds will only be displayed if duration is less than 1 millisecond. Defaults to <see langword="true" />.</param>
/// <returns>A string with a customized output of the elapsed time.</returns>
public static string GetFormattedTime(
TimeSpan timeSpan,
bool abbreviate = true,
bool includeSeconds = true,
bool includeMilliseconds = true,
bool includeMicroseconds = true)
{
var elapsedTime = new StringBuilder();
if (timeSpan.Days > 0)
{
elapsedTime.Append($"{timeSpan:%d} {(abbreviate ? "d" : timeSpan.Days == 1 ? "day" : "days")}");
}
if (timeSpan.Hours > 0)
{
elapsedTime.Append($" {timeSpan:%h} {(abbreviate ? "hr" : timeSpan.Hours == 1 ? "hour" : "hours")}");
}
if (timeSpan.Minutes > 0)
{
elapsedTime.Append($" {timeSpan:%m} {(abbreviate ? "min" : timeSpan.Minutes == 1 ? "minute" : "minutes")}");
}
if (includeSeconds && timeSpan.Seconds > 0)
{
elapsedTime.Append($" {timeSpan:%s} {(abbreviate ? "sec" : timeSpan.Seconds == 1 ? "second" : "seconds")}");
}
if (includeSeconds && includeMilliseconds && timeSpan.Milliseconds > 0)
{
elapsedTime.Append($" {timeSpan:fff} {(abbreviate ? "ms" : timeSpan.Milliseconds == 1 ? "millisecond" : "milliseconds")}");
}
else if (includeSeconds && includeMilliseconds && includeMicroseconds && timeSpan.TotalMilliseconds > 0)
{
elapsedTime.Append($" {timeSpan.TotalMilliseconds * 1000.0} {(abbreviate ? "µs" : "microseconds")}");
}
var result = elapsedTime.ToString().Trim();
if (string.IsNullOrEmpty(result))
{
if (includeSeconds && includeMilliseconds)
{
return abbreviate ? "0 ms" : "0 milliseconds";
}
if (includeSeconds)
{
return abbreviate ? "0 sec" : "0 seconds";
}
return abbreviate ? "0 min" : "0 minutes";
}
return result;
}
}
}
using NUnit.Framework;
using System;
namespace Tests
{
[TestFixture]
public class TimeSpanHelperTests
{
[Test]
public void ToFormattedTime_Should_Format()
{
Assert.That(new TimeSpan(1, 6, 52, 34, 567).ToFormattedTime(), Is.EqualTo("1 d 6 hr 52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 6, 52, 34, 567).ToFormattedTime(), Is.EqualTo("6 hr 52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 52, 34, 567).ToFormattedTime(), Is.EqualTo("52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 0, 34, 567).ToFormattedTime(), Is.EqualTo("34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 0, 0, 567).ToFormattedTime(), Is.EqualTo("567 ms"));
Assert.That(TimeSpan.FromTicks(10).ToFormattedTime(), Is.EqualTo("1 µs"));
Assert.That(TimeSpan.FromTicks(1).ToFormattedTime(), Is.EqualTo("0.1 µs"));
Assert.That(new TimeSpan(0, 0, 0, 0, 0).ToFormattedTime(), Is.EqualTo("0 ms"));
}
[Test]
public void ToFormattedTime_Should_Have_Option_To_Not_Abbreviate()
{
Assert.That(new TimeSpan(1, 6, 52, 34, 567).ToFormattedTime(abbreviate: false), Is.EqualTo("1 day 6 hours 52 minutes 34 seconds 567 milliseconds"));
Assert.That(new TimeSpan(0, 6, 52, 34, 567).ToFormattedTime(abbreviate: false), Is.EqualTo("6 hours 52 minutes 34 seconds 567 milliseconds"));
Assert.That(new TimeSpan(0, 0, 52, 34, 567).ToFormattedTime(abbreviate: false), Is.EqualTo("52 minutes 34 seconds 567 milliseconds"));
Assert.That(new TimeSpan(0, 0, 0, 34, 567).ToFormattedTime(abbreviate: false), Is.EqualTo("34 seconds 567 milliseconds"));
Assert.That(new TimeSpan(0, 0, 0, 0, 567).ToFormattedTime(abbreviate: false), Is.EqualTo("567 milliseconds"));
Assert.That(TimeSpan.FromTicks(10).ToFormattedTime(abbreviate: false), Is.EqualTo("1 microseconds"));
Assert.That(TimeSpan.FromTicks(1).ToFormattedTime(abbreviate: false), Is.EqualTo("0.1 microseconds"));
Assert.That(new TimeSpan(0, 0, 0, 0, 0).ToFormattedTime(abbreviate: false), Is.EqualTo("0 milliseconds"));
}
[Test]
public void ToFormattedTime_Should_Have_Option_To_Not_Include_Microseconds()
{
Assert.That(new TimeSpan(1, 6, 52, 34, 567).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("1 d 6 hr 52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 6, 52, 34, 567).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("6 hr 52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 52, 34, 567).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("52 min 34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 0, 34, 567).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("34 sec 567 ms"));
Assert.That(new TimeSpan(0, 0, 0, 0, 567).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("567 ms"));
Assert.That(TimeSpan.FromTicks(10).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("0 ms"));
Assert.That(TimeSpan.FromTicks(1).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("0 ms"));
Assert.That(new TimeSpan(0, 0, 0, 0, 0).ToFormattedTime(includeMicroseconds: false), Is.EqualTo("0 ms"));
}
[Test]
public void ToFormattedTime_Should_Have_Option_To_Not_Include_Milliseconds()
{
Assert.That(new TimeSpan(1, 6, 52, 34, 567).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("1 d 6 hr 52 min 34 sec"));
Assert.That(new TimeSpan(0, 6, 52, 34, 567).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("6 hr 52 min 34 sec"));
Assert.That(new TimeSpan(0, 0, 52, 34, 567).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("52 min 34 sec"));
Assert.That(new TimeSpan(0, 0, 0, 34, 567).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("34 sec"));
Assert.That(new TimeSpan(0, 0, 0, 0, 567).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("0 sec"));
Assert.That(TimeSpan.FromTicks(10).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("0 sec"));
Assert.That(TimeSpan.FromTicks(1).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("0 sec"));
Assert.That(new TimeSpan(0, 0, 0, 0, 0).ToFormattedTime(includeMilliseconds: false), Is.EqualTo("0 sec"));
}
[Test]
public void ToFormattedTime_Should_Have_Option_To_Not_Include_Seconds()
{
Assert.That(new TimeSpan(1, 6, 52, 34, 567).ToFormattedTime(includeSeconds: false), Is.EqualTo("1 d 6 hr 52 min"));
Assert.That(new TimeSpan(0, 6, 52, 34, 567).ToFormattedTime(includeSeconds: false), Is.EqualTo("6 hr 52 min"));
Assert.That(new TimeSpan(0, 0, 52, 34, 567).ToFormattedTime(includeSeconds: false), Is.EqualTo("52 min"));
Assert.That(new TimeSpan(0, 0, 0, 34, 567).ToFormattedTime(includeSeconds: false), Is.EqualTo("0 min"));
Assert.That(new TimeSpan(0, 0, 0, 0, 567).ToFormattedTime(includeSeconds: false), Is.EqualTo("0 min"));
Assert.That(TimeSpan.FromTicks(10).ToFormattedTime(includeSeconds: false), Is.EqualTo("0 min"));
Assert.That(TimeSpan.FromTicks(1).ToFormattedTime(includeSeconds: false), Is.EqualTo("0 min"));
Assert.That(new TimeSpan(0, 0, 0, 0, 0).ToFormattedTime(includeSeconds: false), Is.EqualTo("0 min"));
}
}
}
@millerchristopher
Copy link

I still love using this gist. Thanks for sharing quality content, Randy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment