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+) |
C# TimeSpan simple example
The following is a simple example that creates TimeSpan values.
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.
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.
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.
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.
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.
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.
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.
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.
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.
// 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
In this article we have worked with the TimeSpan structure in C#.
Author
List all C# tutorials.