I'm attempting to build a logical set of time zone records representing a 5-year period where the stop and start date/time matches to what a person perceives as the time change moment daylight savings is observed. For example, in America/New_York id, I want:
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
I am able to build these records, but I see no other way than to start prior to 1912 when dst went in effect and track the End Instant for all zone intervals. I don't see a way to determine the offset in effect when an Interval record period begins other than the previous Interval record. Is there a way to do this using just a single ZoneInterval record?
Question noted in code:
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();
}
}
}