Installation

Install with CLI Recommended
gh skills-hub install dotnet-timezone

Don't have the extension? Run gh extension install samueltauil/skills-hub first.

Download and extract to your repository:

.github/skills/dotnet-timezone/

Extract the ZIP to .github/skills/ in your repo. The folder name must match dotnet-timezone for Copilot to auto-discover it.

Skill Files (3)

SKILL.md 3.8 KB
---
name: dotnet-timezone
description: '.NET timezone handling guidance for C# applications. Use when working with TimeZoneInfo, DateTimeOffset, NodaTime, UTC conversion, daylight saving time, scheduling across timezones, cross-platform Windows/IANA timezone IDs, or when a .NET user needs the timezone for a city, address, region, or country and copy-paste-ready C# code.'
---

# .NET Timezone

Resolve timezone questions for .NET and C# code with production-safe guidance and copy-paste-ready snippets.

## Start With The Right Path

Identify the request type first:

- Address or location lookup
- Timezone ID lookup
- UTC/local conversion
- Cross-platform timezone compatibility
- Scheduling or DST handling
- API or persistence design

If the library is unclear, default to `TimeZoneConverter` for cross-platform work. If the scenario involves recurring schedules or strict DST rules, prefer `NodaTime`.

## Resolve Addresses And Locations

If the user provides an address, city, region, country, or document containing place names:

1. Extract each location from the input.
2. Read `references/timezone-index.md` for common Windows and IANA mappings.
3. If the exact location is not listed, infer the correct IANA zone from geography, then map it to the Windows ID.
4. Return both IDs and a ready-to-use C# example.

For each resolved location, provide:

```text
Location: <resolved place>
Windows ID: <windows id>
IANA ID: <iana id>
UTC offset: <standard offset and DST offset when relevant>
DST: <yes/no>
```

Then include a cross-platform snippet like:

```csharp
using TimeZoneConverter;

TimeZoneInfo tz = TZConvert.GetTimeZoneInfo("Asia/Colombo");
DateTime local = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
```

If multiple locations are present, include one block per location and then a combined multi-timezone snippet.

If a location is ambiguous, list the possible timezone matches and ask the user to choose the correct one.

## Look Up Timezone IDs

Use `references/timezone-index.md` for Windows to IANA mappings.

Always provide both formats:

- Windows ID for `TimeZoneInfo.FindSystemTimeZoneById()` on Windows
- IANA ID for Linux, containers, `NodaTime`, and `TimeZoneConverter`

## Generate Code

Use `references/code-patterns.md` and pick the smallest pattern that fits:

- Pattern 1: `TimeZoneInfo` for Windows-only code
- Pattern 2: `TimeZoneConverter` for cross-platform conversion
- Pattern 3: `NodaTime` for strict timezone arithmetic and DST-sensitive scheduling
- Pattern 4: `DateTimeOffset` for APIs and data transfer
- Pattern 5: ASP.NET Core persistence and presentation
- Pattern 6: recurring jobs and schedulers
- Pattern 7: ambiguous and invalid DST timestamps

Always include package guidance when recommending third-party libraries.

## Warn About Common Pitfalls

Mention the relevant warning when applicable:

- `TimeZoneInfo.FindSystemTimeZoneById()` is platform-specific for timezone IDs.
- Avoid storing `DateTime.Now` in databases; store UTC instead.
- Treat `DateTimeKind.Unspecified` as a bug risk unless it is deliberate input.
- DST transitions can skip or repeat local times.
- Azure Windows and Azure Linux environments may expect different timezone ID formats.

## Response Shape

For address and location requests:

1. Return the resolved timezone block for each location.
2. State the recommended implementation in one sentence.
3. Include a copy-paste-ready C# snippet.

For code and architecture requests:

1. State the recommended approach in one sentence.
2. Provide the timezone IDs if relevant.
3. Include the minimal working code snippet.
4. Mention the package requirement if needed.
5. Add one pitfall warning if it matters.

Keep responses concise and code-first.

## References

- `references/timezone-index.md`: common Windows and IANA timezone mappings
- `references/code-patterns.md`: ready-to-use .NET timezone patterns
references/
code-patterns.md 4.5 KB
# .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.
timezone-index.md 4.3 KB
# .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}");
}
```

License (MIT)

View full license text
MIT License

Copyright GitHub, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.