Introduction: Why Do Dates Give You a Headache?
If you've ever written code in C# and faced the task of passing a date and time between a server in Moscow and a client in New York, you've probably noticed strange bugs: "Why is it tomorrow for me?", "Where did those 3 hours come from?". Sound familiar?
In .NET, there are two main tools for working with dates: DateTime and DateTimeOffset. At first glance, they seem similar — both store a date and time. But the difference between them is key, and the wrong choice can lead to catastrophic errors in business logic.
In this tutorial, we'll break down DateTime vs DateTimeOffset with real examples, understand when to use which, and forever forget about confusion with time zones. Let's go!
What is DateTime? (And Why It's a Trap)
DateTime is a structure that stores a date and time. It has existed in .NET since the very first version. But it has one feature: it does not store time zone information. More precisely, it does store it, but only as a Kind flag:
DateTimeKind.Unspecified— time zone not specified (default).DateTimeKind.Utc— UTC time.DateTimeKind.Local— system local time.
The problem is that this flag is just information. It does not automatically convert the time. Imagine you save a DateTime with Kind = Local to a database on a server in Moscow. Then you read this date on a server in Vladivostok. What happens? Correct — the program sees that Kind = Local and thinks it's the local time of Vladivostok. But in reality, it's Moscow. An absolute catastrophe.
// Example of a problem with DateTimeDateTime moscowTime = new DateTime(2025, 3, 15, 14, 0, 0, DateTimeKind.Local);Console.WriteLine(moscowTime); // 15.03.2025 14:00:00 (assumed to be MSK)
// If this code is run on a server with time zone UTC+10 (Vladivostok)// DateTime will be interpreted as the local time of Vladivostok!// Which will lead to an error of 7 hours.What is DateTimeOffset? (The Developer's Savior)
DateTimeOffset is a smarter structure that appeared in .NET Framework 3.5. It stores a date, time, and offset from UTC (Offset). The offset is the number of hours and minutes your local time differs from UTC.
For example: 2025-03-15 14:00:00 +03:00 — this is the time in Moscow (UTC+3). If you pass this value to a server in Vladivostok (UTC+10), .NET will understand: "Aha, this is +03, meaning UTC = 11:00. Now I can easily convert it to local time: 21:00." No magic — pure mathematics.
// Example with DateTimeOffsetDateTimeOffset moscowOffset = new DateTimeOffset(2025, 3, 15, 14, 0, 0, TimeSpan.FromHours(3));Console.WriteLine(moscowOffset); // 15.03.2025 14:00:00 +03:00
// Now, even on a server in Vladivostok, we know the exact offset// and can correctly display the time for the userKey Differences: DateTime vs DateTimeOffset
Let's summarize everything in a clear table. This will help you quickly understand what to choose in a specific situation.
1. Storing Time Zone Information
- DateTime: Stores only a Kind flag (Unspecified, Utc, Local). Does not store the offset itself.
- DateTimeOffset: Stores the exact offset from UTC (e.g., +03:00, -05:00).
2. Comparing Values
- DateTime: Compares by the "bare" value. If you have 10:00 UTC and 10:00 MSK (UTC+3), then
DateTime.Equals()will say they are equal. Although in reality, they are different moments in time. - DateTimeOffset: Compares by the moment in time (UTC). 10:00 UTC and 13:00 MSK will be