Time is relative. Especially in code
Imagine: you are writing an application for booking airline tickets. A user from New York selects a flight at 14:00 local time, and the server in London saves it as DateTime.Now. What could go wrong? Absolutely everything. In the best case — a ticket three hours earlier, in the worst case — the user misses the flight and leaves an angry review.
Working with time zones in C# is a topic many developers put off "for later." But it is precisely this that separates amateur code from professional code. Today we will figure out how to make your application friends with time zones, not go crazy with daylight saving time, and not lose a single user.
DateTime vs DateTimeOffset: The First Clash with Reality
Many beginners in C# use DateTime for everything. And that's fine until you encounter global users. The problem is that DateTime itself doesn't know which time zone it is in. It's just a "moment in time" without context.
// Bad: time without contextDateTime localTime = DateTime.Now; // 14:00 — but where?DateTime utcTime = DateTime.UtcNow; // better, but still not ideal
// Good: time with offsetDateTimeOffset withOffset = DateTimeOffset.Now; // 14:00 +03:00Console.WriteLine(withOffset); // 06.04.2025 14:00:00 +03:00DateTimeOffset is your first step towards conscious time management. It stores not only the date and time but also the offset relative to UTC. This saves the day when you need to transfer data between systems or simply show the user the correct local time.
When is DateTime still needed?
Honestly? Almost never for storing moments in time. If you are writing code for a local desktop application — it's okay. But as soon as a database, API, or multiple users appear — switch to DateTimeOffset or DateTime with an explicit DateTimeKind.Utc.
TimeZoneInfo: Your Personal Time Zone Atlas
In .NET, there is a powerful class TimeZoneInfo. It allows you to work with any time zones known to the operating system. And there are hundreds of them — from the standard UTC+0 to exotic zones like "Lord Howe Island" with a 30-minute offset.
// Get a list of all available zonesReadOnlyCollection<TimeZoneInfo> allZones = TimeZoneInfo.GetSystemTimeZones();foreach (var zone in allZones.Take(5)){ Console.WriteLine($"{zone.Id}: {zone.DisplayName}");}// Output (example):// UTC: (UTC) Coordinated Universal Time// Russian Standard Time: (UTC+03:00) Moscow, St. Petersburg// Eastern Standard Time: (UTC-05:00) Eastern Time (US & Canada)Converting Time Between Zones
Let's say a user from Moscow wants to know what time a webinar starts in London. Here's how to do it correctly:
// Original time — Moscow timeDateTime moscowTime = new DateTime(2025, 6, 15, 18, 0, 0);TimeZoneInfo moscowZone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
// ConversionDateTime londonTime = TimeZoneInfo.ConvertTime(moscowTime, moscowZone, londonZone);Console.WriteLine($"In Moscow: {moscowTime}, in London: {londonTime}");// In Moscow: 15.06.2025 18:00:00, in London: 15.06.2025 16:00:00Note: we explicitly specify the source and target zones. This eliminates ambiguity. TimeZoneInfo.ConvertTime will account for the time difference itself, including daylight saving/winter time.
Daylight Saving Time (DST): The Developer's Main Enemy
Twice a year, the world goes crazy. In March, clocks are set forward, in October — back. And if your application is not ready for DST, users will see either an "extra" hour or a missed one. C# handles this automatically