ZetCode

C# TimeSpan

last modified October 18, 2026

In this article we work with TimeSpan in C#.

TimeSpan represents a time interval (a duration or elapsed time) measured as a positive or negative number of days, hours, minutes, seconds, and fractions of a second. It is a structure, so it is a value type.

The value of a TimeSpan can range from TimeSpan.MinValue to TimeSpan.MaxValue. Internally it is stored as a number of ticks, where one tick is 100 nanoseconds. The largest unit of time that TimeSpan measures is a day. It cannot be expressed in terms of years or months, because these have a variable number of days. A month can have 28, 29, 30, or 31 days.

A TimeSpan is a duration. It should not be confused with DateTime, which is a point in time, or TimeOnly, which is a time of day.

Constructor Description
TimeSpan(Int64) the specified number of ticks
TimeSpan(Int32, Int32, Int32) hours, minutes, and seconds
TimeSpan(Int32, Int32, Int32, Int32) days, hours, minutes, and seconds
TimeSpan(Int32, Int32, Int32, Int32, Int32) days, hours, minutes, seconds, and milliseconds
TimeSpan(Int32, Int32, Int32, Int32, Int32, Int32) days, hours, minutes, seconds, milliseconds, and microseconds (.NET 7+)
Table: TimeSpan constructors

C# TimeSpan simple example

The following is a simple example that creates TimeSpan values.

Program.cs
TimeSpan ts1 = new TimeSpan(2, 4, 5);
Console.WriteLine(ts1);

TimeSpan ts2 = new TimeSpan(2, 4, 5, 9);
Console.WriteLine(ts2);

TimeSpan ts3 = new TimeSpan(2, 4, 5, 9, 250);
Console.WriteLine(ts3);

TimeSpan ts4 = new TimeSpan(36000000000);
Console.WriteLine(ts4);

Console.WriteLine(TimeSpan.Zero);
Console.WriteLine(TimeSpan.TicksPerDay);
Console.WriteLine(TimeSpan.TicksPerHour);
Console.WriteLine(TimeSpan.TicksPerMinute);

Console.WriteLine(TimeSpan.MinValue);
Console.WriteLine(TimeSpan.MaxValue);

In the program, we create several instances of TimeSpan, print the number of ticks per day, hour, and minute, and print the minimum and maximum values.

$ dotnet run
02:04:05
2.04:05:09
2.04:05:09.2500000
01:00:00
00:00:00
864000000000
36000000000
600000000
-10675199.02:48:05.4775808
10675199.02:48:05.4775807

The default string format is [-][d.]hh:mm:ss[.fffffff]. The days and fractional seconds are shown only when they are non-zero. Note that, unlike DateTime, the default ToString of a TimeSpan is not culture-sensitive.

C# TimeSpan basic arithmetic

We can use basic arithmetic and comparison operators on TimeSpan values.

Program.cs
TimeSpan ts = new TimeSpan(2, 4, 5, 5);
Console.WriteLine(ts);

TimeSpan ts2 = new TimeSpan(4, 1, 0, 0);
Console.WriteLine(ts2);

Console.WriteLine(ts + ts2);
Console.WriteLine(ts * 2);
Console.WriteLine(ts2 - ts);
Console.WriteLine(ts2 / 4);
Console.WriteLine(ts2 / ts);
Console.WriteLine(-ts);
Console.WriteLine((ts - ts2).Duration());

Console.WriteLine(ts < ts2);
Console.WriteLine(ts == new TimeSpan(2, 4, 5, 5));
Console.WriteLine(TimeSpan.Compare(ts, ts2));

In the program, we add, subtract, multiply, divide, negate, and compare TimeSpan values.

Console.WriteLine(ts2 / 4);
Console.WriteLine(ts2 / ts);

Dividing a TimeSpan by a number gives a TimeSpan. Dividing a TimeSpan by another TimeSpan gives a double, the ratio of the two durations.

Console.WriteLine(-ts);
Console.WriteLine((ts - ts2).Duration());

