简体   繁体   中英

Get ZoneId using country name, datetime and timezone offset values in c#

I have datetime(yyyy-MM-ddTHH:mm:ss), timezone offset value (K ie -/+HH:mm) and country name and want to fetch IANA format ZoneId from these values in my web API.

Eg :

public static string FetchZoneID(string dateTime, string offset, string countryName)
{        
//fetch zoneID    
return zoneId;
}

for values

dateTime = "2020-02-06T06:11:01", offset = "+13:00", countryName = "New Zealand"

I should get

zoneId = "Pacific/Auckland"

and for values

dateTime = "2020-05-06T06:11:01", offset = "+12:00", countryName = "New Zealand"

I should get (due to DST)

zoneId = "Pacific/Auckland"

Currently using NodaTime, I was able to fetch all zoneIds for a particular country, but unable to figure out on how to filter these based on datetime and offset values.

var zoneIds = TzdbDateTimeZoneSource.Default.ZoneLocations.Where(x => x.CountryName == countryName)
    .Select(x => x.ZoneId);

Can someone help me on this?

Note: In case of multiple zoneIds being eligible for the given offset, the first value would be used.

I'll change your method signature slightly, and show how you can a list of all possible time zone ids for a given country where the offset matches for a particular local date and time:

public static ICollection<string> FetchZoneIds(OffsetDateTime dateTime, string countryCode)
{
    return TzdbDateTimeZoneSource.Default.ZoneLocations
        .Where(x => x.CountryCode == countryCode)
        .Select(x => dateTime.InZone(DateTimeZoneProviders.Tzdb[x.ZoneId]))
        .Where(x => x.Offset == dateTime.Offset)
        .Select(x => x.Zone.Id)
        .ToList();
}

Here's a little test method to illustrate calling with string inputs and dump to the output:

private static void Test(string dtoString, string countryCode)
{
    Console.WriteLine($"{dtoString} ({countryCode})");

    var offsetDateTime = OffsetDateTimePattern.GeneralIso.Parse(dtoString).Value;
    var zoneIds = FetchZoneIds(offsetDateTime, countryCode);

    foreach (var zoneId in zoneIds)
    {
        Console.WriteLine(zoneId);
    }

    Console.WriteLine();
}

Your first examples gives the single expected result:

Test("2020-02-06T06:11:01+13:00", "NZ");
2020-02-06T06:11:01+13:00 (NZ)
Pacific/Auckland

So does your second example:

Test("2020-05-06T06:11:01+12:00", "NZ");
2020-05-06T06:11:01+12:00 (NZ)
Pacific/Auckland

But look what happens here:

Test("2020-11-01T01:00:00-05:00", "US");
2020-11-01T01:00:00-05:00 (US)
America/New_York
America/Detroit
America/Kentucky/Louisville
America/Kentucky/Monticello
America/Indiana/Indianapolis
America/Indiana/Vincennes
America/Indiana/Winamac
America/Indiana/Marengo
America/Indiana/Petersburg
America/Indiana/Vevay
America/Chicago
America/Indiana/Tell_City
America/Indiana/Knox
America/Menominee
America/North_Dakota/Center
America/North_Dakota/New_Salem
America/North_Dakota/Beulah

Importantly, note that there are entries for both Eastern Time and Central Time in the results, the main ones being America/New_York and America/Chicago . How can that be?

No, it's not a bug. You can verify it here . In the US, daylight saving time doesn't happen all at once. It goes one time zone at a time from the east to the west. So when the fall-back transition takes place in the Eastern time zone (2:00 EDT becoming 1:00 EST), the Central time zone is still at 1:00 EDT. It doesn't transition for another hour. In other words, it's 1:00 with a UTC-5 offset in both EST and CDT at the same time .

If you don't care about such things, sure you could just do a .FirstOrDefault() (the default being a null string when there is no match). But you may indeed encounter edge cases like this one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM