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

@@ -110,6 +110,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [documentation-writer](../skills/documentation-writer/SKILL.md) | Diátaxis Documentation Expert. An expert technical writer specializing in creating high-quality software documentation, guided by the principles and structure of the Diátaxis technical documentation authoring framework. | None | | [documentation-writer](../skills/documentation-writer/SKILL.md) | Diátaxis Documentation Expert. An expert technical writer specializing in creating high-quality software documentation, guided by the principles and structure of the Diátaxis technical documentation authoring framework. | None |
| [dotnet-best-practices](../skills/dotnet-best-practices/SKILL.md) | Ensure .NET/C# code meets best practices for the solution/project. | None | | [dotnet-best-practices](../skills/dotnet-best-practices/SKILL.md) | Ensure .NET/C# code meets best practices for the solution/project. | None |
| [dotnet-design-pattern-review](../skills/dotnet-design-pattern-review/SKILL.md) | Review the C#/.NET code for design pattern implementation and suggest improvements. | None | | [dotnet-design-pattern-review](../skills/dotnet-design-pattern-review/SKILL.md) | Review the C#/.NET code for design pattern implementation and suggest improvements. | None |
| [dotnet-timezone](../skills/dotnet-timezone/SKILL.md) | .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. | `references/code-patterns.md`<br />`references/timezone-index.md` |
| [dotnet-upgrade](../skills/dotnet-upgrade/SKILL.md) | Ready-to-use prompts for comprehensive .NET framework upgrade analysis and execution | None | | [dotnet-upgrade](../skills/dotnet-upgrade/SKILL.md) | Ready-to-use prompts for comprehensive .NET framework upgrade analysis and execution | None |
| [doublecheck](../skills/doublecheck/SKILL.md) | Three-layer verification pipeline for AI output. Extracts verifiable claims, finds supporting or contradicting sources via web search, runs adversarial review for hallucination patterns, and produces a structured verification report with source links for human review. | `assets/verification-report-template.md` | | [doublecheck](../skills/doublecheck/SKILL.md) | Three-layer verification pipeline for AI output. Extracts verifiable claims, finds supporting or contradicting sources via web search, runs adversarial review for hallucination patterns, and produces a structured verification report with source links for human review. | `assets/verification-report-template.md` |
| [editorconfig](../skills/editorconfig/SKILL.md) | Generates a comprehensive and best-practice-oriented .editorconfig file based on project analysis and user preferences. | None | | [editorconfig](../skills/editorconfig/SKILL.md) | Generates a comprehensive and best-practice-oriented .editorconfig file based on project analysis and user preferences. | None |

View File

@@ -0,0 +1,109 @@
---
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

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}");
}
```