6 * Stephane Delcroix <stephane@delcroix.org>
8 * Copyright 2011 Xamarin Inc.
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Runtime.CompilerServices;
32 using System.Threading;
33 using System.Collections.Generic;
34 using System.Collections.ObjectModel;
35 using System.Runtime.Serialization;
36 using System.Runtime.InteropServices;
38 using System.Globalization;
41 using Microsoft.Win32;
45 partial class TimeZoneInfo
47 TimeSpan baseUtcOffset;
48 public TimeSpan BaseUtcOffset {
49 get { return baseUtcOffset; }
52 string daylightDisplayName;
53 public string DaylightName {
55 return supportsDaylightSavingTime
62 public string DisplayName {
63 get { return displayName; }
71 static TimeZoneInfo local;
72 public static TimeZoneInfo Local {
78 throw new TimeZoneNotFoundException ();
80 if (Interlocked.CompareExchange (ref local, l, null) != null)
89 TimeZone transitions are stored when there is a change on the base offset.
91 private List<KeyValuePair<DateTime, TimeType>> transitions;
93 private static bool readlinkNotFound;
96 private static extern int readlink (string path, byte[] buffer, int buflen);
98 private static string readlink (string path)
100 if (readlinkNotFound)
103 byte[] buf = new byte [512];
107 ret = readlink (path, buf, buf.Length);
108 } catch (DllNotFoundException e) {
109 readlinkNotFound = true;
111 } catch (EntryPointNotFoundException e) {
112 readlinkNotFound = true;
116 if (ret == -1) return null;
117 char[] cbuf = new char [512];
118 int chars = System.Text.Encoding.Default.GetChars (buf, 0, ret, cbuf, 0);
119 return new String (cbuf, 0, chars);
122 private static bool TryGetNameFromPath (string path, out string name)
125 var linkPath = readlink (path);
126 if (linkPath != null) {
127 if (Path.IsPathRooted(linkPath))
130 path = Path.Combine(Path.GetDirectoryName(path), linkPath);
133 path = Path.GetFullPath (path);
135 if (string.IsNullOrEmpty (TimeZoneDirectory))
138 var baseDir = TimeZoneDirectory;
139 if (baseDir [baseDir.Length-1] != Path.DirectorySeparatorChar)
140 baseDir += Path.DirectorySeparatorChar;
142 if (!path.StartsWith (baseDir, StringComparison.InvariantCulture))
145 name = path.Substring (baseDir.Length);
146 if (name == "localtime")
152 #if !MOBILE || MOBILE_STATIC
153 static TimeZoneInfo CreateLocal ()
156 if (IsWindows && LocalZoneKey != null) {
157 string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
159 name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp
160 name = TrimSpecial (name);
162 return TimeZoneInfo.FindSystemTimeZoneById (name);
166 var tz = Environment.GetEnvironmentVariable ("TZ");
168 if (tz == String.Empty)
171 return FindSystemTimeZoneByFileName (tz, Path.Combine (TimeZoneDirectory, tz));
177 var tzFilePaths = new string [] {
179 Path.Combine (TimeZoneDirectory, "localtime")};
181 foreach (var tzFilePath in tzFilePaths) {
183 string tzName = null;
184 if (!TryGetNameFromPath (tzFilePath, out tzName))
186 return FindSystemTimeZoneByFileName (tzName, tzFilePath);
187 } catch (TimeZoneNotFoundException) {
195 static TimeZoneInfo FindSystemTimeZoneByIdCore (string id)
198 string filepath = Path.Combine (TimeZoneDirectory, id);
199 return FindSystemTimeZoneByFileName (id, filepath);
201 throw new NotImplementedException ();
205 static void GetSystemTimeZonesCore (List<TimeZoneInfo> systemTimeZones)
208 if (TimeZoneKey != null) {
209 foreach (string id in TimeZoneKey.GetSubKeyNames ()) {
211 systemTimeZones.Add (FindSystemTimeZoneById (id));
220 string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"};
221 foreach (string continent in continents) {
223 foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) {
225 string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath));
226 systemTimeZones.Add (FindSystemTimeZoneById (id));
227 } catch (ArgumentNullException) {
228 } catch (TimeZoneNotFoundException) {
229 } catch (InvalidTimeZoneException) {
230 } catch (Exception) {
237 throw new NotImplementedException ("This method is not implemented for this platform");
242 string standardDisplayName;
243 public string StandardName {
244 get { return standardDisplayName; }
247 bool supportsDaylightSavingTime;
248 public bool SupportsDaylightSavingTime {
249 get { return supportsDaylightSavingTime; }
252 static TimeZoneInfo utc;
253 public static TimeZoneInfo Utc {
256 utc = CreateCustomTimeZone ("UTC", new TimeSpan (0), "UTC", "UTC");
261 static string timeZoneDirectory;
262 static string TimeZoneDirectory {
264 if (timeZoneDirectory == null)
265 timeZoneDirectory = "/usr/share/zoneinfo";
266 return timeZoneDirectory;
270 timeZoneDirectory = value;
274 private AdjustmentRule [] adjustmentRules;
276 #if !NET_2_1 || MOBILE_STATIC
278 /// Determine whether windows of not (taken Stephane Delcroix's code)
280 private static bool IsWindows
283 int platform = (int) Environment.OSVersion.Platform;
284 return ((platform != 4) && (platform != 6) && (platform != 128));
289 /// Needed to trim misc garbage in MS registry keys
291 private static string TrimSpecial (string str)
296 while (Istart < str.Length && !char.IsLetterOrDigit(str[Istart])) Istart++;
297 var Iend = str.Length - 1;
298 while (Iend > Istart && !char.IsLetterOrDigit(str[Iend]) && str[Iend] != ')') // zone name can include parentheses like "Central Standard Time (Mexico)"
301 return str.Substring (Istart, Iend-Istart+1);
305 static RegistryKey timeZoneKey;
306 static RegistryKey TimeZoneKey {
308 if (timeZoneKey != null)
313 return timeZoneKey = Registry.LocalMachine.OpenSubKey (
314 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
319 static RegistryKey localZoneKey;
320 static RegistryKey LocalZoneKey {
322 if (localZoneKey != null)
328 return localZoneKey = Registry.LocalMachine.OpenSubKey (
329 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", false);
335 private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified)
337 var resultTicks = date.Ticks + ticks;
338 if (resultTicks < DateTime.MinValue.Ticks) {
339 result = DateTime.SpecifyKind (DateTime.MinValue, kind);
343 if (resultTicks > DateTime.MaxValue.Ticks) {
344 result = DateTime.SpecifyKind (DateTime.MaxValue, kind);
348 result = new DateTime (resultTicks, kind);
352 public static void ClearCachedData ()
356 systemTimeZones = null;
359 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
361 return ConvertTime (dateTime, dateTime.Kind == DateTimeKind.Utc ? TimeZoneInfo.Utc : TimeZoneInfo.Local, destinationTimeZone);
364 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
366 if (sourceTimeZone == null)
367 throw new ArgumentNullException ("sourceTimeZone");
369 if (destinationTimeZone == null)
370 throw new ArgumentNullException ("destinationTimeZone");
372 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
373 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
375 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
376 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
378 if (sourceTimeZone.IsInvalidTime (dateTime))
379 throw new ArgumentException ("dateTime parameter is an invalid time");
381 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
384 DateTime utc = ConvertTimeToUtc (dateTime, sourceTimeZone);
386 if (destinationTimeZone != TimeZoneInfo.Utc) {
387 utc = ConvertTimeFromUtc (utc, destinationTimeZone);
388 if (dateTime.Kind == DateTimeKind.Unspecified)
389 return DateTime.SpecifyKind (utc, DateTimeKind.Unspecified);
395 public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
397 if (destinationTimeZone == null)
398 throw new ArgumentNullException("destinationTimeZone");
400 var utcDateTime = dateTimeOffset.UtcDateTime;
403 var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst);
405 return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset);
408 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
410 return ConvertTime (dateTime, FindSystemTimeZoneById (destinationTimeZoneId));
413 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
415 return ConvertTime (dateTime, FindSystemTimeZoneById (sourceTimeZoneId), FindSystemTimeZoneById (destinationTimeZoneId));
418 public static DateTimeOffset ConvertTimeBySystemTimeZoneId (DateTimeOffset dateTimeOffset, string destinationTimeZoneId)
420 return ConvertTime (dateTimeOffset, FindSystemTimeZoneById (destinationTimeZoneId));
423 private DateTime ConvertTimeFromUtc (DateTime dateTime)
425 if (dateTime.Kind == DateTimeKind.Local)
426 throw new ArgumentException ("Kind property of dateTime is Local");
428 if (this == TimeZoneInfo.Utc)
429 return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
431 var utcOffset = GetUtcOffset (dateTime);
433 var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified;
436 if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind))
437 return DateTime.SpecifyKind (DateTime.MaxValue, kind);
442 public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
444 if (destinationTimeZone == null)
445 throw new ArgumentNullException ("destinationTimeZone");
447 return destinationTimeZone.ConvertTimeFromUtc (dateTime);
450 public static DateTime ConvertTimeToUtc (DateTime dateTime)
452 if (dateTime.Kind == DateTimeKind.Utc)
455 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local);
458 static internal DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
460 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local, flags);
463 public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
465 return ConvertTimeToUtc (dateTime, sourceTimeZone, TimeZoneInfoOptions.None);
468 static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfoOptions flags)
470 if ((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) {
471 if (sourceTimeZone == null)
472 throw new ArgumentNullException ("sourceTimeZone");
474 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
475 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
477 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
478 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
480 if (sourceTimeZone.IsInvalidTime (dateTime))
481 throw new ArgumentException ("dateTime parameter is an invalid time");
484 if (dateTime.Kind == DateTimeKind.Utc)
488 var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst);
490 DateTime utcDateTime;
491 TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc);
495 static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
497 bool isDaylightSavings;
498 return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
501 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
503 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, null, null, true);
506 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules)
508 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, false);
511 public static TimeZoneInfo CreateCustomTimeZone ( string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
513 return new TimeZoneInfo (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, disableDaylightSavingTime);
516 public override bool Equals (object obj)
518 return Equals (obj as TimeZoneInfo);
521 public bool Equals (TimeZoneInfo other)
526 return other.Id == this.Id && HasSameRules (other);
529 public static TimeZoneInfo FindSystemTimeZoneById (string id)
531 //FIXME: this method should check for cached values in systemTimeZones
533 throw new ArgumentNullException ("id");
535 if (TimeZoneKey != null)
537 if (id == "Coordinated Universal Time")
538 id = "UTC"; //windows xp exception for "StandardName" property
539 RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
541 throw new TimeZoneNotFoundException ();
542 return FromRegistryKey(id, key);
545 // Local requires special logic that already exists in the Local property (bug #326)
549 return FindSystemTimeZoneByIdCore (id);
553 private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
555 if (!File.Exists (filepath))
556 throw new TimeZoneNotFoundException ();
558 using (FileStream stream = File.OpenRead (filepath)) {
559 return BuildFromStream (id, stream);
565 private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key)
567 byte [] reg_tzi = (byte []) key.GetValue ("TZI");
570 throw new InvalidTimeZoneException ();
572 int bias = BitConverter.ToInt32 (reg_tzi, 0);
573 TimeSpan baseUtcOffset = new TimeSpan (0, -bias, 0);
575 string display_name = (string) key.GetValue ("Display");
576 string standard_name = (string) key.GetValue ("Std");
577 string daylight_name = (string) key.GetValue ("Dlt");
579 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
581 RegistryKey dst_key = key.OpenSubKey ("Dynamic DST", false);
582 if (dst_key != null) {
583 int first_year = (int) dst_key.GetValue ("FirstEntry");
584 int last_year = (int) dst_key.GetValue ("LastEntry");
587 for (year=first_year; year<=last_year; year++) {
588 byte [] dst_tzi = (byte []) dst_key.GetValue (year.ToString ());
589 if (dst_tzi != null) {
590 int start_year = year == first_year ? 1 : year;
591 int end_year = year == last_year ? 9999 : year;
592 ParseRegTzi(adjustmentRules, start_year, end_year, dst_tzi);
597 ParseRegTzi(adjustmentRules, 1, 9999, reg_tzi);
599 return CreateCustomTimeZone (id, baseUtcOffset, display_name, standard_name, daylight_name, ValidateRules (adjustmentRules).ToArray ());
602 private static void ParseRegTzi (List<AdjustmentRule> adjustmentRules, int start_year, int end_year, byte [] buffer)
604 //int standard_bias = BitConverter.ToInt32 (buffer, 4); /* not sure how to handle this */
605 int daylight_bias = BitConverter.ToInt32 (buffer, 8);
607 int standard_year = BitConverter.ToInt16 (buffer, 12);
608 int standard_month = BitConverter.ToInt16 (buffer, 14);
609 int standard_dayofweek = BitConverter.ToInt16 (buffer, 16);
610 int standard_day = BitConverter.ToInt16 (buffer, 18);
611 int standard_hour = BitConverter.ToInt16 (buffer, 20);
612 int standard_minute = BitConverter.ToInt16 (buffer, 22);
613 int standard_second = BitConverter.ToInt16 (buffer, 24);
614 int standard_millisecond = BitConverter.ToInt16 (buffer, 26);
616 int daylight_year = BitConverter.ToInt16 (buffer, 28);
617 int daylight_month = BitConverter.ToInt16 (buffer, 30);
618 int daylight_dayofweek = BitConverter.ToInt16 (buffer, 32);
619 int daylight_day = BitConverter.ToInt16 (buffer, 34);
620 int daylight_hour = BitConverter.ToInt16 (buffer, 36);
621 int daylight_minute = BitConverter.ToInt16 (buffer, 38);
622 int daylight_second = BitConverter.ToInt16 (buffer, 40);
623 int daylight_millisecond = BitConverter.ToInt16 (buffer, 42);
625 if (standard_month == 0 || daylight_month == 0)
629 DateTime start_timeofday = new DateTime (1, 1, 1, daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
630 TransitionTime start_transition_time;
632 if (daylight_year == 0) {
633 start_date = new DateTime (start_year, 1, 1);
634 start_transition_time = TransitionTime.CreateFloatingDateRule (
635 start_timeofday, daylight_month, daylight_day,
636 (DayOfWeek) daylight_dayofweek);
639 start_date = new DateTime (daylight_year, daylight_month, daylight_day,
640 daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
641 start_transition_time = TransitionTime.CreateFixedDateRule (
642 start_timeofday, daylight_month, daylight_day);
646 DateTime end_timeofday = new DateTime (1, 1, 1, standard_hour, standard_minute, standard_second, standard_millisecond);
647 TransitionTime end_transition_time;
649 if (standard_year == 0) {
650 end_date = new DateTime (end_year, 12, 31);
651 end_transition_time = TransitionTime.CreateFloatingDateRule (
652 end_timeofday, standard_month, standard_day,
653 (DayOfWeek) standard_dayofweek);
656 end_date = new DateTime (standard_year, standard_month, standard_day,
657 standard_hour, standard_minute, standard_second, standard_millisecond);
658 end_transition_time = TransitionTime.CreateFixedDateRule (
659 end_timeofday, standard_month, standard_day);
662 TimeSpan daylight_delta = new TimeSpan(0, -daylight_bias, 0);
664 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (
665 start_date, end_date, daylight_delta,
666 start_transition_time, end_transition_time));
670 public AdjustmentRule [] GetAdjustmentRules ()
672 if (!supportsDaylightSavingTime || adjustmentRules == null)
673 return new AdjustmentRule [0];
675 return (AdjustmentRule []) adjustmentRules.Clone ();
678 public TimeSpan [] GetAmbiguousTimeOffsets (DateTime dateTime)
680 if (!IsAmbiguousTime (dateTime))
681 throw new ArgumentException ("dateTime is not an ambiguous time");
683 AdjustmentRule rule = GetApplicableRule (dateTime);
685 return new TimeSpan[] {baseUtcOffset, baseUtcOffset + rule.DaylightDelta};
687 return new TimeSpan[] {baseUtcOffset, baseUtcOffset};
690 public TimeSpan [] GetAmbiguousTimeOffsets (DateTimeOffset dateTimeOffset)
692 if (!IsAmbiguousTime (dateTimeOffset))
693 throw new ArgumentException ("dateTimeOffset is not an ambiguous time");
695 throw new NotImplementedException ();
698 public override int GetHashCode ()
700 int hash_code = Id.GetHashCode ();
701 foreach (AdjustmentRule rule in GetAdjustmentRules ())
702 hash_code ^= rule.GetHashCode ();
706 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
709 throw new ArgumentNullException ("info");
710 info.AddValue ("Id", id);
711 info.AddValue ("DisplayName", displayName);
712 info.AddValue ("StandardName", standardDisplayName);
713 info.AddValue ("DaylightName", daylightDisplayName);
714 info.AddValue ("BaseUtcOffset", baseUtcOffset);
715 info.AddValue ("AdjustmentRules", adjustmentRules);
716 info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime);
719 static ReadOnlyCollection<TimeZoneInfo> systemTimeZones;
721 public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones ()
723 if (systemTimeZones == null) {
724 var tz = new List<TimeZoneInfo> ();
725 GetSystemTimeZonesCore (tz);
726 Interlocked.CompareExchange (ref systemTimeZones, new ReadOnlyCollection<TimeZoneInfo> (tz), null);
729 return systemTimeZones;
732 public TimeSpan GetUtcOffset (DateTime dateTime)
735 return GetUtcOffset (dateTime, out isDST);
738 public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
741 return GetUtcOffset (dateTimeOffset.UtcDateTime, out isDST);
744 private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
748 TimeZoneInfo tz = this;
749 if (dateTime.Kind == DateTimeKind.Utc)
750 tz = TimeZoneInfo.Utc;
752 if (dateTime.Kind == DateTimeKind.Local)
753 tz = TimeZoneInfo.Local;
756 var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
763 DateTime utcDateTime;
764 if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
765 return BaseUtcOffset;
767 return GetUtcOffsetHelper (utcDateTime, this, out isDST);
770 // This is an helper method used by the method above, do not use this on its own.
771 private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
773 if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
774 throw new Exception ();
778 if (tz == TimeZoneInfo.Utc)
779 return TimeSpan.Zero;
782 if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
785 if (dateTime.Kind == DateTimeKind.Utc) {
786 var utcRule = tz.GetApplicableRule (dateTime);
787 if (utcRule != null && tz.IsInDST (utcRule, dateTime)) {
789 return tz.BaseUtcOffset + utcRule.DaylightDelta;
792 return tz.BaseUtcOffset;
795 DateTime stdUtcDateTime;
796 if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc))
797 return tz.BaseUtcOffset;
799 var tzRule = tz.GetApplicableRule (stdUtcDateTime);
801 DateTime dstUtcDateTime = DateTime.MinValue;
802 if (tzRule != null) {
803 if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc))
804 return tz.BaseUtcOffset;
807 if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
809 return tz.BaseUtcOffset + tzRule.DaylightDelta;
812 return tz.BaseUtcOffset;
815 public bool HasSameRules (TimeZoneInfo other)
818 throw new ArgumentNullException ("other");
820 if ((this.adjustmentRules == null) != (other.adjustmentRules == null))
823 if (this.adjustmentRules == null)
826 if (this.BaseUtcOffset != other.BaseUtcOffset)
829 if (this.adjustmentRules.Length != other.adjustmentRules.Length)
832 for (int i = 0; i < adjustmentRules.Length; i++) {
833 if (! (this.adjustmentRules [i]).Equals (other.adjustmentRules [i]))
840 public bool IsAmbiguousTime (DateTime dateTime)
842 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
843 throw new ArgumentException ("Kind is Local and time is Invalid");
845 if (this == TimeZoneInfo.Utc)
848 if (dateTime.Kind == DateTimeKind.Utc)
849 dateTime = ConvertTimeFromUtc (dateTime);
851 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
852 dateTime = ConvertTime (dateTime, TimeZoneInfo.Local, this);
854 AdjustmentRule rule = GetApplicableRule (dateTime);
856 DateTime tpoint = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year);
857 if (dateTime > tpoint - rule.DaylightDelta && dateTime <= tpoint)
864 public bool IsAmbiguousTime (DateTimeOffset dateTimeOffset)
866 throw new NotImplementedException ();
869 private bool IsInDST (AdjustmentRule rule, DateTime dateTime)
871 // Check whether we're in the dateTime year's DST period
872 if (IsInDSTForYear (rule, dateTime, dateTime.Year))
875 // We might be in the dateTime previous year's DST period
876 return dateTime.Year > 1 && IsInDSTForYear (rule, dateTime, dateTime.Year - 1);
879 bool IsInDSTForYear (AdjustmentRule rule, DateTime dateTime, int year)
881 DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, year);
882 DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
883 if (dateTime.Kind == DateTimeKind.Utc) {
884 DST_start -= BaseUtcOffset;
885 DST_end -= (BaseUtcOffset + rule.DaylightDelta);
888 return (dateTime >= DST_start && dateTime < DST_end);
891 public bool IsDaylightSavingTime (DateTime dateTime)
893 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
894 throw new ArgumentException ("dateTime is invalid and Kind is Local");
896 if (this == TimeZoneInfo.Utc)
899 if (!SupportsDaylightSavingTime)
903 GetUtcOffset (dateTime, out isDst);
908 internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags)
910 return IsDaylightSavingTime (dateTime);
913 public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
915 return IsDaylightSavingTime (dateTimeOffset.DateTime);
918 internal DaylightTime GetDaylightChanges (int year)
920 DateTime start = DateTime.MinValue, end = DateTime.MinValue;
921 TimeSpan delta = new TimeSpan ();
923 if (transitions != null) {
924 end = DateTime.MaxValue;
925 for (var i = transitions.Count - 1; i >= 0; i--) {
926 var pair = transitions [i];
927 DateTime ttime = pair.Key;
928 TimeType ttype = pair.Value;
930 if (ttime.Year > year)
932 if (ttime.Year < year)
936 // DaylightTime.Delta is relative to the current BaseUtcOffset.
937 delta = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset;
944 // DaylightTime.Start is relative to the Standard time.
945 if (!TryAddTicks (start, BaseUtcOffset.Ticks, out start))
946 start = DateTime.MinValue;
948 // DaylightTime.End is relative to the DST time.
949 if (!TryAddTicks (end, BaseUtcOffset.Ticks + delta.Ticks, out end))
950 end = DateTime.MinValue;
952 AdjustmentRule first = null, last = null;
954 foreach (var rule in GetAdjustmentRules ()) {
955 if (rule.DateStart.Year != year && rule.DateEnd.Year != year)
957 if (rule.DateStart.Year == year)
959 if (rule.DateEnd.Year == year)
963 if (first == null || last == null)
964 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
966 start = TransitionPoint (first.DaylightTransitionStart, year);
967 end = TransitionPoint (last.DaylightTransitionEnd, year);
968 delta = first.DaylightDelta;
971 if (start == DateTime.MinValue || end == DateTime.MinValue)
972 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
974 return new DaylightTime (start, end, delta);
977 public bool IsInvalidTime (DateTime dateTime)
979 if (dateTime.Kind == DateTimeKind.Utc)
981 if (dateTime.Kind == DateTimeKind.Local && this != Local)
984 AdjustmentRule rule = GetApplicableRule (dateTime);
986 DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
987 if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
994 void IDeserializationCallback.OnDeserialization (object sender)
997 TimeZoneInfo.Validate (id, baseUtcOffset, adjustmentRules);
998 } catch (ArgumentException ex) {
999 throw new SerializationException ("invalid serialization data", ex);
1003 private static void Validate (string id, TimeSpan baseUtcOffset, AdjustmentRule [] adjustmentRules)
1006 throw new ArgumentNullException ("id");
1008 if (id == String.Empty)
1009 throw new ArgumentException ("id parameter is an empty string");
1011 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1012 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1014 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1015 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1019 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1022 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1023 AdjustmentRule prev = null;
1024 foreach (AdjustmentRule current in adjustmentRules) {
1025 if (current == null)
1026 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1028 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1029 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1030 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;");
1032 if (prev != null && prev.DateStart > current.DateStart)
1033 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1035 if (prev != null && prev.DateEnd > current.DateStart)
1036 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1038 if (prev != null && prev.DateEnd == current.DateStart)
1039 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1046 public override string ToString ()
1051 private TimeZoneInfo (SerializationInfo info, StreamingContext context)
1054 throw new ArgumentNullException ("info");
1055 id = (string) info.GetValue ("Id", typeof (string));
1056 displayName = (string) info.GetValue ("DisplayName", typeof (string));
1057 standardDisplayName = (string) info.GetValue ("StandardName", typeof (string));
1058 daylightDisplayName = (string) info.GetValue ("DaylightName", typeof (string));
1059 baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", typeof (TimeSpan));
1060 adjustmentRules = (TimeZoneInfo.AdjustmentRule []) info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
1061 supportsDaylightSavingTime = (bool) info.GetValue ("SupportsDaylightSavingTime", typeof (bool));
1064 private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
1067 throw new ArgumentNullException ("id");
1069 if (id == String.Empty)
1070 throw new ArgumentException ("id parameter is an empty string");
1072 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1073 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1075 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1076 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1080 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1083 bool supportsDaylightSavingTime = !disableDaylightSavingTime;
1085 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1086 AdjustmentRule prev = null;
1087 foreach (AdjustmentRule current in adjustmentRules) {
1088 if (current == null)
1089 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1091 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1092 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1093 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;");
1095 if (prev != null && prev.DateStart > current.DateStart)
1096 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1098 if (prev != null && prev.DateEnd > current.DateStart)
1099 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1101 if (prev != null && prev.DateEnd == current.DateStart)
1102 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1107 supportsDaylightSavingTime = false;
1111 this.baseUtcOffset = baseUtcOffset;
1112 this.displayName = displayName ?? id;
1113 this.standardDisplayName = standardDisplayName ?? id;
1114 this.daylightDisplayName = daylightDisplayName;
1115 this.supportsDaylightSavingTime = supportsDaylightSavingTime;
1116 this.adjustmentRules = adjustmentRules;
1119 private AdjustmentRule GetApplicableRule (DateTime dateTime)
1121 //Applicable rules are in standard time
1122 DateTime date = dateTime;
1124 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1125 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date))
1127 } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) {
1128 if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date))
1132 // get the date component of the datetime
1135 if (adjustmentRules != null) {
1136 foreach (AdjustmentRule rule in adjustmentRules) {
1137 if (rule.DateStart > date)
1139 if (rule.DateEnd < date)
1147 private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out bool isDst)
1149 offset = BaseUtcOffset;
1152 if (transitions == null)
1155 //Transitions are in UTC
1156 DateTime date = dateTime;
1158 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1159 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1163 if (dateTime.Kind != DateTimeKind.Utc) {
1164 if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1168 for (var i = transitions.Count - 1; i >= 0; i--) {
1169 var pair = transitions [i];
1170 DateTime ttime = pair.Key;
1171 TimeType ttype = pair.Value;
1176 offset = new TimeSpan (0, 0, ttype.Offset);
1177 isDst = ttype.IsDst;
1185 private static DateTime TransitionPoint (TransitionTime transition, int year)
1187 if (transition.IsFixedDateRule)
1188 return new DateTime (year, transition.Month, transition.Day) + transition.TimeOfDay.TimeOfDay;
1190 DayOfWeek first = (new DateTime (year, transition.Month, 1)).DayOfWeek;
1191 int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first + 7) % 7;
1192 if (day > DateTime.DaysInMonth (year, transition.Month))
1196 return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
1199 static List<AdjustmentRule> ValidateRules (List<AdjustmentRule> adjustmentRules)
1201 AdjustmentRule prev = null;
1202 foreach (AdjustmentRule current in adjustmentRules.ToArray ()) {
1203 if (prev != null && prev.DateEnd > current.DateStart) {
1204 adjustmentRules.Remove (current);
1208 return adjustmentRules;
1211 #if LIBC || MONOTOUCH
1212 const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k)
1214 private static TimeZoneInfo BuildFromStream (string id, Stream stream)
1216 byte [] buffer = new byte [BUFFER_SIZE];
1217 int length = stream.Read (buffer, 0, BUFFER_SIZE);
1219 if (!ValidTZFile (buffer, length))
1220 throw new InvalidTimeZoneException ("TZ file too big for the buffer");
1223 return ParseTZBuffer (id, buffer, length);
1224 } catch (InvalidTimeZoneException) {
1226 } catch (Exception e) {
1227 throw new InvalidTimeZoneException ("Time zone information file contains invalid data", e);
1231 private static bool ValidTZFile (byte [] buffer, int length)
1233 StringBuilder magic = new StringBuilder ();
1235 for (int i = 0; i < 4; i++)
1236 magic.Append ((char)buffer [i]);
1238 if (magic.ToString () != "TZif")
1241 if (length >= BUFFER_SIZE)
1247 static int SwapInt32 (int i)
1249 return (((i >> 24) & 0xff)
1250 | ((i >> 8) & 0xff00)
1251 | ((i << 8) & 0xff0000)
1252 | (((i & 0xff) << 24)));
1255 static int ReadBigEndianInt32 (byte [] buffer, int start)
1257 int i = BitConverter.ToInt32 (buffer, start);
1258 if (!BitConverter.IsLittleEndian)
1261 return SwapInt32 (i);
1264 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
1266 //Reading the header. 4 bytes for magic, 16 are reserved
1267 int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
1268 int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
1269 int leapcnt = ReadBigEndianInt32 (buffer, 28);
1270 int timecnt = ReadBigEndianInt32 (buffer, 32);
1271 int typecnt = ReadBigEndianInt32 (buffer, 36);
1272 int charcnt = ReadBigEndianInt32 (buffer, 40);
1274 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
1275 throw new InvalidTimeZoneException ();
1277 Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
1278 Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
1279 List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
1281 if (time_types.Count == 0)
1282 throw new InvalidTimeZoneException ();
1284 if (time_types.Count == 1 && time_types[0].IsDst)
1285 throw new InvalidTimeZoneException ();
1287 TimeSpan baseUtcOffset = new TimeSpan (0);
1288 TimeSpan dstDelta = new TimeSpan (0);
1289 string standardDisplayName = null;
1290 string daylightDisplayName = null;
1291 bool dst_observed = false;
1292 DateTime dst_start = DateTime.MinValue;
1293 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
1294 bool storeTransition = false;
1296 for (int i = 0; i < transitions.Count; i++) {
1297 var pair = transitions [i];
1298 DateTime ttime = pair.Key;
1299 TimeType ttype = pair.Value;
1301 if (standardDisplayName != ttype.Name)
1302 standardDisplayName = ttype.Name;
1303 if (baseUtcOffset.TotalSeconds != ttype.Offset) {
1304 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
1305 if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions.
1306 storeTransition = true;
1307 adjustmentRules = new List<AdjustmentRule> ();
1308 dst_observed = false;
1311 //FIXME: check additional fields for this:
1312 //most of the transitions are expressed in GMT
1313 dst_start += baseUtcOffset;
1314 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
1316 //some weird timezone (America/Phoenix) have end dates on Jan 1st
1317 if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
1318 dst_end -= new TimeSpan (24, 0, 0);
1321 * AdjustmentRule specifies a DST period that starts and ends within a year.
1322 * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
1323 * Thus we fallback to the transitions.
1325 if (dst_start.AddYears (1) < dst_end)
1326 storeTransition = true;
1328 DateTime dateStart, dateEnd;
1329 if (dst_start.Month < 7)
1330 dateStart = new DateTime (dst_start.Year, 1, 1);
1332 dateStart = new DateTime (dst_start.Year, 7, 1);
1334 if (dst_end.Month >= 7)
1335 dateEnd = new DateTime (dst_end.Year, 12, 31);
1337 dateEnd = new DateTime (dst_end.Year, 6, 30);
1340 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
1341 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
1342 if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
1343 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
1345 dst_observed = false;
1347 if (daylightDisplayName != ttype.Name)
1348 daylightDisplayName = ttype.Name;
1349 if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
1350 // Round to nearest minute, since it's not possible to create an adjustment rule
1351 // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
1352 // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
1353 dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset;
1354 if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0)
1355 dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f));
1359 dst_observed = true;
1364 if (adjustmentRules.Count == 0 && !storeTransition) {
1365 if (standardDisplayName == null) {
1366 var t = time_types [0];
1367 standardDisplayName = t.Name;
1368 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
1370 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
1372 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
1375 if (storeTransition && transitions.Count > 0) {
1376 tz.transitions = transitions;
1377 tz.supportsDaylightSavingTime = true;
1383 static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
1385 var abbrevs = new Dictionary<int, string> ();
1386 int abbrev_index = 0;
1387 var sb = new StringBuilder ();
1388 for (int i = 0; i < count; i++) {
1389 char c = (char) buffer [index + i];
1393 abbrevs.Add (abbrev_index, sb.ToString ());
1394 //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
1395 //j == sb.Length empty substring also needs to be added #31432
1396 for (int j = 1; j <= sb.Length; j++)
1397 abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
1398 abbrev_index = i + 1;
1399 sb = new StringBuilder ();
1405 static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
1407 var types = new Dictionary<int, TimeType> (count);
1408 for (int i = 0; i < count; i++) {
1409 int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
1412 // The official tz database contains timezone with GMT offsets
1413 // not only in whole hours/minutes but in seconds. This happens for years
1414 // before 1901. For example
1416 // NAME GMTOFF RULES FORMAT UNTIL
1417 // Europe/Madrid -0:14:44 - LMT 1901 Jan 1 0:00s
1419 // .NET as of 4.6.2 cannot handle that and uses hours/minutes only, so
1420 // we remove seconds to not crash later
1422 offset = (offset / 60) * 60;
1424 byte is_dst = buffer [index + 6 * i + 4];
1425 byte abbrev = buffer [index + 6 * i + 5];
1426 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
1431 static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
1433 var list = new List<KeyValuePair<DateTime, TimeType>> (count);
1434 for (int i = 0; i < count; i++) {
1435 int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
1436 DateTime ttime = DateTimeFromUnixTime (unixtime);
1437 byte ttype = buffer [index + 4 * count + i];
1438 list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
1443 static DateTime DateTimeFromUnixTime (long unix_time)
1445 DateTime date_time = new DateTime (1970, 1, 1);
1446 return date_time.AddSeconds (unix_time);
1449 #region reference sources
1450 // Shortcut for TimeZoneInfo.Local.GetUtcOffset
1451 internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1454 return Local.GetUtcOffset (dateTime, out dst);
1457 internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1460 return GetUtcOffset (dateTime, out dst);
1463 static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
1465 isDaylightSavings = false;
1466 isAmbiguousLocalDst = false;
1467 TimeSpan baseOffset = zone.BaseUtcOffset;
1469 if (zone.IsAmbiguousTime (time)) {
1470 isAmbiguousLocalDst = true;
1474 return zone.GetUtcOffset (time, out isDaylightSavings);
1480 public readonly int Offset;
1481 public readonly bool IsDst;
1484 public TimeType (int offset, bool is_dst, string abbrev)
1486 this.Offset = offset;
1487 this.IsDst = is_dst;
1491 public override string ToString ()
1493 return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;