The unary minus negates the interval. The Duration method returns the absolute value, which is useful when we do not care about the order of the operands.

$ dotnet run
2.04:05:05
4.01:00:00
6.05:05:05
4.08:10:10
1.20:54:55
1.00:15:00
1.862350337324338
-2.04:05:05
1.20:54:55
True
True
-1

The TimeSpan.Compare method returns -1, 0, or 1 when the first interval is shorter than, equal to, or longer than the second.

C# TimeSpan with DateTime

In the next example, we combine a TimeSpan with DateTime. Adding a TimeSpan to a DateTime gives a new DateTime; subtracting two DateTime values gives a TimeSpan.

Program.cs
var start = new DateTime(2026, 10, 18, 9, 30, 0);
var ts = new TimeSpan(2, 4, 5);

Console.WriteLine(start);
Console.WriteLine(start.Add(ts));
Console.WriteLine(start.Subtract(ts));
Console.WriteLine(start + ts);
Console.WriteLine(start - ts);

var end = new DateTime(2026, 10, 20, 17, 45, 0);
TimeSpan diff = end - start;
Console.WriteLine(diff);

In the program, we add a TimeSpan to a date and subtract it, using both the methods and the operators. Finally, we compute the interval between two dates.

$ dotnet run
10/18/2026 9:30:00 AM
10/18/2026 11:34:05 AM
10/18/2026 7:25:55 AM
10/18/2026 11:34:05 AM
10/18/2026 7:25:55 AM
2.08:15:00

The format of the dates depends on the current culture.

C# TimeSpan From methods

TimeSpan has convenient methods for creating instances from a single value: FromDays, FromHours, FromMinutes, FromSeconds, FromMilliseconds, and FromMicroseconds.

Program.cs
var ts1 = TimeSpan.FromDays(2.7);
Console.WriteLine(ts1);

var ts2 = TimeSpan.FromHours(4.5);
Console.WriteLine(ts2);

var ts3 = TimeSpan.FromMinutes(12.5);
Console.WriteLine(ts3);

var ts4 = TimeSpan.FromSeconds(90);
Console.WriteLine(ts4);

var ts5 = TimeSpan.FromMilliseconds(1500);
Console.WriteLine(ts5);

// .NET 9: overloads with several integer arguments
var ts6 = TimeSpan.FromHours(1, 30, 15);
Console.WriteLine(ts6);

Console.WriteLine(ts1 + ts2 + ts3);

The program creates intervals from fractional values and adds them together.

var ts6 = TimeSpan.FromHours(1, 30, 15);

Since .NET 9, the methods also have overloads that take several integer arguments, here hours, minutes, and seconds. They avoid the floating-point rounding errors of the double versions, which round to the nearest millisecond in older versions of .NET.

$ dotnet run
2.16:48:00
04:30:00
00:12:30
00:01:30
00:00:01.5000000
01:30:15
2.21:30:30

C# TimeSpan components and totals

A TimeSpan has two sets of properties that are easy to confuse. The component properties Days, Hours, Minutes, Seconds, and Milliseconds return the part of the interval in the respective unit, with the hours between -23 and 23 and the minutes and seconds between -59 and 59. The total properties TotalDays, TotalHours, and so on express the whole interval in one unit, as a double.

Program.cs
var ts = new TimeSpan(2, 4, 5, 9, 250);

// components: the part of each unit
Console.WriteLine($"Days: {ts.Days}");
Console.WriteLine($"Hours: {ts.Hours}");
Console.WriteLine($"Minutes: {ts.Minutes}");
Console.WriteLine($"Seconds: {ts.Seconds}");
Console.WriteLine($"Milliseconds: {ts.Milliseconds}");

Console.WriteLine();

// totals: the whole interval expressed in one unit
Console.WriteLine($"TotalDays: {ts.TotalDays}");
Console.WriteLine($"TotalHours: {ts.TotalHours}");
Console.WriteLine($"TotalMinutes: {ts.TotalMinutes}");
Console.WriteLine($"TotalSeconds: {ts.TotalSeconds}");
Console.WriteLine($"TotalMilliseconds: {ts.TotalMilliseconds}");
Console.WriteLine($"Ticks: {ts.Ticks}");

