Как я могу получить Noda Time в Xamarin, чтобы вернуть "America/Los_Angeles"?

Я использую Noda Time, чтобы получить местный часовой пояс на устройстве в формах Xamarin. Мы используем код ниже, чтобы вернуть его.

NodaTime.DateTimeZoneProviders.Tzdb.GetSystemDefault().Id;

Однако, если вы установите местоположение телефона в любом месте на западном побережье США, он вернет "Америка / Тихуана". Это вызывает у нас проблему, которая будет решена, если она вернет что-то вроде "America/Los_Angeles" (это действительный идентификатор), когда для вашего местоположения установлено значение LA

Какие-нибудь мысли? Как я могу заставить библиотеку возвращать более точный идентификатор?

1 ответ

Решение

На некоторых платформах и в зависимости от используемой версии Noda Time идентификатор системного часового пояса недоступен, поэтому мы должны догадаться. Мы делаем это на основе переходов часовых поясов с 1 января текущего года на 1 января через 5 лет. (Это единственное место, где системные часы используются в рабочем коде.)

Мы спрашиваем TimeZoneInfo для его идеи смещения UTC во множестве различных моментов в пределах того интервала (каждый момент, когда у любой из часовых поясов IANA есть переход). Затем мы "забиваем" каждый часовой пояс IANA против TimeZoneInfo значения, чтобы увидеть, какая доля моментов возвращает одинаковое смещение UTC. Мы возвращаем идентификатор часового пояса с наибольшим количеством совпадений, если оно составляет не менее 70% из них.

В вашем случае и Америка / Тихуана, и Америка / Лос-Анджелес имеют одинаковые переходы за 5 лет 2018-2022 гг. В формате tzvalidate:

2018-03-11 10:00:00Z -07:00:00 daylight PDT
2018-11-04 09:00:00Z -08:00:00 standard PST
2019-03-10 10:00:00Z -07:00:00 daylight PDT
2019-11-03 09:00:00Z -08:00:00 standard PST
2020-03-08 10:00:00Z -07:00:00 daylight PDT
2020-11-01 09:00:00Z -08:00:00 standard PST
2021-03-14 10:00:00Z -07:00:00 daylight PDT
2021-11-07 09:00:00Z -08:00:00 standard PST
2022-03-13 10:00:00Z -07:00:00 daylight PDT
2022-11-06 09:00:00Z -08:00:00 standard PST

Noda Time не может различить два часовых пояса за этот 5-летний период, поэтому он возвращает тот, который был найден первым. (Неизвестно, какой из них он найдет первым - это то, что мы могли бы исправить, упорядочив часовые пояса по ID. Я напишу для этого проблему.)

Я надеюсь, что это помогает объяснить то, что вы видите - это не решает проблему, но показывает, что весь код (ваш и Noda Time) ведет себя разумно, в контексте ограниченной доступной информации.

Как предлагается в комментариях, вы можете использовать специальный код платформы для решения этой проблемы: .Name собственностью Foundation.NSTimeZone.LocalTimeZone на iOS и, возможно, .ID собственностью Java.Util.TimeZone.Default на андроид.

Эту проблему можно исправить так:

Например: 1. В App.xaml.cs

public partial class App : Application
{

   public static string timeZoneName { get; set; } = string.Empty;

    public App()
    {
        var timezone = string.Empty;
        if (Device.RuntimePlatform == Device.Android)
        {
                timezone = TimeZoneInfo.Local.DisplayName;
        }
        else if (Device.RuntimePlatform == Device.iOS)
        {
                timezone = App.timeZoneName;
        }
    }

}

2. AppDelegate.cs

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
     global::Xamarin.Forms.Forms.Init();
     App.timeZoneName = NSTimeZone.LocalTimeZone.Name;
     LoadApplication(new App());
     return base.FinishedLaunching(app, options);
}
  1. В ViewModel или любом классе

var timezone = App.timeZoneName;

Другие вопросы по тегам