如何使用矩时区与C#TimeZoneInfo进行映射?

时间:2019-01-04 12:43:39

标签: javascript datetime timezone moment-timezone

我需要幻想在用户所选择的时区工作。服务器和客户端代码坚持使用JavaScript日期的问题。因此,为了达到要求,我已经将utc手动映射到客户端的日期:

dateToServer(date) {
    const momentDate = moment(date);
    let serverDate = null;
    if (momentDate.isValid() && date) {
        const browserUtcOffset = momentDate.utcOffset();
        serverDate = momentDate
            .utc()
            .subtract(this.clientTimeZoneOffset, 'minutes')
            .add(browserUtcOffset, 'minutes')
            .toDate();
    }
    return serverDate;
}


dateToClient(date) {
    const momentDate = moment(date);
    let uiDate = null;
    if (momentDate.isValid() && date) {
        const browserUtcOffset = momentDate.utcOffset();
        uiDate = momentDate
            .utc()
            .subtract(browserUtcOffset, 'minutes')
            .add(this.clientTimeZoneOffset, 'minutes')
            .toDate();
    }
    return uiDate;
}

我要添加/减去browserUtcOffset,因为当日期在服务器和客户端之间时,浏览器会自动添加/减去。

它运行良好,但是此解决方案缺少对DST的处理。我想检查DST在该日期是否有效,然后根据需要添加DST偏移量。

这里有C#代码,可以做到这一点:

        string timeZone = "Central Standard Time";
        TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
        DateTime date = new DateTime(2011, 3, 14);
        Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); // -360
        Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); // -300
        Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); // true
        Console.WriteLine(timeZoneInfo.DaylightName);
        Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);

我在momentjs中发现了isDST,当我将Windows本地时区设置为CST并在浏览器控制台中检查了moment([2011, 2, 14]).isDST();时,我看到了。 isDST的显示方式取决于浏览器的本地时间。

下一步,请尝试像我在C#中所做的那样使用moment-timezone来执行某项操作。不幸的是,我不知道如何实现这一目标。作为起点,我遇到的第一个问题是:UTC timeBase offset(-360 in C# sample)timezone name: Central Standard Time,但是时刻-时区中的时区不同。

moment.tz.names().map(name => moment.tz.zone(name)).filter(zone => zone.abbrs.find(abbr => abbr === 'CST') != null)此代码返回68个时区。 enter image description here

为什么每个人都有很多修道院?直到什么意思?我要检查的时间是否为所选时区的UTC时间,即“中央标准时间”,即夏令时处于活动状态。再看一次C#示例:)

        string timeZone = "Central Standard Time";
        TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
        DateTime date = new DateTime(2011, 3, 14);
        Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); //-360
        Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); //-300
        Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); //true, I have checked is daylightsavingtime for the date
        Console.WriteLine(timeZoneInfo.DaylightName);
        Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);

该应用程序已在angularjs 1.6.3上编写。

2 个答案:

答案 0 :(得分:1)

几件事:

  • JavaScript中的Date对象跟踪基于UTC的特定时间点。您可以通过调用.valueOf().getTime()看到该时间戳。只有某些函数和构造函数参数才能与本地时区一起使用(例如.toString())。该本地时区在调用函数时应用。一个人不能替代另一个时区(传递给toLocaleString的选项对象除外)。因此,Date对象 无法从一个时区转换为另一个时区。因为您的两个函数都接受一个Date对象并返回一个Date对象,所以您在中间所做的就是选择另一个时间点。这在使用Moment的addsubtract方法的函数中也可以看到。他们操纵表示的时间点-他们不更改时区。

  • 通过传递this.clientTimeZoneOffset,您似乎已将时区与时区偏移量混为一谈。这些是单独的概念,因为时区可能会由于DST的影响而经历多个不同的偏移,也可能由于历史发生的标准时间的变化而发生偏移。另请参见the timezone tag wiki中的“时区!=偏移”。仅传递客户的偏移量是没有用的,因为该偏移量仅适用于单个时间点。您不能将其用于时区转换,因为它无法告诉您其他时间点使用的偏移量。

  • 相反,传递时区标识符。在.NET中的Windows上,它们看起来像"Central Standard Time"(尽管有名称,但代表标准时间和夏令时),在JavaScript和大多数其他操作系统中,使用IANA时区名称。它们看起来像"America/Chicago"timezone tag wiki中也对此进行了介绍。

  • 如果您的.NET代码使用Windows标识符,则可以使用我的TimeZoneConverter库将Windows从Windows转换为IANA,然后将该字符串作为时区发送到浏览器。

    < / li>
  • 使用Moment-Timezone,您可以像这样简单地检查DST:

    moment.tz([2011, 2, 14], 'America/Chicago').isDST()
    

    同样,您应该使用IANA time zone IDs,如果您在服务器端使用Windows时区,则TimeZoneConverter可以提供它们。

  • 可以考虑通过其TZDB时区提供程序在服务器端使用Noda Time。这样一来,您就可以在两侧使用IANA时区。在Linux或Mac OSX的.NET Core上运行时,TimeZoneInfo也是如此。

  • 使用CST搜索缩写词时看到这么多条目的原因是时区缩写词模棱两可。您可能指的是美国中部标准时间,但您也可能指的是古巴标准时间或中国标准时间,或其他使用该缩写的其他地方。

  • 关于untils数组,通常不需要担心。 Moment-Timezone是内部数据的一部分,用于为选择时区偏移量和缩写选择正确的时间点。

  • 您最后说的是一些不同的内容:

      

    我要检查的是所选时区的UTC时间是否为“中央标准时间”,即夏令时是否处于活动状态。

    我之前给出的示例假定您从该时区的本地时间开始。如果您是从UTC时间开始的,则如下所示:

    moment.utc([2011, 2, 14]).tz('America/Chicago').isDST()
    

    当然,您可以在传递数组的地方传递其他各种受支持的输入。 Moment docs可以为您提供可用的选项。

