Time⚓︎
Let's covers the core time-related types in .NET:
TimeZoneInfo,DateTime, andDateTimeOffset.
TimeZoneInfo⚓︎
TimeZoneInfo represents time zones and their rules (standard time and daylight saving time). It is used to convert between time zones and apply historical/future rules.
Key points
- Backed by the system time zone database (IDs differ between Windows and Linux/macOS).
- Always specify source and destination time zones to avoid accidental local-time assumptions.
TimeZoneInfo in .NET
using System;
class Demo
{
static void Main()
{
string tzId = OperatingSystem.IsWindows() ? "China Standard Time" : "Asia/Shanghai";
var shanghai = TimeZoneInfo.FindSystemTimeZoneById(tzId);
var utc = TimeZoneInfo.Utc;
Console.WriteLine($"Zone: {shanghai.Id}");
Console.WriteLine($"UTC: {utc.Id}");
DateTime localTime = new DateTime(2024, 1, 20, 10, 0, 0, DateTimeKind.Unspecified);
DateTime utcFromZone = TimeZoneInfo.ConvertTimeToUtc(localTime, shanghai);
DateTime zoneFromUtc = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(2024, 1, 20, 2, 0, 0, DateTimeKind.Utc), shanghai);
Console.WriteLine($"Zone Time: {localTime:o}");
Console.WriteLine($"UTC From Zone: {utcFromZone:o}");
Console.WriteLine($"Zone From UTC: {zoneFromUtc:o}");
}
}
// >>> Zone: China Standard Time
// >>> UTC: UTC
// >>> Zone Time: 2024-01-20T10:00:00.0000000
// >>> UTC From Zone: 2024-01-20T02:00:00.0000000Z
// >>> Zone From UTC: 2024-01-20T10:00:00.0000000+08:00
TimeZoneInfo conversion example
using System;
class Demo
{
static void Main()
{
// Windows: "China Standard Time"
// Linux/macOS: "Asia/Shanghai"
string tzId = OperatingSystem.IsWindows() ? "China Standard Time" : "Asia/Shanghai";
var shanghai = TimeZoneInfo.FindSystemTimeZoneById(tzId);
DateTime utc = new DateTime(2024, 1, 20, 2, 0, 0, DateTimeKind.Utc);
DateTime shanghaiTime = TimeZoneInfo.ConvertTimeFromUtc(utc, shanghai);
Console.WriteLine($"UTC: {utc:o}");
Console.WriteLine($"Shanghai: {shanghaiTime:o}");
}
}
// >>> UTC: 2024-01-20T02:00:00.0000000Z
// >>> Shanghai: 2024-01-20T10:00:00.0000000+08:00
DateTime⚓︎
DateTime represents a date and time without a specific time zone. The "kind" is indicated by DateTime.Kind: Local, Utc, or Unspecified.
Key points
- Storing local time with
DateTimecan be risky across systems or time zones. - Prefer
DateTime.UtcNowfor time points to avoid DST ambiguity. DateTime.Kindis just a flag; it does not convert values.
DateTime in .NET
using System;
class Demo
{
static void Main()
{
string tzId = OperatingSystem.IsWindows() ? "China Standard Time" : "Asia/Shanghai";
var shanghai = TimeZoneInfo.FindSystemTimeZoneById(tzId);
DateTime utc = new DateTime(2024, 1, 20, 2, 0, 0, DateTimeKind.Utc);
DateTime unspecified = new DateTime(2024, 1, 20, 10, 0, 0, DateTimeKind.Unspecified);
DateTime localInZone = TimeZoneInfo.ConvertTimeFromUtc(utc, shanghai);
Console.WriteLine($"UTC: {utc:o} Kind={utc.Kind}");
Console.WriteLine($"Unspecified: {unspecified:o} Kind={unspecified.Kind}");
Console.WriteLine($"Zone Time: {localInZone:o} Kind={localInZone.Kind}");
DateTime zoneToUtc = TimeZoneInfo.ConvertTimeToUtc(unspecified, shanghai);
DateTime utcToZone = TimeZoneInfo.ConvertTimeFromUtc(utc, shanghai);
Console.WriteLine($"Zone->UTC: {zoneToUtc:o}");
Console.WriteLine($"UTC->Zone: {utcToZone:o}");
}
}
// >>> UTC: 2024-01-20T02:00:00.0000000Z Kind=Utc
// >>> Unspecified: 2024-01-20T10:00:00.0000000 Kind=Unspecified
// >>> Zone Time: 2024-01-20T10:00:00.0000000+08:00 Kind=Local
// >>> Zone->UTC: 2024-01-20T02:00:00.0000000Z
// >>> UTC->Zone: 2024-01-20T10:00:00.0000000+08:00
Parsing and serializing DateTime
using System;
using System.Globalization;
class Demo
{
static void Main()
{
// ISO 8601 string
string iso = "2024-01-20T15:30:00Z";
DateTime parsed = DateTime.Parse(iso, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
Console.WriteLine($"Parsed: {parsed:o} Kind={parsed.Kind}");
// Serialize as UTC
DateTime utc = new DateTime(2024, 1, 20, 2, 0, 0, DateTimeKind.Utc);
string serialized = utc.ToString("o");
Console.WriteLine($"Serialized: {serialized}");
}
}
// >>> Parsed: 2024-01-20T15:30:00.0000000Z Kind=Utc
// >>> Serialized: 2024-01-20T02:00:00.0000000Z
DateTimeOffset⚓︎
DateTimeOffset represents an absolute point in time as local time plus an offset. It explicitly carries the UTC offset and is better for cross-time-zone scenarios.
Key points
- The offset is a numeric value and does not include time zone rules (e.g., DST changes).
- Prefer
DateTimeOffsetfor storing time points, especially across regions.
UTC offset and Kind
When assigning values, 2024-01-20T10:00:00.0000000+00:00 will be treated below:
- As DateTimeOffset, it means 2024-01-20 10:00 at UTC+0 offset.
- As DateTime, it means 2024-01-20 10:00 with Kind=Unspecified unless parsed/constructed differently. Some of the system will treat it as UTC, some as local time.
DateTimeOffset in .NET
using System;
class Demo
{
static void Main()
{
DateTimeOffset fixedLocal = new DateTimeOffset(2024, 1, 20, 10, 0, 0, TimeSpan.FromHours(8));
DateTimeOffset fixedUtc = fixedLocal.ToUniversalTime();
Console.WriteLine($"Local: {fixedLocal:o}");
Console.WriteLine($"UTC: {fixedUtc:o}");
// Convert to a specific offset
DateTimeOffset toOffset = fixedLocal.ToOffset(TimeSpan.FromHours(8));
Console.WriteLine($"+08:00: {toOffset:o}");
}
}
// >>> Local: 2024-01-20T10:00:00.0000000+08:00
// >>> UTC: 2024-01-20T02:00:00.0000000+00:00
// >>> +08:00: 2024-01-20T10:00:00.0000000+08:00
Convert to/from DateTime
using System;
class Demo
{
static void Main()
{
DateTimeOffset dto = new DateTimeOffset(2024, 1, 20, 2, 0, 0, TimeSpan.Zero);
DateTime utc = dto.UtcDateTime;
DateTime local = dto.ToOffset(TimeSpan.FromHours(8)).DateTime;
Console.WriteLine($"DTO: {dto:o}");
Console.WriteLine($"UTC DateTime: {utc:o} Kind={utc.Kind}");
Console.WriteLine($"Local DateTime: {local:o} Kind={DateTimeKind.Unspecified}");
}
}
// >>> DTO: 2024-01-20T02:00:00.0000000+00:00
// >>> UTC DateTime: 2024-01-20T02:00:00.0000000Z Kind=Utc
// >>> Local DateTime: 2024-01-20T10:00:00.0000000 Kind=Unspecified
Recommendation⚓︎
- If you need a time point across time zones, prefer
DateTimeOffset. - If you only use local time within a single environment,
DateTimecan be acceptable; still preferUtcNow. - For time zone rules and conversions, use
TimeZoneInfo.