有什么办法可以找出C#中夏令时前后的时间范围?

时间:2020-10-12 12:07:43

标签: c# timezone utc dst timezone-offset

TimeZoneInfo类中有很多选项可用于找出当前服务器时间UTC偏移量(如果时间是否为夏时制等)。

但是有什么方法可以找出给定日期范围内的夏令时范围

所以基本上可以说,考虑夏令时的一年中任何服务器的时区偏移量是

Mar-Oct - UTC-4
Nov-Feb - UCT-5

如果我将范围传递为Jan-Jun,那么我正在寻找的输出就是这个

Jan-Feb   - UTC-5
Mar-Jun   - UTC-4

我尝试检查以下方法和属性,但无法以最佳方式获得所需的输出

TimeZoneInfo.Local.BaseUtcOffset.Hours

TimeZoneInfo.Local.IsDaylightSavingTime

1 个答案:

答案 0 :(得分:1)

我建议使用Noda Time解决此问题。 (当然,您可以使用TimeZoneInfo,但是处理调整规则很麻烦。)

声明这些导入:

using System;
using System.Linq;
using NodaTime;

定义一个函数来完成主要工作:

static (LocalDateTime StartDateTime, LocalDateTime UntilDateTime, Offset UtcOffset)[] 
    GetOffsetRanges(DateTimeZone tz, LocalDate startDate, LocalDate untilDate)
{
    // Get ZonedDateTime values representing the start of day of each date in the given time zone.
    ZonedDateTime startZonedDateTime = startDate.AtStartOfDayInZone(tz);
    ZonedDateTime untilZonedDateTime = untilDate.AtStartOfDayInZone(tz);

    // We'll also need the corresponding Instant of each value.
    Instant startInstant = startZonedDateTime.ToInstant();
    Instant untilInstant = untilZonedDateTime.ToInstant();

    // Get the ZoneInterval values, which tell us the range that an offset is in effect.
    return tz.GetZoneIntervals(startInstant, untilInstant).Select(interval =>
    {
        // Snap the values to the range we're working with.  (This is optional, you could just use the IsoLocalStart/End values directly.)
        LocalDateTime startDateTime = interval.Start >= startInstant ? interval.IsoLocalStart : startZonedDateTime.LocalDateTime;
        LocalDateTime untilDateTime = interval.End <= untilInstant ? interval.IsoLocalEnd : untilZonedDateTime.LocalDateTime;

        // Include the local date and time range, and the offset that's in effect over that range.
        return (startDateTime, untilDateTime, interval.WallOffset);

    }).ToArray();
}

使用如下功能:

// Define your input values
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
var startDate = new LocalDate(2020, 1, 1);
var untilDate = new LocalDate(2021, 1, 1);

// Call the function to get the ranges
var ranges = GetOffsetRanges(tz, startDate, untilDate);

// Example output
Console.WriteLine($"Offsets in {tz.Id} between {startDate:R} and {untilDate:R} are as follows:");
foreach (var range in ranges)
{
    Console.WriteLine($"{range.StartDateTime:s} - {range.UntilDateTime:s} : {range.UtcOffset:m}");
}

输出:

Offsets in America/New_York between 2020-01-01 and 2021-01-01 are as follows:
2020-01-01T00:00:00 - 2020-03-08T02:00:00 : -05:00
2020-03-08T03:00:00 - 2020-11-01T02:00:00 : -04:00
2020-11-01T01:00:00 - 2021-01-01T00:00:00 : -05:00

一些注意事项:

  • 我不建议您以问题中提到的方式简化输入或输出。说Mar-Oct不够精确。

  • 正如Panagiotis在问题注释中指出的那样,您需要确定要使用的时区。单偏置输入是不够的。我的示例显示了一个命名的IANA时区,但如果需要,您可以使用DateTimeZoneProviders.Tzdb.GetSystemDefault()

    来传递系统的本地时区。
  • 如果此操作将在服务器上运行,请不要使用系统的本地时区。通常,应将服务器的时区视为不相关

  • 在Web应用程序中,您可以使用Intl.DateTimeFormat().resolvedOptions().timeZone在JavaScript中获取用户的IANA时区标识符。您可以将其传递给服务器,并将其用作上述代码的输入。 (这适用于大多数现代网络浏览器,但不适用于所有较旧的网络浏览器。)