c# - 查找两个时区之间的所有时区偏移周期

标签 c# date timezone dst

我有代码可以为我提供目标时区和源时区之间的偏移量(以秒为单位),适用于两个日期之间的所有时间段。

以下代码将执行此操作:

// example start and end date
DateTime startDate = DateTime.SpecifyKind(DateTime.Now.Date, DateTimeKind.Unspecified);
DateTime endDate = startDate.AddYears(10);

// the timezones to use
var sourceZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var destinationZone = TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time");

// the periods and timezone offsets
var results = new List<Tuple<DateTime, DateTime, double>>();

// loop variables
DateTime currentDate = startDate;
DateTime periodStartDate = currentDate;

// the current period offset (in seconds)
double? currentOffset = null;

while (currentDate < endDate)
{
    DateTime destTime;

    // if the current time is invalid for the source timezone
    // then advance time until it is not invalid
    while (sourceZone.IsInvalidTime(currentDate))
        currentDate = currentDate.AddMinutes(30d);

    destTime = TimeZoneInfo.ConvertTime(currentDate, sourceZone, destinationZone);

    // calculate the offset for this iteration
    double iterationOffset = destTime.Subtract(currentDate).TotalSeconds;

    if (currentOffset == null)
        // the current offset is null so use the iteration offset
        currentOffset = iterationOffset;
    else if (iterationOffset != currentOffset.Value)
    {
        // the current offset doesn't equal the iteration offset
        // that means that a period has been identified

        // add the period to the list
        results.Add(Tuple.Create(periodStartDate, currentDate, currentOffset.Value));

        // the start of the period is the new current date
        periodStartDate = currentDate;

        // the current offset becomes the iteration offset
        currentOffset = iterationOffset;
    }

    // increment the day by 30 minutes
    currentDate = currentDate.AddMinutes(30d);
}

foreach (var item in results)
    Console.WriteLine("{0}\t{1}\t{2}", item.Item1, item.Item2, item.Item3);

结果:

╔═══════════════════════╦═══════════════════════╦════════╗
║      PeriodStart      ║       PeriodEnd       ║ Offset ║
╠═══════════════════════╬═══════════════════════╬════════╣
║ 7/13/2015 12:00:00 AM ║ 10/25/2015 1:00:00 AM ║ -25200 ║
║ 10/25/2015 1:00:00 AM ║ 11/1/2015 8:00:00 AM  ║ -21600 ║
║ 11/1/2015 8:00:00 AM  ║ 3/13/2016 9:00:00 AM  ║ -25200 ║
║ 3/13/2016 9:00:00 AM  ║ 3/27/2016 2:00:00 AM  ║ -21600 ║
║ 3/27/2016 2:00:00 AM  ║ 10/30/2016 1:00:00 AM ║ -25200 ║
║ 10/30/2016 1:00:00 AM ║ 11/6/2016 8:00:00 AM  ║ -21600 ║
║ 11/6/2016 8:00:00 AM  ║ 3/12/2017 9:00:00 AM  ║ -25200 ║
║ 3/12/2017 9:00:00 AM  ║ 3/26/2017 2:00:00 AM  ║ -21600 ║
║ 3/26/2017 2:00:00 AM  ║ 10/29/2017 1:00:00 AM ║ -25200 ║
║ 10/29/2017 1:00:00 AM ║ 11/5/2017 8:00:00 AM  ║ -21600 ║
║ 11/5/2017 8:00:00 AM  ║ 3/11/2018 9:00:00 AM  ║ -25200 ║
║          ...          ║          ...          ║   ...  ║
╚═══════════════════════╩═══════════════════════╩════════╝

虽然这看起来可行,但我知道这不是最有效的方法。我基本上坚持将其转换为一种方法,该方法不必使用硬编码时间增量来查找偏移量发生变化的所有这些时间段。

如有任何帮助,我们将不胜感激。

最佳答案

这是一个使用 Noda Time 的解决方案:

using System;
using System.Linq;
using NodaTime;

...

// Get some time zones.  You can use Tzdb or Bcl zones here.
DateTimeZone sourceZone = DateTimeZoneProviders.Bcl["GMT Standard Time"]; // London
DateTimeZone destinationZone = DateTimeZoneProviders.Bcl["Mountain Standard Time"]; // Denver

// Determine the period of time we're interested in evaluating.
// I'm taking today in the source time zone, up to 10 years in the future.
Instant now = SystemClock.Instance.Now;
Instant start = sourceZone.AtStartOfDay(now.InZone(sourceZone).Date).ToInstant();
Instant end = start.InZone(sourceZone).LocalDateTime.PlusYears(10).InZoneLeniently(sourceZone).ToInstant();

// Get the intervals for our each of the zones over these periods
var sourceIntervals = sourceZone.GetZoneIntervals(start, end);
var destinationIntervals = destinationZone.GetZoneIntervals(start, end);

// Find all of the instants we care about, including the start and end points,
// and all transitions from either zone in between
var instants = sourceIntervals.Union(destinationIntervals)
    .SelectMany(x => new[] {x.Start, x.End})
    .Union(new[] {start, end})
    .OrderBy(x => x).Distinct()
    .Where(x => x >= start && x < end)
    .ToArray();

// Loop through the instants
for (int i = 0; i < instants.Length -1; i++)
{
    // Get this instant and the next one
    Instant instant1 = instants[i];
    Instant instant2 = instants[i + 1];

    // convert each instant to the source zone
    ZonedDateTime zdt1 = instant1.InZone(sourceZone);
    ZonedDateTime zdt2 = instant2.InZone(sourceZone);

    // Get the offsets for instant1 in each zone 
    Offset sourceOffset = zdt1.Offset;
    Offset destOffset = destinationZone.GetUtcOffset(instant1);

    // Calc the difference between the offsets
    int deltaSeconds = (destOffset.Milliseconds - sourceOffset.Milliseconds)/1000;

    // Convert to the same types you had in your example (optional)
    DateTime dt1 = zdt1.ToDateTimeUnspecified();
    DateTime dt2 = zdt2.ToDateTimeUnspecified();

    // emit output
    Console.WriteLine("{0}\t{1}\t{2}", dt1, dt2, deltaSeconds);
}

请注意,输出与您自己的相同,除了第一次约会。当我运行它时,你的代码为 7/13/2015,而我的代码为 7/14/2015。这是因为您的代码有一个错误,因为您的开始日期不是基于源时区的今天,而是从本地时区的今天开始。

此外,我假设您希望所有输出都以 时区表示,因为这就是您的示例给出的。

此外,您可能需要考虑到源区域中的转换输出不是特别清晰。在后退过渡期间,您无法 输出表示两次中的哪一个,而在 Spring 前过渡期间,您不清楚差距 实际在哪里。 DateTimeOffset 输出在这方面会更加清晰。

关于c# - 查找两个时区之间的所有时区偏移周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31392039/

相关文章:

database - 我可以在数据库中使用 Unix 时间作为复合主键吗?

c# - WebAPI线程

c# - 当 InPlaceBitmapMetadataWriter.TrySave() 返回 false 时,如何向图像添加元数据?

ios - Swift - IOS - 限时功能

javascript - 如何为http请求设置 "Date" header

sql - 带时区查询的时间不符合预期

c# - 如何在用户控件中的两个控件之间绘制直线?

c# - 当我关闭启动窗体时,如何防止应用程序终止?

mysql - 从一个月mysql获取两个日期和最后一天之间的星期日日期

qt - 如何在 Qt 中获取所有时区列表?