feat: add .NET timezone handling guidance and reference materials (#1123)

* feat: add .NET timezone handling guidance and reference materials

* feat: update Finland, Lithuania, Estonia timezone reference in index

* feat: remove Finland, Lithuania, Estonia timezone reference from index
This commit is contained in:
Pasindu Premarathna
2026-03-23 06:00:33 +05:30
committed by GitHub
parent dc66a73d74
commit b3de20181a
4 changed files with 350 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
# .NET Timezone Code Patterns
## Pattern 1: Basic TimeZoneInfo
Use this only when the application is Windows-only and Windows timezone IDs are acceptable.
```csharp
DateTime utcNow = DateTime.UtcNow;
TimeZoneInfo sriLankaTz = TimeZoneInfo.FindSystemTimeZoneById("Sri Lanka Standard Time");
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, sriLankaTz);
DateTime backToUtc = TimeZoneInfo.ConvertTimeToUtc(localTime, sriLankaTz);
TimeZoneInfo tokyoTz = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime tokyoTime = TimeZoneInfo.ConvertTime(localTime, sriLankaTz, tokyoTz);
```
Use `TimeZoneConverter` or `NodaTime` instead for Linux, containers, or mixed environments.
## Pattern 2: Cross-Platform With TimeZoneConverter
Recommended default for most .NET apps that run across Windows and Linux.
```xml
<PackageReference Include="TimeZoneConverter" Version="6.*" />
```
```csharp
using TimeZoneConverter;
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Asia/Colombo");
DateTime converted = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
```
This also accepts Windows IDs:
```csharp
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Sri Lanka Standard Time");
```
## Pattern 3: NodaTime
Use this for strict timezone arithmetic, recurring schedules, or DST edge cases where correctness matters more than minimal dependencies.
```xml
<PackageReference Include="NodaTime" Version="3.*" />
```
```csharp
using NodaTime;
DateTimeZone colomboZone = DateTimeZoneProviders.Tzdb["Asia/Colombo"];
Instant now = SystemClock.Instance.GetCurrentInstant();
ZonedDateTime colomboTime = now.InZone(colomboZone);
DateTimeZone tokyoZone = DateTimeZoneProviders.Tzdb["Asia/Tokyo"];
ZonedDateTime tokyoTime = colomboTime.WithZone(tokyoZone);
LocalDateTime localDt = new LocalDateTime(2024, 6, 15, 14, 30, 0);
ZonedDateTime zoned = colomboZone.AtStrictly(localDt);
Instant utcInstant = zoned.ToInstant();
```
## Pattern 4: DateTimeOffset For APIs
Prefer `DateTimeOffset` for values crossing service or process boundaries.
```csharp
using TimeZoneConverter;
DateTimeOffset utcNow = DateTimeOffset.UtcNow;
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Asia/Colombo");
DateTimeOffset colomboTime = TimeZoneInfo.ConvertTime(utcNow, tz);
```
## Pattern 5: ASP.NET Core Persistence And Presentation
Store UTC, convert at the edges.
```csharp
using TimeZoneConverter;
entity.CreatedAtUtc = DateTime.UtcNow;
public DateTimeOffset ToUserTime(DateTime utc, string userIanaTimezone)
{
var tz = TZConvert.GetTimeZoneInfo(userIanaTimezone);
return TimeZoneInfo.ConvertTimeFromUtc(utc, tz);
}
```
## Pattern 6: Scheduling And Recurring Jobs
Translate a user-facing local time to UTC before scheduling.
```csharp
using TimeZoneConverter;
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Asia/Colombo");
DateTime scheduledLocal = new DateTime(2024, 12, 1, 9, 0, 0, DateTimeKind.Unspecified);
DateTime scheduledUtc = TimeZoneInfo.ConvertTimeToUtc(scheduledLocal, tz);
```
With Hangfire:
```csharp
RecurringJob.AddOrUpdate(
"morning-job",
() => DoWork(),
"0 9 * * *",
new RecurringJobOptions { TimeZone = tz });
```
## Pattern 7: Ambiguous And Invalid DST Times
Check for repeated or skipped local timestamps when the timezone observes daylight saving time.
```csharp
using TimeZoneConverter;
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("America/New_York");
DateTime localTime = new DateTime(2024, 11, 3, 1, 30, 0);
if (tz.IsAmbiguousTime(localTime))
{
var offsets = tz.GetAmbiguousTimeOffsets(localTime);
var standardOffset = offsets.Min();
var dto = new DateTimeOffset(localTime, standardOffset);
}
if (tz.IsInvalidTime(localTime))
{
localTime = localTime.AddHours(1);
}
```
## Common Mistakes
| Wrong | Better |
| --- | --- |
| `DateTime.Now` in server code | `DateTime.UtcNow` |
| Storing local timestamps in the database | Store UTC and convert for display |
| Hardcoding offsets such as `+05:30` | Use timezone IDs |
| Using `FindSystemTimeZoneById("Asia/Colombo")` on Windows | Use `TZConvert.GetTimeZoneInfo("Asia/Colombo")` |
| Comparing local `DateTime` values from different zones | Compare UTC or use `DateTimeOffset` |
| Creating `DateTime` without intentional kind semantics | Use `Utc`, `Local`, or deliberate `Unspecified` |
## Decision Guide
- Use `TimeZoneInfo` only for Windows-only code with Windows IDs.
- Use `TimeZoneConverter` for most cross-platform applications.
- Use `NodaTime` when DST arithmetic or calendaring accuracy is central.
- Use `DateTimeOffset` for APIs and serialized timestamps.

View File

@@ -0,0 +1,87 @@
# .NET Timezone Reference Index
## Windows To IANA Mapping
Use this file for common mappings between Windows timezone IDs and IANA timezone IDs.
### Asia And Pacific
| Display Name | Windows ID | IANA ID | UTC Offset | DST? |
| --- | --- | --- | --- | --- |
| Sri Lanka Standard Time | Sri Lanka Standard Time | Asia/Colombo | +05:30 | No |
| India Standard Time | India Standard Time | Asia/Calcutta | +05:30 | No |
| Pakistan Standard Time | Pakistan Standard Time | Asia/Karachi | +05:00 | No |
| Bangladesh Standard Time | Bangladesh Standard Time | Asia/Dhaka | +06:00 | No |
| Nepal Standard Time | Nepal Standard Time | Asia/Katmandu | +05:45 | No |
| SE Asia Standard Time | SE Asia Standard Time | Asia/Bangkok | +07:00 | No |
| Singapore Standard Time | Singapore Standard Time | Asia/Singapore | +08:00 | No |
| China Standard Time | China Standard Time | Asia/Shanghai | +08:00 | No |
| Tokyo Standard Time | Tokyo Standard Time | Asia/Tokyo | +09:00 | No |
| Korea Standard Time | Korea Standard Time | Asia/Seoul | +09:00 | No |
| AUS Eastern Standard Time | AUS Eastern Standard Time | Australia/Sydney | +10:00/+11:00 | Yes |
| New Zealand Standard Time | New Zealand Standard Time | Pacific/Auckland | +12:00/+13:00 | Yes |
| Arabian Standard Time | Arabian Standard Time | Asia/Dubai | +04:00 | No |
| Arab Standard Time | Arab Standard Time | Asia/Riyadh | +03:00 | No |
| Israel Standard Time | Israel Standard Time | Asia/Jerusalem | +02:00/+03:00 | Yes |
| Turkey Standard Time | Turkey Standard Time | Europe/Istanbul | +03:00 | No |
### Europe
| Display Name | Windows ID | IANA ID | UTC Offset | DST? |
| --- | --- | --- | --- | --- |
| UTC | UTC | Etc/UTC | +00:00 | No |
| GMT Standard Time | GMT Standard Time | Europe/London | +00:00/+01:00 | Yes |
| W. Europe Standard Time | W. Europe Standard Time | Europe/Berlin | +01:00/+02:00 | Yes |
| Central Europe Standard Time | Central Europe Standard Time | Europe/Budapest | +01:00/+02:00 | Yes |
| Romance Standard Time | Romance Standard Time | Europe/Paris | +01:00/+02:00 | Yes |
| E. Europe Standard Time | E. Europe Standard Time | Asia/Nicosia | +02:00/+03:00 | Yes |
| GTB Standard Time | GTB Standard Time | Europe/Bucharest | +02:00/+03:00 | Yes |
| Russian Standard Time | Russian Standard Time | Europe/Moscow | +03:00 | No |
### Americas
| Display Name | Windows ID | IANA ID | UTC Offset | DST? |
| --- | --- | --- | --- | --- |
| Eastern Standard Time | Eastern Standard Time | America/New_York | -05:00/-04:00 | Yes |
| Central Standard Time | Central Standard Time | America/Chicago | -06:00/-05:00 | Yes |
| Mountain Standard Time | Mountain Standard Time | America/Denver | -07:00/-06:00 | Yes |
| Pacific Standard Time | Pacific Standard Time | America/Los_Angeles | -08:00/-07:00 | Yes |
| Alaskan Standard Time | Alaskan Standard Time | America/Anchorage | -09:00/-08:00 | Yes |
| Hawaiian Standard Time | Hawaiian Standard Time | Pacific/Honolulu | -10:00 | No |
| Canada Central Standard Time | Canada Central Standard Time | America/Regina | -06:00 | No |
| SA Eastern Standard Time | SA Eastern Standard Time | America/Cayenne | -03:00 | No |
| E. South America Standard Time | E. South America Standard Time | America/Sao_Paulo | -03:00/-02:00 | Yes |
### Africa
| Display Name | Windows ID | IANA ID | UTC Offset | DST? |
| --- | --- | --- | --- | --- |
| South Africa Standard Time | South Africa Standard Time | Africa/Johannesburg | +02:00 | No |
| Egypt Standard Time | Egypt Standard Time | Africa/Cairo | +02:00 | No |
| E. Africa Standard Time | E. Africa Standard Time | Africa/Nairobi | +03:00 | No |
| W. Central Africa Standard Time | W. Central Africa Standard Time | Africa/Lagos | +01:00 | No |
| Morocco Standard Time | Morocco Standard Time | Africa/Casablanca | +00:00/+01:00 | Yes |
## NodaTime Providers
```csharp
DateTimeZoneProviders.Tzdb["Asia/Colombo"]
DateTimeZoneProviders.Bcl["Sri Lanka Standard Time"]
```
## TimeZoneConverter Examples
```csharp
string ianaId = TZConvert.WindowsToIana("Sri Lanka Standard Time");
string windowsId = TZConvert.IanaToWindows("Asia/Colombo");
TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Asia/Colombo");
```
## Programmatic Discovery
```csharp
foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
{
Console.WriteLine($"ID: {tz.Id} | Display: {tz.DisplayName}");
}
```