Dates, Time, Globalization and Localization
Store instants in UTC (or as DateTimeOffset); convert to a local time zone only for display.
Concept
Date and Time Types A date and time with a Kind (Utc, Local, Unspecified) Local calendar values, legacy APIs
A date and time plus its UTC offset — an unambiguous instant Timestamps, logs, APIs, databases Timeouts, elapsed time A calendar date without time Birthdays, service dates A time of day without date Shift start, schedules
Time-zone rules including daylight saving Converting between zones An abstraction of "now" (.NET 8+) Testable time-dependent code
Store instants in UTC (or as DateTimeOffset); convert to a local time zone only for display. IANA IDs ("Europe/Berlin") work on all platforms in .NET 6+.
Culture and Formatting CultureInfo controls number, date, currency and text rules. ToString("N2", culture), ToString("d", culture), Parse(text, culture), ParseExact(text, format, CultureInfo.InvariantCulture). CultureInfo.CurrentCulture affects formatting; CurrentUICulture selects localized resources.
.resx resource files per culture (Strings.resx, Strings.de.resx) plus IStringLocalizer<T> (ASP.NET Core) or the generated resource class select translated text based on CurrentUICulture.
Unicode and Encoding.NET strings are UTF-16. Files and network data are usually UTF-8: Encoding.UTF8.GetBytes, GetString, GetByteCount. A char is a UTF-16 code unit — some characters (emoji) need two; use Rune or StringInfo for user-perceived characters.
Why It Matters
Test logs, diagnostic sessions and fleet data span time zones and daylight-saving changes; reports go to users in many locales. Time-zone and culture bugs are notoriously hard to spot and expensive in production.
Syntax
DateTimeOffset now = DateTimeOffset.UtcNow;
var local = TimeZoneInfo.ConvertTime(now, TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin"));
string s = value.ToString("N2", CultureInfo.GetCultureInfo("de-DE"));
DateTime d = DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture);Example
using System.Globalization;
using System.Text;
// DateTime vs DateTimeOffset
var local = new DateTime(2026, 3, 10, 14, 30, 0, DateTimeKind.Unspecified);
var utc = new DateTime(2026, 3, 10, 9, 0, 0, DateTimeKind.Utc);
var offset = new DateTimeOffset(2026, 3, 10, 14, 30, 0, TimeSpan.FromHours(5.5)); // India
Console.WriteLine($"DateTime (unspecified): {local:O}, Kind {local.Kind}");
Console.WriteLine($"DateTime (UTC): {utc:O}");
Console.WriteLine($"DateTimeOffset: {offset:O} = {offset.UtcDateTime:O} UTC");
// TimeSpan arithmetic
TimeSpan testDuration = TimeSpan.FromMinutes(95);
DateTime end = utc + testDuration;
Console.WriteLine($"duration {testDuration} ({testDuration.TotalHours:F2} h), ends {end:HH:mm}, difference {end - utc}");
// DateOnly and TimeOnly
var serviceDate = new DateOnly(2026, 3, 10);
var shiftStart = new TimeOnly(6, 0);
Console.WriteLine($"DateOnly {serviceDate:yyyy-MM-dd} ({serviceDate.DayOfWeek}), +90 days {serviceDate.AddDays(90):yyyy-MM-dd}; TimeOnly {shiftStart}, +10h {shiftStart.AddHours(10)}");
// Time zones (IANA ids work on all platforms in .NET 6+)
var berlin = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
var tokyo = TimeZoneInfo.FindSystemTi
…Expected Result
The same number prints as 1,234,567.89, 1.234.567,89, 1 234 567,89 (with a narrow no-break space) and 12,34,567.89 depending on culture. Berlin's offset changes from +1 h to +2 h in summer.
DateTime (unspecified): 2026-03-10T14:30:00.0000000, Kind Unspecified
DateTime (UTC): 2026-03-10T09:00:00.0000000Z
DateTimeOffset: 2026-03-10T14:30:00.0000000+05:30 = 2026-03-10T09:00:00.0000000Z UTC
duration 01:35:00 (1.58 h), ends 10:35, difference 01:35:00
DateOnly 2026-03-10 (Tuesday), +90 days 2026-06-08; TimeOnly 06:00, +10h 16:00
09:00 UTC = 10:00 Berlin = 18:00 Tokyo
Berlin offset in March: 01:00:00, in July: 02:00:00 (daylight saving)
en-US: 1,234,567.89 3/10/2026 March
de-DE: 1.234.567,89 10.03.2026 März
fr-FR: 1 234 567,89 10/03/2026 mars
hi-IN: 12,34,567.89 10/3/2026 मार्च
ja-JP: 1,234,567.89 2026/03/10 3月
invariant: 1,234,567.89 2026-03-10
ParseExact -> 2026-03-10T14:30:00.0000000
TryParse("31/02/2026") = False
chars: 11, UTF-8 bytes: 18, UTF-16 bytes: 22
UTF-8 of 'ü': C3BC, of '温': E6B8A9
"resume" vs "résumé": ordinal equal False, culture-aware ignoring accents TrueCommon Mistakes
Storing local DateTime values in databases and logs (ambiguous during daylight-saving changes).
DateTime.Now in business logic and tests — inject a TimeProvider/clock.
Parsing dates without a format and culture ("03/10/2026" is March 10 in the US and October 3 elsewhere).
Using the machine's current culture for machine-readable output (files, protocols, JSON).
Assuming one char is one character (emoji, some Asian characters).
Hard-coding Windows time-zone IDs in cross-platform code.
Best Practices
Use DateTimeOffset (or UTC DateTime) for instants; DateOnly/TimeOnly for calendar values.
Format with ISO 8601 ("O") for machine data.
Use CultureInfo.InvariantCulture for persistence and protocols; current culture for display.
Keep translatable strings in resource files from the start.
Interview Relevance
Medium: "DateTime vs DateTimeOffset", "how do you handle time zones?", "what is the invariant culture?", "UTF-8 vs UTF-16".
Practice Questions
Convert a list of UTC test timestamps to the tester's local time zone.
Compute the number of working days between two DateOnly values.
Parse "2026-03-10T14:30:00+05:30" and print it in UTC and Berlin time.
Format a price in five cultures, and parse user input in German format.
Count the user-perceived characters in a string containing emoji.
Quick Quiz
Q1. (Multiple choice) Best type for an unambiguous event timestamp? A) DateTime (Local) B) DateTimeOffset C) TimeOnly D) string Answer: B.
Q2. (Predict the output) 1234567.891.ToString("N2", CultureInfo.GetCultureInfo("de-DE")) Answer: 1.234.567,89 (verified).
Q3. (True/False) DateTime.TryParseExact("31/02/2026", "dd/MM/yyyy", ...) returns true.
Answer: False. February 31 does not exist (verified).
Q4. (Predict the output) UTF-8 byte count of "ü"?
Answer: 2 bytes (C3BC, verified).
Coding Challenge
Write a method that takes a UTC DateTime and a list of IANA time-zone IDs and prints the local time in each, marking whether daylight saving time is in effect.
static void ShowTimes(DateTime utc, string[] zoneIds)
{
foreach (string id in zoneIds)
{
var zone = TimeZoneInfo.FindSystemTimeZoneById(id);
DateTime local = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);
Console.WriteLine($"{id,-20} {local:yyyy-MM-dd HH:mm} DST: {zone.IsDaylightSavingTime(local)}");
}
}
ShowTimes(new DateTime(2026, 7, 1, 12, 0, 0, DateTimeKind.Utc), ["Europe/Berlin", "Asia/Kolkata", "America/New_York"]);