5 * Stephane Delcroix <stephane@delcroix.org>
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 using System.Runtime.Serialization;
28 using System.Collections.Generic;
29 using System.Collections.ObjectModel;
32 using System.Collections;
38 [SerializableAttribute]
39 public sealed partial class TimeZoneInfo : IEquatable<TimeZoneInfo>, ISerializable, IDeserializationCallback
41 TimeSpan baseUtcOffset;
42 public TimeSpan BaseUtcOffset {
43 get { return baseUtcOffset; }
46 string daylightDisplayName;
47 public string DaylightName {
49 if (disableDaylightSavingTime)
51 return daylightDisplayName;
56 public string DisplayName {
57 get { return displayName; }
65 static TimeZoneInfo local;
66 public static TimeZoneInfo Local {
71 local = FindSystemTimeZoneByFileName ("Local", "/etc/localtime");
74 local = FindSystemTimeZoneByFileName ("Local", Path.Combine (TimeZoneDirectory, "localtime"));
76 throw new TimeZoneNotFoundException ();
80 throw new TimeZoneNotFoundException ();
87 string standardDisplayName;
88 public string StandardName {
89 get { return standardDisplayName; }
92 bool disableDaylightSavingTime;
93 public bool SupportsDaylightSavingTime {
94 get { return !disableDaylightSavingTime; }
97 static TimeZoneInfo utc;
98 public static TimeZoneInfo Utc {
101 utc = CreateCustomTimeZone ("UTC", new TimeSpan (0), "UTC", "UTC");
106 static string timeZoneDirectory = null;
107 public static string TimeZoneDirectory {
109 if (timeZoneDirectory == null)
110 timeZoneDirectory = "/usr/share/zoneinfo";
111 return timeZoneDirectory;
115 timeZoneDirectory = value;
119 private AdjustmentRule [] adjustmentRules;
121 public static void ClearCachedData ()
125 systemTimeZones = null;
128 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
130 return ConvertTime (dateTime, TimeZoneInfo.Local, destinationTimeZone);
133 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
135 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
136 throw new ArgumentException ("Kind propery of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
138 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
139 throw new ArgumentException ("Kind propery of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
141 if (sourceTimeZone.IsInvalidTime (dateTime))
142 throw new ArgumentException ("dateTime parameter is an invalid time");
144 if (sourceTimeZone == null)
145 throw new ArgumentNullException ("sourceTimeZone");
147 if (destinationTimeZone == null)
148 throw new ArgumentNullException ("destinationTimeZone");
150 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
153 DateTime utc = ConvertTimeToUtc (dateTime);
155 if (destinationTimeZone == TimeZoneInfo.Utc)
158 return ConvertTimeFromUtc (utc, destinationTimeZone);
162 public static DateTimeOffset ConvertTime (DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
164 throw new NotImplementedException ();
167 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
169 return ConvertTime (dateTime, FindSystemTimeZoneById (destinationTimeZoneId));
172 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
174 return ConvertTime (dateTime, FindSystemTimeZoneById (sourceTimeZoneId), FindSystemTimeZoneById (destinationTimeZoneId));
177 public static DateTimeOffset ConvertTimeBySystemTimeZoneId (DateTimeOffset dateTimeOffset, string destinationTimeZoneId)
179 return ConvertTime (dateTimeOffset, FindSystemTimeZoneById (destinationTimeZoneId));
182 private DateTime ConvertTimeFromUtc (DateTime dateTime)
184 if (dateTime.Kind == DateTimeKind.Local)
185 throw new ArgumentException ("Kind property of dateTime is Local");
187 if (this == TimeZoneInfo.Utc)
188 return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
190 //FIXME: do not rely on DateTime implementation !
191 if (this == TimeZoneInfo.Local)
192 return DateTime.SpecifyKind (dateTime.ToLocalTime (), DateTimeKind.Unspecified);
194 AdjustmentRule rule = GetApplicableRule (dateTime);
196 if (IsDaylightSavingTime (DateTime.SpecifyKind (dateTime, DateTimeKind.Utc)))
197 return DateTime.SpecifyKind (dateTime + BaseUtcOffset + rule.DaylightDelta , DateTimeKind.Unspecified);
199 return DateTime.SpecifyKind (dateTime + BaseUtcOffset, DateTimeKind.Unspecified);
202 public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
204 if (destinationTimeZone == null)
205 throw new ArgumentNullException ("destinationTimeZone");
207 return destinationTimeZone.ConvertTimeFromUtc (dateTime);
210 public static DateTime ConvertTimeToUtc (DateTime dateTime)
212 if (dateTime.Kind == DateTimeKind.Utc)
215 //FIXME: do not rely on DateTime implementation !
216 return DateTime.SpecifyKind (dateTime.ToUniversalTime (), DateTimeKind.Utc);
219 public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
221 if (sourceTimeZone == null)
222 throw new ArgumentNullException ("sourceTimeZone");
224 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
225 throw new ArgumentException ("Kind propery of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
227 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
228 throw new ArgumentException ("Kind propery of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
230 if (sourceTimeZone.IsInvalidTime (dateTime))
231 throw new ArgumentException ("dateTime parameter is an invalid time");
233 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone == TimeZoneInfo.Utc)
236 if (dateTime.Kind == DateTimeKind.Utc)
239 if (dateTime.Kind == DateTimeKind.Local)
240 return ConvertTimeToUtc (dateTime);
242 if (sourceTimeZone.IsAmbiguousTime (dateTime) || !sourceTimeZone.IsDaylightSavingTime (dateTime))
243 return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset, DateTimeKind.Utc);
245 AdjustmentRule rule = sourceTimeZone.GetApplicableRule (dateTime);
246 return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset - rule.DaylightDelta, DateTimeKind.Utc);
250 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
252 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, null, null, true);
255 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules)
257 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, false);
260 public static TimeZoneInfo CreateCustomTimeZone ( string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
262 return new TimeZoneInfo (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, disableDaylightSavingTime);
265 public bool Equals (TimeZoneInfo other)
270 return other.Id == this.Id && HasSameRules (other);
273 public static TimeZoneInfo FindSystemTimeZoneById (string id)
275 //FIXME: this method should check for cached values in systemTimeZones
277 throw new ArgumentNullException ("id");
279 string filepath = Path.Combine (TimeZoneDirectory, id);
280 return FindSystemTimeZoneByFileName (id, filepath);
282 throw new NotImplementedException ();
287 const int BUFFER_SIZE = 8192; //Big enough for any tz file
288 private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
290 if (!File.Exists (filepath))
291 throw new TimeZoneNotFoundException ();
293 byte [] buffer = new byte [BUFFER_SIZE];
295 using (FileStream stream = File.OpenRead (filepath)) {
296 length = stream.Read (buffer, 0, BUFFER_SIZE);
299 if (!ValidTZFile (buffer, length))
300 throw new InvalidTimeZoneException ();
303 return ParseTZBuffer (id, buffer, length);
304 } catch (Exception e) {
305 throw new InvalidTimeZoneException (e.Message);
310 public static TimeZoneInfo FromSerializedString (string source)
312 throw new NotImplementedException ();
315 public AdjustmentRule [] GetAdjustmentRules ()
317 if (disableDaylightSavingTime)
318 return new AdjustmentRule [0];
320 return (AdjustmentRule []) adjustmentRules.Clone ();
323 public TimeSpan [] GetAmbiguousTimeOffsets (DateTime dateTime)
325 if (!IsAmbiguousTime (dateTime))
326 throw new ArgumentException ("dateTime is not an ambiguous time");
328 AdjustmentRule rule = GetApplicableRule (dateTime);
329 return new TimeSpan[] {baseUtcOffset, baseUtcOffset + rule.DaylightDelta};
332 public TimeSpan [] GetAmbiguousTimeOffsets (DateTimeOffset dateTimeOffset)
334 if (!IsAmbiguousTime (dateTimeOffset))
335 throw new ArgumentException ("dateTimeOffset is not an ambiguous time");
337 throw new NotImplementedException ();
340 public override int GetHashCode ()
342 int hash_code = Id.GetHashCode ();
343 foreach (AdjustmentRule rule in GetAdjustmentRules ())
344 hash_code ^= rule.GetHashCode ();
348 public void GetObjectData (SerializationInfo info, StreamingContext context)
350 throw new NotImplementedException ();
353 //FIXME: change this to a generic Dictionary and allow caching for FindSystemTimeZoneById
354 private static List<TimeZoneInfo> systemTimeZones = null;
355 public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones ()
357 if (systemTimeZones == null) {
358 systemTimeZones = new List<TimeZoneInfo> ();
360 string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"};
361 foreach (string continent in continents) {
363 foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) {
365 string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath));
366 systemTimeZones.Add (FindSystemTimeZoneById (id));
367 } catch (ArgumentNullException) {
368 } catch (TimeZoneNotFoundException) {
369 } catch (InvalidTimeZoneException) {
370 } catch (Exception e) {
371 if (e is OutOfMemoryException || e is System.Security.SecurityException)
374 Console.WriteLine ("Unexpected Exception");
382 throw new NotImplementedException ("This method is not implemented for this platform");
385 return new ReadOnlyCollection<TimeZoneInfo> (systemTimeZones);
388 public TimeSpan GetUtcOffset (DateTime dateTime)
390 if (IsDaylightSavingTime (dateTime)) {
391 AdjustmentRule rule = GetApplicableRule (dateTime);
392 return BaseUtcOffset + rule.DaylightDelta;
395 return BaseUtcOffset;
398 public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
400 throw new NotImplementedException ();
403 public bool HasSameRules (TimeZoneInfo other)
406 throw new ArgumentNullException ("other");
408 if (this.BaseUtcOffset != other.BaseUtcOffset)
411 if (this.adjustmentRules.Length != other.adjustmentRules.Length)
414 for (int i = 0; i < adjustmentRules.Length; i++) {
415 if (! (this.adjustmentRules [i]).Equals (other.adjustmentRules [i]))
422 public bool IsAmbiguousTime (DateTime dateTime)
424 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
425 throw new ArgumentException ("Kind is Local and time is Invalid");
427 if (this == TimeZoneInfo.Utc)
430 if (dateTime.Kind == DateTimeKind.Utc)
431 dateTime = ConvertTimeFromUtc (dateTime);
433 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
434 dateTime = ConvertTime (dateTime, TimeZoneInfo.Local, this);
436 AdjustmentRule rule = GetApplicableRule (dateTime);
437 DateTime tpoint = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year);
438 if (dateTime > tpoint - rule.DaylightDelta && dateTime <= tpoint)
444 public bool IsAmbiguousTime (DateTimeOffset dateTimeOffset)
446 throw new NotImplementedException ();
449 public bool IsDaylightSavingTime (DateTime dateTime)
451 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
452 throw new ArgumentException ("dateTime is invalid and Kind is Local");
454 if (this == TimeZoneInfo.Utc)
457 if (!SupportsDaylightSavingTime)
459 //FIXME: do not rely on DateTime implementation !
460 if ((dateTime.Kind == DateTimeKind.Local || dateTime.Kind == DateTimeKind.Unspecified) && this == TimeZoneInfo.Local)
461 return dateTime.IsDaylightSavingTime ();
463 //FIXME: do not rely on DateTime implementation !
464 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Utc)
465 return IsDaylightSavingTime (DateTime.SpecifyKind (dateTime.ToUniversalTime (), DateTimeKind.Utc));
467 AdjustmentRule rule = GetApplicableRule (dateTime.Date);
471 DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
472 DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
473 if (dateTime.Kind == DateTimeKind.Utc) {
474 DST_start -= BaseUtcOffset;
475 DST_end -= (BaseUtcOffset + rule.DaylightDelta);
478 return (dateTime >= DST_start && dateTime < DST_end);
481 public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
483 throw new NotImplementedException ();
486 public bool IsInvalidTime (DateTime dateTime)
488 if (dateTime.Kind == DateTimeKind.Utc)
490 if (dateTime.Kind == DateTimeKind.Local && this != Local)
493 AdjustmentRule rule = GetApplicableRule (dateTime);
494 DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
495 if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
501 public void OnDeserialization (object sender)
503 throw new NotImplementedException ();
506 public string ToSerializedString ()
508 throw new NotImplementedException ();
511 public override string ToString ()
516 private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
519 throw new ArgumentNullException ("id");
521 if (id == String.Empty)
522 throw new ArgumentException ("id parameter is an empty string");
524 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
525 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
527 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
528 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
532 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
535 if (adjustmentRules != null && adjustmentRules.Length != 0) {
536 AdjustmentRule prev = null;
537 foreach (AdjustmentRule current in adjustmentRules) {
539 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
541 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
542 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
543 throw new InvalidTimeZoneException ("Sum of baseUtcOffset and DaylightDelta of one or more object in adjustmentRules array is greater than 14 or less than -14 hours;");
545 if (prev != null && prev.DateStart > current.DateStart)
546 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
548 if (prev != null && prev.DateEnd > current.DateStart)
549 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
551 if (prev != null && prev.DateEnd == current.DateStart)
552 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
559 this.baseUtcOffset = baseUtcOffset;
560 this.displayName = displayName ?? id;
561 this.standardDisplayName = standardDisplayName ?? id;
562 this.daylightDisplayName = daylightDisplayName;
563 this.disableDaylightSavingTime = disableDaylightSavingTime;
564 this.adjustmentRules = adjustmentRules;
567 private AdjustmentRule GetApplicableRule (DateTime dateTime)
569 //Transitions are always in standard time
570 DateTime date = dateTime;
572 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
573 date = date.ToUniversalTime () + BaseUtcOffset;
575 if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc)
576 date = date + BaseUtcOffset;
578 foreach (AdjustmentRule rule in adjustmentRules) {
579 if (rule.DateStart > date.Date)
581 if (rule.DateEnd < date.Date)
588 private static DateTime TransitionPoint (TransitionTime transition, int year)
590 if (transition.IsFixedDateRule)
591 return new DateTime (year, transition.Month, transition.Day) + transition.TimeOfDay.TimeOfDay;
593 DayOfWeek first = (new DateTime (year, transition.Month, 1)).DayOfWeek;
594 int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first) % 7;
595 if (day > DateTime.DaysInMonth (year, transition.Month))
597 return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
601 private static bool ValidTZFile (byte [] buffer, int length)
603 System.Text.StringBuilder magic = new System.Text.StringBuilder ();
605 for (int i = 0; i < 4; i++)
606 magic.Append ((char)buffer [i]);
608 if (magic.ToString () != "TZif")
611 if (length >= BUFFER_SIZE)
617 private struct TimeType
623 public TimeType (int offset, bool is_dst, string abbrev)
625 this.offset = offset;
626 this.is_dst = is_dst;
627 this.abbrev = abbrev;
631 get { return offset; }
635 get { return is_dst; }
639 get { return abbrev; }
642 public override string ToString ()
644 return "offset: " + offset + "s, is_dst: " + is_dst + ", zone name: " + abbrev;
648 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
650 DataConverter enc = DataConverter.BigEndian;
652 //Reading the header. 4 bytes for magic, 16 are reserved
653 int ttisgmtcnt = enc.GetInt32 (buffer, 20);
654 int ttisstdcnt = enc.GetInt32 (buffer, 24);
655 int leapcnt = enc.GetInt32 (buffer, 28);
656 int timecnt = enc.GetInt32 (buffer, 32);
657 int typecnt = enc.GetInt32 (buffer, 36);
658 int charcnt = enc.GetInt32 (buffer, 40);
660 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
661 throw new InvalidTimeZoneException ();
663 Hashtable abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
664 Hashtable time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
665 SortedList transitions = ParseTransitions (buffer, 44, timecnt, time_types);
667 if (time_types.Count == 0)
668 throw new InvalidTimeZoneException ();
670 if (time_types.Count == 1 && ((TimeType)time_types[0]).IsDst)
671 throw new InvalidTimeZoneException ();
673 TimeSpan baseUtcOffset = new TimeSpan (0);
674 TimeSpan dstDelta = new TimeSpan (0);
675 string standardDisplayName = null;
676 string daylightDisplayName = null;
677 bool dst_observed = false;
678 DateTime dst_start = DateTime.MinValue;
679 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
681 for (int i = 0; i < transitions.Count; i++) {
682 DateTime ttime = (DateTime) transitions.GetKey (i);
683 TimeType ttype = (TimeType) transitions [ttime];
685 if (standardDisplayName != ttype.Name || baseUtcOffset.TotalSeconds != ttype.Offset) {
686 standardDisplayName = ttype.Name;
687 daylightDisplayName = null;
688 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
689 adjustmentRules = new List<AdjustmentRule> ();
690 dst_observed = false;
693 //FIXME: check additional fields for this:
694 //most of the transitions are expressed in GMT
695 dst_start += baseUtcOffset;
696 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
698 DateTime dateStart, dateEnd;
699 if (dst_start.Month < 7)
700 dateStart = new DateTime (dst_start.Year, 1, 1);
702 dateStart = new DateTime (dst_start.Year, 7, 1);
704 if (dst_end.Month >= 7)
705 dateEnd = new DateTime (dst_start.Year, 12, 31);
707 dateEnd = new DateTime (dst_end.Year, 6, 30);
709 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
710 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
711 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
713 dst_observed = false;
715 if (daylightDisplayName != ttype.Name || dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
716 daylightDisplayName = ttype.Name;
717 dstDelta = new TimeSpan(0, 0, ttype.Offset) - baseUtcOffset;
724 if (adjustmentRules.Count == 0) {
725 TimeType t = (TimeType)time_types [0];
726 if (standardDisplayName == null) {
727 standardDisplayName = t.Name;
728 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
730 return CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
732 return CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, adjustmentRules.ToArray ());
736 private static Hashtable ParseAbbreviations (byte [] buffer, int index, int count)
738 Hashtable abbrevs = new Hashtable ();
739 int abbrev_index = 0;
740 System.Text.StringBuilder sb = new System.Text.StringBuilder ();
741 for (int i = 0; i < count; i++) {
742 char c = (char) buffer [index + i];
746 abbrevs.Add (abbrev_index, sb.ToString ());
747 abbrev_index = i + 1;
748 sb = new System.Text.StringBuilder ();
754 private static Hashtable ParseTimesTypes (byte [] buffer, int index, int count, Hashtable abbreviations)
756 DataConverter enc = DataConverter.BigEndian;
757 Hashtable types = new Hashtable (count);
758 for (int i = 0; i < count; i++) {
759 int offset = enc.GetInt32 (buffer, index + 6 * i);
760 byte is_dst = buffer [index + 6 * i + 4];
761 byte abbrev = buffer [index + 6 * i + 5];
762 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev] as string));
767 private static SortedList ParseTransitions (byte [] buffer, int index, int count, Hashtable time_types)
769 DataConverter enc = DataConverter.BigEndian;
770 SortedList trans = new SortedList (count);
771 for (int i = 0; i < count; i++) {
772 int unixtime = enc.GetInt32 (buffer, index + 4 * i);
773 DateTime ttime = DateTimeFromUnixTime (unixtime);
774 byte ttype = buffer [index + 4 * count + i];
775 trans.Add (ttime, time_types [(int)ttype]);
780 private static DateTime DateTimeFromUnixTime (long unix_time)
782 DateTime date_time = new DateTime (1970, 1, 1);
783 return date_time.AddSeconds (unix_time);