野田时区间隔
NodaTime ZoneIntervals
我正在尝试构建一组逻辑时区记录,代表一个 5 年的时间段,其中停止和开始 date/time 与人们认为的时间变化时刻夏令时相匹配。比如在America/New_York id中,我要:
03/13/2016 2:00 am - 11/06/2016 01:59:59 am gmtOffset: -4:00
11/06/2016 2:00 am - 03/12/2017 01:59:59 am gmtOffset: -5:00
我能够建立这些记录,但除了在夏令时生效的 1912 年之前开始并跟踪所有区域间隔的结束时间外,我别无他法。当间隔记录周期开始时,除了先前的间隔记录之外,我没有看到一种方法来确定有效的偏移量。有没有办法只使用一个 ZoneInterval 记录来做到这一点?
代码中注明的问题:
private void processTZ()
{
IDateTimeZoneProvider provider = DateTimeZoneProviders.Tzdb;
using (StreamWriter sw = new StreamWriter(@"c:\tzOut.txt"))
{
foreach (string id in provider.Ids)
{
sw.WriteLine("Id: {0}", id);
DateTimeZone zone = provider[id];
// Time period I wish to create logical representation.
Instant instantValidityBegin = (new LocalDateTime(2014, 1, 1, 0, 0)).InZoneLeniently(zone).ToInstant();
Instant instantValidityEnd = (new LocalDateTime(2019, 12, 31, 23, 59, 59, 0)).InZoneLeniently(zone).ToInstant();
// Time period from zone intervals to iterate. The first year dst was observed was 1916.
Instant instantFetchYearBegin = (new LocalDateTime(1916, 1, 1, 0, 0)).InZoneLeniently(zone).ToInstant();
Instant instantFetchYearEnd = (new LocalDateTime(2019, 12, 31, 23, 59, 0)).InZoneLeniently(zone).ToInstant();
// Get the intervals
IEnumerable<NodaTime.TimeZones.ZoneInterval> intervals = zone.GetZoneIntervals(instantFetchYearBegin, instantFetchYearEnd);
// Determine number of intervals for this tzId.
int count = 0;
foreach (NodaTime.TimeZones.ZoneInterval zi in intervals)
count++;
bool singleEntry = (1 == count);
// myStart and myEnd are desired output period
// capture IsoLocalEnd of an interval to be used as the start date of program output (myStart). Don't display older than 1900.
DateTime myStart, myEnd, prevEnd = new DateTime(1900, 1, 1);
foreach (NodaTime.TimeZones.ZoneInterval zi in intervals)
{
if (singleEntry)
{
// No question here
}
else
{
// skip intervals for this tzId that represent a period beginning after time period I want.
if (instantValidityEnd < zi.Start)
break;
// Skip intervals ending prior to time period I want, but capture the IsoLocalEnd to be used for report output.
if (zi.End < instantValidityBegin)
{
prevEnd = (zi.End == Instant.MinValue) ? prevEnd = new DateTime(1900, 1, 1) : prevEnd = zi.IsoLocalEnd.ToDateTimeUnspecified(); ;
continue;
}
// ***Question*** Can this myStart value be determined using the current interval record instead of storing the previous interval's IsoLocalEnd??
myStart = prevEnd;
if (zi.End == Instant.MaxValue)
prevEnd = myEnd = new DateTime(9999, 12, 31);
else
{
prevEnd = zi.IsoLocalEnd.ToDateTimeUnspecified();
myEnd = prevEnd.Subtract(TimeSpan.FromSeconds(1)); // force period back 1 second for logical representation.
}
sw.WriteLine("Name: " + zi.Name);
sw.WriteLine("Multi Entry: {0}", zi.ToString());
sw.WriteLine("myStart: {0:G} myEnd: {1:G} gmtOffset: {2:G}", myStart, myEnd, zi.WallOffset.ToTimeSpan());
sw.WriteLine();
}
}
sw.WriteLine("------------------------------------------------------------------");
sw.WriteLine();
}
}
}
在任何给定的 ZoneInterval
记录上,IsoLocalStart
和 IsoLocalEnd
是相对于该特定间隔的,两者使用相同的 WallOffset
值。
如果您想获得实际生效的本地时间,您可以将 Start
和 End
时刻转换为特定时区。
zi.Start.InZone(zone)
zi.End.InZone(zone)
您会发现起始值的 LocalDateTime
与 IsoLocalStart
匹配,但结束值不匹配。到观察到该时刻时,它现在已被下一个间隔的偏移量覆盖。不要忘记这些是 半开 区间。 [inclusive-start, exclusive-end)
.
让我们回到您最初请求中的示例:
03/13/2016 2:00 am - 11/06/2016 01:59:59 am gmtOffset: -4:00
11/06/2016 2:00 am - 03/12/2017 01:59:59 am gmtOffset: -5:00
它实际上并不是那样工作的。三月,当地时间从 1:59:59 am
移动到 3:00:00 am
,跳过了一个多小时。所以第一行应该从 3:00 am
而不是 2:00 am
开始。在 11 月,当地时间从 01:59:59 am
移动到 1:00:00 am
重复一个小时。所以第二行应该从 1:00 am
而不是 2:00 am
开始。您的图表似乎试图使当地时间连续 - 但事实并非如此。
此外,您会发现按照惯例,许多人认为过渡时间是 2:00,而不是 1:59:59。将其视为接近 2:00。这就是 IsoLocalEnd
显示当前间隔偏移量的原因,您可以使用该字段来实现此目的。
此外,您可能想查看 how timeanddate.com 显示的内容。他们决定只显示结束值,这可能会使事情更清楚一些。
我正在尝试构建一组逻辑时区记录,代表一个 5 年的时间段,其中停止和开始 date/time 与人们认为的时间变化时刻夏令时相匹配。比如在America/New_York id中,我要:
03/13/2016 2:00 am - 11/06/2016 01:59:59 am gmtOffset: -4:00
11/06/2016 2:00 am - 03/12/2017 01:59:59 am gmtOffset: -5:00
我能够建立这些记录,但除了在夏令时生效的 1912 年之前开始并跟踪所有区域间隔的结束时间外,我别无他法。当间隔记录周期开始时,除了先前的间隔记录之外,我没有看到一种方法来确定有效的偏移量。有没有办法只使用一个 ZoneInterval 记录来做到这一点?
代码中注明的问题:
private void processTZ()
{
IDateTimeZoneProvider provider = DateTimeZoneProviders.Tzdb;
using (StreamWriter sw = new StreamWriter(@"c:\tzOut.txt"))
{
foreach (string id in provider.Ids)
{
sw.WriteLine("Id: {0}", id);
DateTimeZone zone = provider[id];
// Time period I wish to create logical representation.
Instant instantValidityBegin = (new LocalDateTime(2014, 1, 1, 0, 0)).InZoneLeniently(zone).ToInstant();
Instant instantValidityEnd = (new LocalDateTime(2019, 12, 31, 23, 59, 59, 0)).InZoneLeniently(zone).ToInstant();
// Time period from zone intervals to iterate. The first year dst was observed was 1916.
Instant instantFetchYearBegin = (new LocalDateTime(1916, 1, 1, 0, 0)).InZoneLeniently(zone).ToInstant();
Instant instantFetchYearEnd = (new LocalDateTime(2019, 12, 31, 23, 59, 0)).InZoneLeniently(zone).ToInstant();
// Get the intervals
IEnumerable<NodaTime.TimeZones.ZoneInterval> intervals = zone.GetZoneIntervals(instantFetchYearBegin, instantFetchYearEnd);
// Determine number of intervals for this tzId.
int count = 0;
foreach (NodaTime.TimeZones.ZoneInterval zi in intervals)
count++;
bool singleEntry = (1 == count);
// myStart and myEnd are desired output period
// capture IsoLocalEnd of an interval to be used as the start date of program output (myStart). Don't display older than 1900.
DateTime myStart, myEnd, prevEnd = new DateTime(1900, 1, 1);
foreach (NodaTime.TimeZones.ZoneInterval zi in intervals)
{
if (singleEntry)
{
// No question here
}
else
{
// skip intervals for this tzId that represent a period beginning after time period I want.
if (instantValidityEnd < zi.Start)
break;
// Skip intervals ending prior to time period I want, but capture the IsoLocalEnd to be used for report output.
if (zi.End < instantValidityBegin)
{
prevEnd = (zi.End == Instant.MinValue) ? prevEnd = new DateTime(1900, 1, 1) : prevEnd = zi.IsoLocalEnd.ToDateTimeUnspecified(); ;
continue;
}
// ***Question*** Can this myStart value be determined using the current interval record instead of storing the previous interval's IsoLocalEnd??
myStart = prevEnd;
if (zi.End == Instant.MaxValue)
prevEnd = myEnd = new DateTime(9999, 12, 31);
else
{
prevEnd = zi.IsoLocalEnd.ToDateTimeUnspecified();
myEnd = prevEnd.Subtract(TimeSpan.FromSeconds(1)); // force period back 1 second for logical representation.
}
sw.WriteLine("Name: " + zi.Name);
sw.WriteLine("Multi Entry: {0}", zi.ToString());
sw.WriteLine("myStart: {0:G} myEnd: {1:G} gmtOffset: {2:G}", myStart, myEnd, zi.WallOffset.ToTimeSpan());
sw.WriteLine();
}
}
sw.WriteLine("------------------------------------------------------------------");
sw.WriteLine();
}
}
}
在任何给定的 ZoneInterval
记录上,IsoLocalStart
和 IsoLocalEnd
是相对于该特定间隔的,两者使用相同的 WallOffset
值。
如果您想获得实际生效的本地时间,您可以将 Start
和 End
时刻转换为特定时区。
zi.Start.InZone(zone)
zi.End.InZone(zone)
您会发现起始值的 LocalDateTime
与 IsoLocalStart
匹配,但结束值不匹配。到观察到该时刻时,它现在已被下一个间隔的偏移量覆盖。不要忘记这些是 半开 区间。 [inclusive-start, exclusive-end)
.
让我们回到您最初请求中的示例:
03/13/2016 2:00 am - 11/06/2016 01:59:59 am gmtOffset: -4:00 11/06/2016 2:00 am - 03/12/2017 01:59:59 am gmtOffset: -5:00
它实际上并不是那样工作的。三月,当地时间从 1:59:59 am
移动到 3:00:00 am
,跳过了一个多小时。所以第一行应该从 3:00 am
而不是 2:00 am
开始。在 11 月,当地时间从 01:59:59 am
移动到 1:00:00 am
重复一个小时。所以第二行应该从 1:00 am
而不是 2:00 am
开始。您的图表似乎试图使当地时间连续 - 但事实并非如此。
此外,您会发现按照惯例,许多人认为过渡时间是 2:00,而不是 1:59:59。将其视为接近 2:00。这就是 IsoLocalEnd
显示当前间隔偏移量的原因,您可以使用该字段来实现此目的。
此外,您可能想查看 how timeanddate.com 显示的内容。他们决定只显示结束值,这可能会使事情更清楚一些。