We print both sets of properties for an interval of two days, four hours, five minutes, nine seconds, and 250 milliseconds.

$ dotnet run
Days: 2
Hours: 4
Minutes: 5
Seconds: 9
Milliseconds: 250

TotalDays: 2.170245949074074
TotalHours: 52.085902777777775
TotalMinutes: 3125.1541666666667
TotalSeconds: 187509.25
TotalMilliseconds: 187509250
Ticks: 1875092500000

A frequent bug is using ts.Hours where ts.TotalHours is meant. For an interval of 30 hours, Hours is 6, while TotalHours is 30.

C# TimeSpan formatting

The standard format specifiers are c (constant, culture-invariant, the default), g (general short), and G (general long). Custom format strings are built from d, h, m, s, and f specifiers. The literal characters, including the colon and the period, must be escaped with a backslash.

Program.cs
using System.Globalization;

var ts = new TimeSpan(2, 4, 5, 9, 250);

Console.WriteLine(ts);
Console.WriteLine(ts.ToString("c"));
Console.WriteLine(ts.ToString("g"));
Console.WriteLine(ts.ToString("G"));
Console.WriteLine(ts.ToString(@"d\.hh\:mm\:ss"));
Console.WriteLine(ts.ToString(@"hh\:mm"));
Console.WriteLine(ts.ToString(@"h\h\ m\m\ s\s"));
Console.WriteLine($"{ts:hh\\:mm\\:ss\\.fff}");

// total hours are not available as a format specifier, so build the string
Console.WriteLine($"{(int)ts.TotalHours:00}:{ts.Minutes:00}:{ts.Seconds:00}");

Console.WriteLine(TimeSpan.FromSeconds(75).ToString("m\\:ss"));

The program formats one interval in several ways.

Console.WriteLine(ts.ToString(@"d\.hh\:mm\:ss"));

The backslashes escape the separators. Without them, the format string is invalid and a FormatException is thrown. A verbatim string avoids doubling the backslashes.

Console.WriteLine($"{(int)ts.TotalHours:00}:{ts.Minutes:00}:{ts.Seconds:00}");

There is no format specifier for total hours. When an interval can exceed 24 hours and we want to show, say, 52 hours, we build the string from the TotalHours property.

$ dotnet run
2.04:05:09.2500000
2.04:05:09.2500000
2:4:05:09.25
2:04:05:09.2500000
2.04:05:09
04:05
4h 5m 9s
04:05:09.250
52:05:09
1:15

C# TimeSpan parsing

The Parse and TryParse methods convert strings to TimeSpan. ParseExact requires a specific format.

Program.cs
using System.Globalization;

Console.WriteLine(TimeSpan.Parse("2.04:05:09"));
Console.WriteLine(TimeSpan.Parse("04:05"));
Console.WriteLine(TimeSpan.Parse("-00:30:00"));

Console.WriteLine(TimeSpan.ParseExact("1h30m", @"h\hm\m", CultureInfo.InvariantCulture));

foreach (var s in new[] { "01:30:00", "90", "1:75:00", "abc" })
{
    if (TimeSpan.TryParse(s, CultureInfo.InvariantCulture, out var ts))
    {
        Console.WriteLine($"'{s}' -> {ts}");
    }
    else
    {
        Console.WriteLine($"'{s}' is not valid");
    }
}

The program parses several strings.

foreach (var s in new[] { "01:30:00", "90", "1:75:00", "abc" })

A single number without separators is interpreted as days, so "90" is 90 days. The value "1:75:00" is rejected, because the minutes component must be less than 60. Use TryParse for user input.

$ dotnet run
2.04:05:09
04:05:00
-00:30:00
01:30:00
'01:30:00' -> 01:30:00
'90' -> 90.00:00:00
'1:75:00' is not valid
'abc' is not valid

