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

Nik*_*ita 2 javascript timezone datetime 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;
}
Run Code Online (Sandbox Code Playgroud)

我正在添加/减去 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);
Run Code Online (Sandbox Code Playgroud)

我在 momentjs 中找到了isDST,当我将 Windows 本地时区设置为 CST 并检查moment([2011, 2, 14]).isDST();浏览器控制台时,我看到了真实的情况。如何查看isDST取决于浏览器本地时间。

下一步尝试使用 moment-timezone 来做一些事情,就像我在 C# 中所做的那样。不幸的是我不明白如何实现这一点。作为起点的第一个问题是:UTC time, Base 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 个时区。 在此输入图像描述

为什么他们每个人都有很多缩写?直到什么意思?我只想检查所选时区的 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);
Run Code Online (Sandbox Code Playgroud)

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

Mat*_*int 5

一些东西:

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

  • 通过传递this.clientTimeZoneOffset,您似乎将时区与时区偏移量混为一谈。这些是不同的概念,因为时区可能会经历多个不同的偏移量,这是由于夏令时,也是由于历史上发生过的标准时间的变化。另请参阅时区标签 wiki中的“时区!= 偏移量” 。仅传递客户端的偏移量是没有用的,因为该偏移量仅适用于单个时间点。您不能将它用于时区转换,因为它不会告诉您有关其他时间点使用哪些偏移量的任何信息。

  • 相反,传递时区标识符。在 .NET 中的 Windows 上,这些看起来像"Central Standard Time"(尽管名称不同,但代表标准时间和夏令时),而在 JavaScript 和大多数其他操作系统中,使用 IANA 时区名称。他们看着像是"America/Chicago"时区标签 wiki中也涵盖了这一点。

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

  • 使用 Moment-Timezone,您可以简单地检查 DST,如下所示:

    moment.tz([2011, 2, 14], 'America/Chicago').isDST()
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • 当您搜索缩写时看到如此多的条目的原因CST是时区缩写不明确。您可能指的是美国中部标准时间,但也可能指的是古巴标准时间或中国标准时间,或其他使用该缩写的地方。

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

  • 你最后说的有些不同:

    我只想检查所选时区的 UTC 时间(即“中部标准时间”)是否启用夏令时。

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

    moment.utc([2011, 2, 14]).tz('America/Chicago').isDST()
    
    Run Code Online (Sandbox Code Playgroud)

    当然,您可以在我传递数组的地方传递各种其他支持的输入。Moment 文档可以帮助您找到可用的选项。