Получите ZoneId, используя название страны, дату и время и смещение часового пояса в c# - PullRequest
1 голос
/ 01 апреля 2020

У меня есть дата и время (гггг-ММ-ддТЧч: мм: сс), значение смещения часового пояса (K т.е. - / + ЧЧ: мм) и название страны, и я хочу получить ZoneId в формате IANA из этих значений в моем веб-API.

Например:

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

для значений

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

Я должен получить

zoneId = "Pacific/Auckland"

и для значений

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

Я должен получить (из-за летнего времени)

zoneId = "Pacific/Auckland"

В настоящее время, используя NodaTime, я смог получить все идентификаторы зоны для конкретной страны, но не смог выяснить, как их фильтровать, основываясь на значениях даты и времени и смещений. .

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

Может ли кто-нибудь помочь мне в этом?

Примечание. В случае, если для данного смещения допускается использование нескольких идентификаторов зон, будет использоваться первое значение.

1 Ответ

1 голос
/ 01 апреля 2020

Я немного изменю сигнатуру вашего метода и покажу, как вы можете получить список всех возможных идентификаторов часовых поясов для данной страны, где смещение соответствует определенной локальной дате и времени:

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();
}

Вот небольшой тестовый метод, чтобы проиллюстрировать вызовы со строковыми входами и дампом на выход:

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();
}

В ваших первых примерах приведен единственный ожидаемый результат:

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

Как и ваш второй пример:

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

Но посмотрите, что здесь происходит:

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

Важно отметить, что есть записи для и восточное время и центральное время в результатах, основными из которых являются America/New_York и America/Chicago. Как это может быть?

Нет, это не ошибка. Вы можете проверить это здесь . В США переход на летнее время не происходит одновременно. Он идет один часовой пояс с востока на запад. Поэтому, когда запасной переход происходит в восточном часовом поясе (2:00 по восточному поясному времени, который становится 1:00 по восточному поясному времени), центральный часовой пояс все еще находится в 1:00 по восточному поясному времени. Это не переходит на другой час. Другими словами, это 1:00 со смещением UT C -5 в EST и CDT одновременно .

Если вас это не волнует, обязательно Вы можете просто сделать .FirstOrDefault() (по умолчанию это строка null, когда нет совпадений). Но вы действительно можете встретить крайние случаи, подобные этому.

...