Menu

C# DateTime and TimeSpan: Now, UtcNow, Add, Subtract, Compare and Parse

Working with dates and times in C#: creating DateTime values, Now vs UtcNow vs Today, adding days and months, subtracting to get a TimeSpan, TotalHours vs Hours, comparing dates, DayOfWeek, parsing with ParseExact and TryParse, DateTimeOffset, and DateOnly.

This page includes runnable editors - edit, run, and see output instantly.

System.DateTime represents a date and a time of day, from year 1 to year 9999, with a precision of 100 nanoseconds (one "tick"). System.TimeSpan represents a duration: the difference between two DateTime values. Both are immutable value types (structs), so every operation returns a new value.

Output:

2026-09-24 00:00:00
2026-09-24 14:30:00
14:30:05.250
2026 9 24
14:30
Thursday
267
2026-09-24 00:00
14:30:00

Every example on this page prints dates with an explicit format string. The default ToString() follows the current culture (9/24/2026 2:30:00 PM in the US, 24.09.2026 14:30:00 in Germany), so its output depends on the machine. The format codes are on DateTime format.

An invalid date throws: new DateTime(2026, 2, 30) raises an ArgumentOutOfRangeException, as does month 13 or hour 24.

Now, UtcNow and Today

Three static properties read the clock:

Example output:

Now:    2026-09-24 18:20:41 (Local)
UtcNow: 2026-09-24 16:20:41 (Utc)
Today:  2026-09-24 00:00:00

In this example the local time zone is two hours ahead of UTC, so the first two lines differ by two hours; on a machine set to UTC they match. The Kind property records whether a value is Local, Utc or Unspecified (the default for dates you construct yourself). Use DateTime.UtcNow for anything you store, log, compare or send to another system: it does not jump when daylight saving time starts or ends, and it means the same moment on every server. Convert to local time only when showing a value to a person.

To measure how long code takes, use System.Diagnostics.Stopwatch rather than subtracting two DateTime.Now values; it has much finer resolution and is not affected by clock adjustments.

Adding and subtracting time

AddDays, AddHours, AddMinutes, AddSeconds, AddMonths and AddYears return a new DateTime. Pass a negative number to go back. Because DateTime is immutable, the result must be assigned:

Output:

2026-01-31
2026-02-03 09:00
2026-01-30 21:00
2026-02-28
2027-01-31
10:30
29
True

AddMonths clamps to the last day of the month when the day does not exist: January 31 plus one month is February 28 (or 29 in a leap year), not March 3. Adding one month twice and adding two months can therefore give different dates.

Subtracting dates: TimeSpan

Subtracting one DateTime from another gives a TimeSpan:

Output:

3.20:30:00
Days: 3, Hours: 20, Minutes: 30
TotalDays: 3.85
TotalHours: 92.5
TotalMinutes: 5550
Nights: 4

This is the part of the API people get wrong most often. Days, Hours, Minutes and Seconds are the components of the span (3 days, 20 hours, 30 minutes). TotalDays, TotalHours and TotalMinutes are the whole duration in one unit, as a double. "How many hours did the guest stay?" is TotalHours (92.5), not Hours (20).

The last line shows a related point: 3.85 days elapsed, but the guest stayed 4 nights. Comparing the .Date parts counts calendar days, which is usually what billing and "days until" displays want.

Creating and formatting TimeSpan values

Output:

02:15:00
01:30:00
1.12:00:00
True
03:45:00
True
02:15
36h 0m
00:00:00

TimeSpan supports +, -, comparisons, Duration() (absolute value) and Negate(). Custom formats such as @"hh\:mm" need a backslash before literal characters, and hh there shows only the hours component (0 to 23), so for durations over a day, build the text from TotalHours as in the second to last line.

Comparing dates

DateTime supports ==, !=, <, >, <= and >=, plus CompareTo and DateTime.Compare. To compare only the date and ignore the time, compare the .Date properties:

Output:

True
True
1
True
2026-09-01

For "is this timestamp within September 30?", compare against the start of the next day with <, as above. Writing check <= end would exclude everything after midnight on the last day, because end is 2026-09-30 00:00:00.

Comparisons look only at the ticks, not at Kind: a Local value and a Utc value that print the same are considered equal even though they are different moments. Another reason to keep stored times in UTC.

Day of week and start of week

DayOfWeek is an enum from Sunday (0) to Saturday (6). Arithmetic on it finds weekdays and week boundaries:

Output:

Thursday
4
Weekend: False
Week starts 2026-09-21 (Monday)
Next Friday: 2026-09-25
2026-09-01 to 2026-09-30

The day names printed by DayOfWeek.ToString() are always English. For a localized name, format the date with "dddd" and a culture.

Parsing dates from strings

When you know the format of the input, use ParseExact or TryParseExact with CultureInfo.InvariantCulture. The format string uses the same codes as formatting:

Output:

2026-09-24 00:00
2026-09-24 18:05
'2026-02-28' -> Saturday, February 28
'2026-02-30' -> invalid
'28.02.2026' -> invalid
'' -> invalid
2026-02-28
2026-09-24 10:00 Utc

DateTime.Parse(text) without a format tries to guess using the current culture. "03/04/2026" is March 4 on a US machine and April 3 on a British one, and a date that parses on your laptop can throw a FormatException on a server. Keep Parse for input typed by a local user; use ParseExact with the invariant culture for files, APIs and databases. ParseExact throws FormatException when the text does not match; TryParseExact returns false instead.

Calculating an age

Subtracting birth dates and dividing by 365 is wrong around birthdays and leap years. Compare years, then correct if this year's birthday has not happened yet:

Output:

36
35
18
70 days to go

DateTimeOffset

A DateTime does not record which time zone it is in beyond the vague Kind flag. DateTimeOffset stores the value together with its offset from UTC, so it always identifies one exact moment:

Output:

2026-09-24 14:00 +02:00
2026-09-24 12:00
2026-09-24 12:30
00:30:00
21:00 +09:00

Use DateTimeOffset (or UTC DateTime values) for timestamps: when an order was placed, when a message was sent. Databases and JSON serializers handle it well. For converting between named time zones with daylight saving rules, use TimeZoneInfo.ConvertTime; the zone IDs differ by operating system on older .NET versions ("Europe/Paris" on Linux, "Romance Standard Time" on Windows), and .NET 6 and later accept both.

DateOnly and TimeOnly (.NET 6 and later)

Many values are a date with no time (a birthday, a due date) or a time with no date (opening hours). .NET 6 added two types for them:

// .NET 6 and later
DateOnly birthday = new DateOnly(1990, 9, 24);
DateOnly due = DateOnly.FromDateTime(DateTime.Today).AddDays(14);
int daysLeft = due.DayNumber - DateOnly.FromDateTime(DateTime.Today).DayNumber;

TimeOnly opens = new TimeOnly(9, 0);
TimeOnly closes = new TimeOnly(17, 30);
bool isOpen = TimeOnly.FromDateTime(DateTime.Now).IsBetween(opens, closes);

They remove a class of bugs where a stray time or time zone shifts a date by one day. Older code, and code targeting .NET Framework or Unity, uses DateTime with the time left at midnight.

Common mistakes

  • Discarding the result of AddDays. DateTime is immutable; assign the returned value.
  • Using Hours instead of TotalHours. Components versus total duration.
  • Storing DateTime.Now. Store UTC and convert for display.
  • Calling ToString() without a format in logs, files or tests, where the output depends on the machine's culture.
  • Parsing with DateTime.Parse on machine data. Use ParseExact and the invariant culture.
  • Mixing up mm and MM in format strings (minutes and months). See DateTime format.

Frequently Asked Questions

What is the difference between DateTime.Now and DateTime.UtcNow?

DateTime.Now is the current time in the computer's local time zone, with Kind set to Local. DateTime.UtcNow is the current time in UTC, with Kind set to Utc, and it is also faster because it skips the time zone conversion. Store and compare timestamps in UTC, and convert to local time only for display.

How do I get the difference between two dates in C#?

Subtract them: TimeSpan gap = end - start;. Then read gap.TotalDays, gap.TotalHours or gap.TotalMinutes for the whole duration as a double, or gap.Days for the whole-day part. For calendar months or years there is no built-in property, because months have different lengths; compare the year and month fields yourself.

What is the difference between TimeSpan.Hours and TotalHours?

Hours is only the hours component, from 0 to 23, after whole days are taken out. TotalHours is the entire duration expressed in hours, as a double. For a span of 1 day and 3 hours, Hours is 3 and TotalHours is 27. Using Hours where TotalHours was meant is a very common bug.

How do I parse a date string in C#?

When you know the format, use DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture), or DateTime.TryParseExact to get false instead of a FormatException on bad input. DateTime.Parse guesses the format from the current culture, so 03/04/2026 means March 4 in the US and 3 April in the UK.

Why doesn't AddDays change my DateTime?

DateTime is an immutable value type. AddDays, AddHours and the other methods return a new DateTime and leave the original unchanged, so you must assign the result: due = due.AddDays(7);.

When should I use DateTimeOffset instead of DateTime?

Use DateTimeOffset for timestamps that must identify an exact moment, such as when an order was placed or a log entry was written, especially if data moves between servers and time zones. It stores the offset from UTC with the value. DateTime is fine for UTC-only timestamps and for dates without a meaningful time zone.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED