////////////////////////////////////////////////////////////////////////// // DST Calculations ////////////////////////////////////////////////////////////////////////// /// <summary> /// Compute the daylight savings time offset (in seconds) /// for the specified parameters: /// - Rule: the rule for a given year /// - mon: month 0-11 /// - day: day 1-31 /// - weekday: 0-6 /// - time: seconds since midnight /// </summary> internal static int dstOffset(Rule rule, int year, int mon, int day, int time) { DstTime start = rule.dstStart; DstTime end = rule.dstEnd; if (start == null) { return(0); } int s = compare(rule, start, year, mon, day, time); int e = compare(rule, end, year, mon, day, time); // if end month comes earlier than start month, // then this is dst in southern hemisphere if (end.mon < start.mon) { if (e > 0 || s <= 0) { return(rule.dstOffset); } } else { if (s <= 0 && e > 0) { return(rule.dstOffset); } } return(0); }
/// <summary> /// Compare at time. /// </summary> static int compareAtTime(Rule rule, DstTime x, int time) { int atTime = x.atTime; // if universal time, then we need to move atTime back to // local time (we might cross into the previous day) if (x.atMode == 'u') { if (rule.offset + x.atTime < 0) { atTime = 24 * 60 * 60 + rule.offset + x.atTime; } else { atTime += rule.offset; } } if (atTime < time) { return(-1); } if (atTime > time) { return(+1); } return(0); }
/// <summary> /// Compare on day. /// 'd' 5 the fifth of the month /// 'l' lastSun the last Sunday in the month /// 'l' lastMon the last Monday in the month /// '>' Sun>=8 first Sunday on or after the eighth /// '<' Sun<=25 last Sunday on or before the 25th (not used) /// </summary> static int compareOnDay(Rule rule, DstTime x, int year, int mon, int day) { // universal atTime might push us into the previous day if (x.atMode == 'u' && rule.offset + x.atTime < 0) { ++day; } switch (x.onMode) { case (byte)'d': if (x.onDay < day) { return(-1); } if (x.onDay > day) { return(+1); } return(0); case (byte)'l': int last = DateTime.weekdayInMonth(year, mon, x.onWeekday, -1); if (last < day) { return(-1); } if (last > day) { return(+1); } return(0); case (byte)'>': int start = DateTime.weekdayInMonth(year, mon, x.onWeekday, 1); while (start < x.onDay) { start += 7; } if (start < day) { return(-1); } if (start > day) { return(+1); } return(0); default: throw new Exception("" + (char)x.onMode); } }
/// <summary> /// Compare month /// </summary> static int compareMonth(DstTime x, int mon) { if (x.mon < mon) { return(-1); } if (x.mon > mon) { return(+1); } return(0); }
/* * static void loadIndex() * { * DataReader reader = new DataReader(new BufferedStream(dbFile.OpenRead())); * try * { * // check magic "fantz 01" * long magic = reader.ReadLong(); * if (magic != 0x66616e747a203031L) * throw new IOException("Invalid magic 0x" + magic.ToString("X").ToLower()); * reader.ReadUTF(); * * // load the name/offset pairs and verify in sort order * int num = reader.ReadInt(); * indexNames = new string[num]; * indexTypes = new byte[num]; * indexOffsets = new int[num]; * for (int i=0; i<num; ++i) * { * indexNames[i] = reader.ReadUTF(); * indexTypes[i] = (byte)reader.ReadByte(); * indexOffsets[i] = reader.ReadInt(); * if (i != 0 && String.Compare(indexNames[i-1], indexNames[i], StringComparison.Ordinal) >= 0) * throw new IOException("Index not sorted"); * } * } * finally * { * reader.Close(); * } * } * * static TimeZone loadTimeZone(string name) * { * // find index, which maps the file offset * int ix = Array.BinarySearch(indexNames, name); * if (ix < 0) return null; * int seekOffset = indexOffsets[ix]; * * // create time zone instance * TimeZone tz = new TimeZone(); * tz.m_name = name; * * // read time zone definition from database file * FileStream f = dbFile.OpenRead(); * DataReader d = new DataReader(f); * try * { * f.Seek(seekOffset, SeekOrigin.Begin); * tz.m_name = d.ReadUTF(); * tz.m_fullName = d.ReadUTF(); * int numRules = d.ReadUnsignedShort(); * tz.rules = new Rule[numRules]; * for (int i=0; i<numRules; ++i) * { * Rule r = tz.rules[i] = new Rule(); * r.startYear = d.ReadUnsignedShort(); * r.offset = d.ReadInt(); * r.stdAbbr = d.ReadUTF(); * r.dstOffset = d.ReadInt(); * if (r.dstOffset == 0) continue; * r.dstAbbr = d.ReadUTF(); * r.dstStart = loadDstTime(d); * r.dstEnd = loadDstTime(d); * if (i != 0 && tz.rules[i-1].startYear <= r.startYear) * throw new IOException("TimeZone rules not sorted: " + name); * } * } * finally * { * f.Close(); * } * return tz; * } */ static DstTime loadDstTime(DataReader d) { DstTime t = new DstTime(); t.mon = d.ReadByte(); t.onMode = d.ReadByte(); t.onWeekday = d.ReadByte(); t.onDay = d.ReadByte(); t.atTime = d.ReadInt(); t.atMode = d.ReadByte(); return(t); }
/// <summary> /// Compare the specified time to the dst start/end time. /// Return -1 if x < specified time and +1 if x > specified time. /// </summary> static int compare(Rule rule, DstTime x, int year, int mon, int day, int time) { int c = compareMonth(x, mon); if (c != 0) { return(c); } c = compareOnDay(rule, x, year, mon, day); if (c != 0) { return(c); } return(compareAtTime(rule, x, time)); }
/* static void loadIndex() { DataReader reader = new DataReader(new BufferedStream(dbFile.OpenRead())); try { // check magic "fantz 01" long magic = reader.ReadLong(); if (magic != 0x66616e747a203031L) throw new IOException("Invalid magic 0x" + magic.ToString("X").ToLower()); reader.ReadUTF(); // load the name/offset pairs and verify in sort order int num = reader.ReadInt(); indexNames = new string[num]; indexTypes = new byte[num]; indexOffsets = new int[num]; for (int i=0; i<num; ++i) { indexNames[i] = reader.ReadUTF(); indexTypes[i] = (byte)reader.ReadByte(); indexOffsets[i] = reader.ReadInt(); if (i != 0 && String.Compare(indexNames[i-1], indexNames[i], StringComparison.Ordinal) >= 0) throw new IOException("Index not sorted"); } } finally { reader.Close(); } } static TimeZone loadTimeZone(string name) { // find index, which maps the file offset int ix = Array.BinarySearch(indexNames, name); if (ix < 0) return null; int seekOffset = indexOffsets[ix]; // create time zone instance TimeZone tz = new TimeZone(); tz.m_name = name; // read time zone definition from database file FileStream f = dbFile.OpenRead(); DataReader d = new DataReader(f); try { f.Seek(seekOffset, SeekOrigin.Begin); tz.m_name = d.ReadUTF(); tz.m_fullName = d.ReadUTF(); int numRules = d.ReadUnsignedShort(); tz.rules = new Rule[numRules]; for (int i=0; i<numRules; ++i) { Rule r = tz.rules[i] = new Rule(); r.startYear = d.ReadUnsignedShort(); r.offset = d.ReadInt(); r.stdAbbr = d.ReadUTF(); r.dstOffset = d.ReadInt(); if (r.dstOffset == 0) continue; r.dstAbbr = d.ReadUTF(); r.dstStart = loadDstTime(d); r.dstEnd = loadDstTime(d); if (i != 0 && tz.rules[i-1].startYear <= r.startYear) throw new IOException("TimeZone rules not sorted: " + name); } } finally { f.Close(); } return tz; } */ static DstTime loadDstTime(DataReader d) { DstTime t = new DstTime(); t.mon = d.ReadByte(); t.onMode = d.ReadByte(); t.onWeekday = d.ReadByte(); t.onDay = d.ReadByte(); t.atTime = d.ReadInt(); t.atMode = d.ReadByte(); return t; }
/// <summary> /// Compare on day. /// 'd' 5 the fifth of the month /// 'l' lastSun the last Sunday in the month /// 'l' lastMon the last Monday in the month /// '>' Sun>=8 first Sunday on or after the eighth /// '<' Sun<=25 last Sunday on or before the 25th (not used) /// </summary> static int compareOnDay(Rule rule, DstTime x, int year, int mon, int day) { // universal atTime might push us into the previous day if (x.atMode == 'u' && rule.offset + x.atTime < 0) ++day; switch (x.onMode) { case (byte)'d': if (x.onDay < day) return -1; if (x.onDay > day) return +1; return 0; case (byte)'l': int last = DateTime.weekdayInMonth(year, mon, x.onWeekday, -1); if (last < day) return -1; if (last > day) return +1; return 0; case (byte)'>': int start = DateTime.weekdayInMonth(year, mon, x.onWeekday, 1); while (start < x.onDay) start += 7; if (start < day) return -1; if (start > day) return +1; return 0; default: throw new Exception(""+(char)x.onMode); } }
/// <summary> /// Compare month /// </summary> static int compareMonth(DstTime x, int mon) { if (x.mon < mon) return -1; if (x.mon > mon) return +1; return 0; }
/// <summary> /// Compare at time. /// </summary> static int compareAtTime(Rule rule, DstTime x, int time) { int atTime = x.atTime; // if universal time, then we need to move atTime back to // local time (we might cross into the previous day) if (x.atMode == 'u') { if (rule.offset + x.atTime < 0) atTime = 24*60*60 + rule.offset + x.atTime; else atTime += rule.offset; } if (atTime < time) return -1; if (atTime > time) return +1; return 0; }
/// <summary> /// Compare the specified time to the dst start/end time. /// Return -1 if x < specified time and +1 if x > specified time. /// </summary> static int compare(Rule rule, DstTime x, int year, int mon, int day, int time) { int c = compareMonth(x, mon); if (c != 0) return c; c = compareOnDay(rule, x, year, mon, day); if (c != 0) return c; return compareAtTime(rule, x, time); }
/// <summary> /// Return if given date is the DstTime transition date /// </summary> internal static bool isDstDate(Rule rule, DstTime x, int year, int mon, int day) { return compareMonth(x, mon) == 0 && compareOnDay(rule, x, year, mon, day) == 0; }
/// <summary> /// Return if given date is the DstTime transition date /// </summary> internal static bool isDstDate(Rule rule, DstTime x, int year, int mon, int day) { return(compareMonth(x, mon) == 0 && compareOnDay(rule, x, year, mon, day) == 0); }