答案 1 :(得分:0)

请检查answer of Matt Johnson for this question。对我来说非常有用。

词汇

  • UI日期-用户在html日期输入上看到的日期 选择日期。 UI日期是本地js日期。
  • 服务器日期-存储在 数据库。
  • 时区ID-字符串,标识时区。对于Windows(.net),IANA(javascript)和Rails约定,它们是不同的。

问题

主要问题当某人登录位于时区+3,但其帐户设置为-6的PC时。 angularjs和kendo在项目上使用,并且kendo时间仅适用于本机js日期。并且本机js日期始终在浏览器时区中,对于本示例来说为+3。但是我应该在-6中设置类似的东西。 F.e.例如,用户选择的时间为07:00,UTC为04:00(07:00-3),但是对于他的帐户,时区为-6,utc应该为13:00(07:00 + 6)。当我们将本地js日期(UI日期)转换为UTC并向后转换时,此对话会自动应用。因此,决定计算服务器上的偏移量并摆脱浏览器时区偏移量:utcTime = UItime + browserOffset - clientTimezoneBaseOffset - daylightSavingTimeOffset。 但是,当需要恢复UI时间时会有问题,例如今天是06.06.2014,并且DST在该日期是正确的,但是当我们从服务器获取03.03.2014日期时,我们不知道DST是否在2014年03月03日处于活动状态。

答案

在服务器上的项目.net中,因此在数据库中存储了窗口的时区ID。我以另一种方式完成了该任务:依靠服务器DST来确定日期范围,该范围是从服务器上的最小日期开始,然后保存在本地存储中的客户端上。我喜欢这种情况 服务器是事实的来源之一,因此可以在任何客户端上执行计算而无需操纵时区。无需在IANA,Windows和Rails时区之间进行转换。但也有一个问题:需要预先计算介于DateTime.MinValueDateTime.MaxValue范围内的DST,目前为了加快速度,我正在计算介于01.01.2000DateTime.Now范围内的DST-将值从数据库转换为UI时就足够了,因为在数据库中,最小日期是2008年,但是对于html输入来说还不够,因为用户可以选择大于DateTime.Now且小于{{1}的值}。为了解决这个问题,我正在计划使用TimeZoneConverter发送给客户端01.01.2000,并且对于提供的日期(UI或服务器)不在IANATimezoneId范围内的情况, [01.01.2000, DateTime.Now]

这是服务器端的新代码

moment.utc(date).tz(IANATimezoneId).isDST()

这是在客户端修改的dateToServer,dateToClient

    private class DaylightSavingTimeDescriptor
    {
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public bool IsDaylightSavingTime { get; set; }
    }

    private string GetDaylightSavingTimeDescriptorsJson(string timeZone)
    {
        string daylightSaveingTimeDescriptorsJson = String.Empty;
        if(timeZone != null)
        {
            List<DaylightSavingTimeDescriptor> dstList = new List<DaylightSavingTimeDescriptor>();
            TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
            DateTime startDate = new DateTime(2000, 1, 1);
            DateTime dateIterator = startDate;
            bool isDST = timeZoneInfo.IsDaylightSavingTime(startDate);
            while (dateIterator < DateTime.Now)
            {
                bool currDST = timeZoneInfo.IsDaylightSavingTime(dateIterator);
                if (isDST != currDST)
                {
                    dstList.Add(new DaylightSavingTimeDescriptor()
                    {
                        EndTime = dateIterator.AddDays(-1),
                        IsDaylightSavingTime = isDST,
                        StartTime = startDate
                    });
                    startDate = dateIterator;
                    isDST = currDST;
                }
                dateIterator = dateIterator.AddDays(1);
            }

            daylightSaveingTimeDescriptorsJson = Newtonsoft.Json.JsonConvert.SerializeObject(dstList);
        }
        return daylightSaveingTimeDescriptorsJson;
    }

PS

我想摆脱偏移量,并在客户端上使用moment-timezone,在服务器上使用timezone converter,但是如果客户要求,那将是稍后的时间,因为我以前从未尝试过,不确定它是否会正常工作,并且当前的解决方案是否正常。偏移也将用于dateinputs组件(angularjs),因为它们使用的是kendo-dateinput,其ng-model是浏览器本地时间中的JS Date,但是我提供了另一个时区,因此需要在组件内部进行转换。

从我的角度来看,PPS是最佳的解决方案,如果timezone convertermoment-timezone将按预期工作

无论如何,整个应用程序都在运行JS日期,因此,当莫斯科(美国)用户在kendo输入中选择日期时间时,或查看setup kendo-scheduler,或在kendo表中显示日期时,我需要用偏移量进行操作。但是,我计划不直接传递客户端偏移,而是计划借助timezone converter从服务器传递IANA时区ID,并直接从moment-timezone获取我需要的偏移。