计算DST转换边界处的持续时间(向前/向后弹回的量) [英] Calculate duration (amount to spring forward/fall back) at DST transitions boundaries
问题描述
通过这个很好的答案,我可以确定夏令时的转换日期: https://stackoverflow.com/a/24378695/1159939
From this great answer, I am able to determine daylight saving transitions dates: https://stackoverflow.com/a/24378695/1159939
除了这些日期外,我还需要知道时钟是要增加还是减少以及时钟量是多少(总是一个小时?).
In addition of those dates, I need to know if the clock is going to increase or decrease and by what amount (is it always an hour?).
例如,在美国/太平洋时区,当本地时钟到达2020-03-08T02:00:00时,我需要以某种方式获取值+ 1h.当时钟到达2020-11-01T02:00:00时,我需要获取值-1h.
For example, in time zone US/Pacific, when the local clock reaches 2020-03-08T02:00:00, I need to somehow get value +1h. When the clock reaches 2020-11-01T02:00:00, I need to get value -1h.
在NodaTime中,有一个Offset.Savings值是+0或+1.现在确定如何使用它.
In NodaTime, there is a Offset.Savings value that is +0 or +1. Now sure how I can use that.
-更新1:我正在建立一个调度程序.我需要让用户更好地控制在节省1小时的时间内执行预定作业时如何执行作业.
--Update1: I am building a scheduler. I need to give the user more control over how jobs are executed when a scheduled job is to occur during the 1-hour savings period.
我正在考虑进行以下设置,以便用户可以选择:
I am thinking for having the following settings the user can select:
[x]时钟向前跳时,运行丢失的作业.
[x] Run missed jobs when clocks spring forward.
[x]时钟回落时重新运行作业.
[x] Re-run jobs when clocks fall back.
例如,假设一项工作计划在2020-03-08T02:15:00美国/太平洋地区运行.该本地时间不存在.如果用户勾选时钟向前运行时运行错过的作业",则显示为否".复选框,该作业将在凌晨3:15执行,否则,该作业将被跳过.
For example, let's say a job is scheduled to run at 2020-03-08T02:15:00 US/Pacific. This local time does not exist. If the user ticks off the "Run missed jobs when clocks spring forward" checkbox, the job will be executed at 3:15AM, otherwise, the job will be skipped.
例如,假设一个作业计划在2020-11-01T01:45:00美国/太平洋地区运行.此本地时间将发生两次.如果用户勾选时钟回落时重新运行作业",则该作业将被执行两次,否则将被执行一次.
For example, let's say a job is scheduled to run at 2020-11-01T01:45:00 US/Pacific. This local time will occur twice. If the user ticks off the "Re-run jobs when clocks fall back", the job will be executed twice, otherwise, it will be executed once.
为了进行上述计算,我需要知道我从前面提到的帖子中获得的夏令时转换日期.我还需要知道时钟将改变哪个方向以及改变多少时间(例如:1h).
In order to make the calculations above, I need to know the day light saving transition dates which I got from the post mentioned previously. I also need to know which direction the clocks will change and by how much (e.g.: 1h).
-Update2:
经过深思熟虑之后,我认为我需要一个包含以下数据的时区转换列表:
After giving it more thought, I think I need a list of time zone transitions that contains the following data:
2020-03-08 02:00:00 US/Eastern | 2020-03-08 07:00:00 UAT (01:00:00)
2020-11-01 02:00:00 US/Eastern | 2020-11-01 06:00:00 UAT (-01:00:00)
下面是我用来生成此数据的代码.不知道这是否在所有情况下都有效.为了计算时间变化,我使用下一个区域间隔的开始与当前区域间隔的结束之间的差.
Below is the code that I am using to generate this data. Not sure if this will work in all cases. In order to calculate time change, I am using the difference between the start of the next zone interval and the end of the current zone interval.
using NodaTime;
using System;
using System.Collections.Generic;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
string timeZoneId = "US/Eastern";
DateTimeZone? timeZone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(timeZoneId);
if (timeZone is null)
throw new Exception($"Cannot find time zone '{timeZoneId}'.");
int year = 2020;
var daylightSavingTransitions = GetDaylightSavingTransitions(timeZone, year);
foreach (var daylightSavingTransition in daylightSavingTransitions)
{
Console.WriteLine(daylightSavingTransition);
}
/// <summary>
/// Get points in time when a daylight saving time transitions occur.
/// </summary>
/// <param name="timeZone">Time zone of the local clock.</param>
/// <param name="year">The year to find transitions.</param>
/// <returns></returns>
static IEnumerable<DaylightSavingTransition> GetDaylightSavingTransitions(DateTimeZone timeZone, int year)
{
var yearStart = new LocalDateTime(year, 1, 1, 0, 0).InZoneLeniently(timeZone).ToInstant();
var yearEnd = new LocalDateTime(year + 1, 1, 1, 0, 0).InZoneLeniently(timeZone).ToInstant();
LinkedList<NodaTime.TimeZones.ZoneInterval> zoneIntervals = new LinkedList<NodaTime.TimeZones.ZoneInterval>(timeZone.GetZoneIntervals(yearStart, yearEnd));
LinkedListNode<NodaTime.TimeZones.ZoneInterval>? currentNode = zoneIntervals.First;
while (currentNode is { })
{
if (currentNode.Next is null)
break;
//Time change is the difference between the start of the next zone interval and the end of the current zone interval.
Period timeChangePeriod = currentNode.Next.Value.IsoLocalStart - currentNode.Value.IsoLocalEnd;
TimeSpan timeChange = new TimeSpan(Convert.ToInt32(timeChangePeriod.Hours), Convert.ToInt32(timeChangePeriod.Minutes), Convert.ToInt32(timeChangePeriod.Seconds));
DaylightSavingTransition daylightSavingTransition = new DaylightSavingTransition(timeZone.Id, currentNode.Value.IsoLocalEnd.ToDateTimeUnspecified(), currentNode.Value.End.ToDateTimeUtc(), timeChange);
yield return daylightSavingTransition;
currentNode = currentNode.Next;
}
}
}
}
public class DaylightSavingTransition
{
public DaylightSavingTransition(string timeZoneId, DateTime transitionLocalDate, DateTime transitionUtcDate, TimeSpan timeChange)
{
TimeZoneId = timeZoneId;
TransitionLocalDate = DateTime.SpecifyKind(transitionLocalDate, DateTimeKind.Unspecified);
TransitionUtcDate = DateTime.SpecifyKind(transitionUtcDate, DateTimeKind.Utc);
TimeChange = timeChange;
}
public string TimeZoneId { get; }
public DateTime TransitionLocalDate { get; }
public DateTime TransitionUtcDate { get; }
public TimeSpan TimeChange { get; }
/// <summary>
/// For fall back transition, used to determine if date is in the duplicated time period.
/// </summary>
/// <param name="utcDateTime">Point in time to test if it is inside the repeating time period.</param>
public bool IsRepeatingDateTime(DateTime utcDateTime)
{
if (utcDateTime >= TransitionUtcDate && utcDateTime < TransitionUtcDate.Add(TimeChange.Duration()))
{
return true;
}
else
{
return false;
}
}
public override string ToString()
{
return $"{TransitionLocalDate.ToString("yyyy-MM-dd HH:mm:ss")} {TimeZoneId} | {TransitionUtcDate.ToString("yyyy-MM-dd HH:mm:ss")} UAT ({TimeChange})";
}
}
}
推荐答案
听起来您真正想要的是 ZoneLocalMapping
其中告诉您如何将本地日期/时间映射到指定的时区.重要的属性是:
It sounds like what you're really looking for is DateTimeZone.MapLocal(LocalDateTime)
. That returns a ZoneLocalMapping
which tells you how a local date/time is mapped into the specified time zone. The important properties are:
-
计数
- 如果跳过日期/时间,则为0
- 1(如果已明确映射)
- 2如果映射不明确
ZoneInterval
包含一个WallOffset
,它是该区域间隔内的整个UTC偏移量.我强烈建议使用该选项,而不是使用Savings
,以应对没有夏令时的过渡(例如,如果某个区域的标准时间发生了变化).ZoneInterval
contains aWallOffset
which is the overall UTC offset during that zone interval. I'd strongly recommend using that rather thanSavings
, in order to cope with transitions which aren't daylight saving transitions (e.g. if the standard time for a zone changes).您应该能够使用该信息来确定何时运行事物.
You should be able to use that information to determine when to run things.
您还可以使用(Local.Recode,DateTime,LocalTimeZoneLocalMappingResolver) ,您将在其中基于用户选择的内容构建自己的解析器(用于处理跳过/模糊时间).
You could also use
DateTimeZone.ResolveLocal(LocalDateTime, ZoneLocalMappingResolver)
where you'd build your own resolver (for handling skipped/ambiguous times) based on what the user has selected.这篇关于计算DST转换边界处的持续时间(向前/向后弹回的量)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!