Borodino battle

In the following example, we subtract two dates.

Program.cs
DateTime today = new DateTime(2026, 10, 18);
DateTime borodinoBattle = new DateTime(1812, 9, 7);

TimeSpan diff = today - borodinoBattle;

Console.WriteLine($"{diff.TotalDays} days have passed since the Battle of Borodino.");

In the example, we compute the number of days passed since the Battle of Borodino.

TimeSpan diff = today - borodinoBattle;

By subtracting the two DateTime objects, we get a TimeSpan.

Console.WriteLine($"{diff.TotalDays} days have passed since the Battle of Borodino.");

The TotalDays property has the number of days of the elapsed time. In this example we fixed the current date to October 18, 2026; use DateTime.Today to get the live value.

$ dotnet run
78203 days have passed since the Battle of Borodino.

C# TimeSpan for timeouts, timers and measuring

Many .NET APIs accept a TimeSpan for durations, such as timeouts and timer intervals. Stopwatch.Elapsed returns the measured time as a TimeSpan.

Program.cs
using System.Diagnostics;

var sw = Stopwatch.StartNew();
await Task.Delay(1200);
sw.Stop();

TimeSpan elapsed = sw.Elapsed;
Console.WriteLine($"elapsed about {Math.Round(elapsed.TotalSeconds, 1)} s");

// TimeSpan as a timeout
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300));
try
{
    await Task.Delay(TimeSpan.FromSeconds(5), cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("canceled by the timeout");
}

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
Console.WriteLine(client.Timeout);

Console.WriteLine(Timeout.InfiniteTimeSpan);

using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(200));
int ticks = 0;
while (await timer.WaitForNextTickAsync())
{
    Console.WriteLine($"tick {++ticks}");

    if (ticks == 3)
    {
        break;
    }
}

The program uses a TimeSpan in several common scenarios.

using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300));

The token source cancels itself after 300 ms, which interrupts the five second delay.

Console.WriteLine(Timeout.InfiniteTimeSpan);

Timeout.InfiniteTimeSpan is a special value of -1 millisecond that means "wait forever" in APIs such as HttpClient.Timeout. Do not confuse it with TimeSpan.Zero.

using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(200));

PeriodicTimer is the modern asynchronous timer; it waits for the next tick with WaitForNextTickAsync.

$ dotnet run
elapsed about 1.2 s
canceled by the timeout
00:00:10
-00:00:00.0010000
tick 1
tick 2
tick 3

C# TimeSpan, TimeOnly and TimeOfDay

A TimeSpan can be longer than 24 hours and can be negative. A TimeOnly (since .NET 6) represents a time of day, from midnight up to but not including 24:00. Subtracting two TimeOnly values gives a TimeSpan.

Program.cs
// TimeSpan measures a duration; TimeOnly represents a time of day
var open = new TimeOnly(9, 0);
var close = new TimeOnly(17, 30);

TimeSpan opening = close - open;
Console.WriteLine(opening);

var lunch = open.Add(TimeSpan.FromHours(3));
Console.WriteLine(lunch);

// TimeSpan has a maximum unit of a day and is not bounded to 24 h
Console.WriteLine(TimeSpan.FromHours(30));
Console.WriteLine(TimeSpan.FromHours(30).Hours);

// a time of day as a TimeSpan since midnight
Console.WriteLine(new DateTime(2026, 10, 18, 14, 25, 0).TimeOfDay);

The program shows how the types relate.

Console.WriteLine(TimeSpan.FromHours(30).Hours);

The component Hours wraps at 24 and returns 6; it is not the same as TotalHours.

Console.WriteLine(new DateTime(2026, 10, 18, 14, 25, 0).TimeOfDay);

The TimeOfDay property of a DateTime returns the time elapsed since midnight as a TimeSpan.

$ dotnet run
08:30:00
12:00 PM
1.06:00:00
6
14:25:00

Source

TimeSpan struct

In this article we have worked with the TimeSpan structure in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.