Created
May 17, 2018 01:28
-
-
Save kg/b93c022f2d48cae0665068a6f55b915c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length) | |
{ | |
//Reading the header. 4 bytes for magic, 16 are reserved | |
int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20); | |
int ttisstdcnt = ReadBigEndianInt32 (buffer, 24); | |
int leapcnt = ReadBigEndianInt32 (buffer, 28); | |
int timecnt = ReadBigEndianInt32 (buffer, 32); | |
int typecnt = ReadBigEndianInt32 (buffer, 36); | |
int charcnt = ReadBigEndianInt32 (buffer, 40); | |
if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt) | |
throw new InvalidTimeZoneException (); | |
Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt); | |
Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations); | |
List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types); | |
if (time_types.Count == 0) | |
throw new InvalidTimeZoneException (); | |
if (time_types.Count == 1 && time_types[0].IsDst) | |
throw new InvalidTimeZoneException (); | |
TimeSpan baseUtcOffset = new TimeSpan (0); | |
TimeSpan dstDelta = new TimeSpan (0); | |
string standardDisplayName = null; | |
string daylightDisplayName = null; | |
bool dst_observed = false; | |
DateTime dst_start = DateTime.MinValue; | |
List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> (); | |
bool storeTransition = false; | |
for (int i = 0; i < transitions.Count; i++) { | |
var pair = transitions [i]; | |
DateTime ttime = pair.Key; | |
TimeType ttype = pair.Value; | |
if (!ttype.IsDst) { | |
if (standardDisplayName != ttype.Name) | |
standardDisplayName = ttype.Name; | |
if (baseUtcOffset.TotalSeconds != ttype.Offset) { | |
baseUtcOffset = new TimeSpan (0, 0, ttype.Offset); | |
if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions. | |
storeTransition = true; | |
adjustmentRules = new List<AdjustmentRule> (); | |
dst_observed = false; | |
} | |
if (dst_observed) { | |
//FIXME: check additional fields for this: | |
//most of the transitions are expressed in GMT | |
dst_start += baseUtcOffset; | |
DateTime dst_end = ttime + baseUtcOffset + dstDelta; | |
//some weird timezone (America/Phoenix) have end dates on Jan 1st | |
if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year) | |
dst_end -= new TimeSpan (24, 0, 0); | |
/* | |
* AdjustmentRule specifies a DST period that starts and ends within a year. | |
* When we have a DST period longer than a year, the generated AdjustmentRule may not be usable. | |
* Thus we fallback to the transitions. | |
*/ | |
if (dst_start.AddYears (1) < dst_end) | |
storeTransition = true; | |
DateTime dateStart, dateEnd; | |
if (dst_start.Month < 7) | |
dateStart = new DateTime (dst_start.Year, 1, 1); | |
else | |
dateStart = new DateTime (dst_start.Year, 7, 1); | |
if (dst_end.Month >= 7) | |
dateEnd = new DateTime (dst_end.Year, 12, 31); | |
else | |
dateEnd = new DateTime (dst_end.Year, 6, 30); | |
TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day); | |
TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day); | |
if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946 | |
adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end)); | |
} | |
dst_observed = false; | |
} else { | |
if (daylightDisplayName != ttype.Name) | |
daylightDisplayName = ttype.Name; | |
if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) { | |
// Round to nearest minute, since it's not possible to create an adjustment rule | |
// with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.") | |
// This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916. | |
dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset; | |
if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0) | |
dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f)); | |
} | |
dst_start = ttime; | |
dst_observed = true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment