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 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 throw new NotImplementedException ();
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 (Exception e) {
1225 throw new InvalidTimeZoneException (e.Message);
1229 private static bool ValidTZFile (byte [] buffer, int length)
1231 StringBuilder magic = new StringBuilder ();
1233 for (int i = 0; i < 4; i++)
1234 magic.Append ((char)buffer [i]);
1236 if (magic.ToString () != "TZif")
1239 if (length >= BUFFER_SIZE)
1245 static int SwapInt32 (int i)
1247 return (((i >> 24) & 0xff)
1248 | ((i >> 8) & 0xff00)
1249 | ((i << 8) & 0xff0000)
1250 | (((i & 0xff) << 24)));
1253 static int ReadBigEndianInt32 (byte [] buffer, int start)
1255 int i = BitConverter.ToInt32 (buffer, start);
1256 if (!BitConverter.IsLittleEndian)
1259 return SwapInt32 (i);
1262 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
1264 //Reading the header. 4 bytes for magic, 16 are reserved
1265 int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
1266 int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
1267 int leapcnt = ReadBigEndianInt32 (buffer, 28);
1268 int timecnt = ReadBigEndianInt32 (buffer, 32);
1269 int typecnt = ReadBigEndianInt32 (buffer, 36);
1270 int charcnt = ReadBigEndianInt32 (buffer, 40);
1272 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
1273 throw new InvalidTimeZoneException ();
1275 Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
1276 Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
1277 List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
1279 if (time_types.Count == 0)
1280 throw new InvalidTimeZoneException ();
1282 if (time_types.Count == 1 && ((TimeType)time_types[0]).IsDst)
1283 throw new InvalidTimeZoneException ();
1285 TimeSpan baseUtcOffset = new TimeSpan (0);
1286 TimeSpan dstDelta = new TimeSpan (0);
1287 string standardDisplayName = null;
1288 string daylightDisplayName = null;
1289 bool dst_observed = false;
1290 DateTime dst_start = DateTime.MinValue;
1291 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
1292 bool storeTransition = false;
1294 for (int i = 0; i < transitions.Count; i++) {
1295 var pair = transitions [i];
1296 DateTime ttime = pair.Key;
1297 TimeType ttype = pair.Value;
1299 if (standardDisplayName != ttype.Name)
1300 standardDisplayName = ttype.Name;
1301 if (baseUtcOffset.TotalSeconds != ttype.Offset) {
1302 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
1303 if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions.
1304 storeTransition = true;
1305 adjustmentRules = new List<AdjustmentRule> ();
1306 dst_observed = false;
1309 //FIXME: check additional fields for this:
1310 //most of the transitions are expressed in GMT
1311 dst_start += baseUtcOffset;
1312 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
1314 //some weird timezone (America/Phoenix) have end dates on Jan 1st
1315 if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
1316 dst_end -= new TimeSpan (24, 0, 0);
1319 * AdjustmentRule specifies a DST period that starts and ends within a year.
1320 * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
1321 * Thus we fallback to the transitions.
1323 if (dst_start.AddYears (1) < dst_end)
1324 storeTransition = true;
1326 DateTime dateStart, dateEnd;
1327 if (dst_start.Month < 7)
1328 dateStart = new DateTime (dst_start.Year, 1, 1);
1330 dateStart = new DateTime (dst_start.Year, 7, 1);
1332 if (dst_end.Month >= 7)
1333 dateEnd = new DateTime (dst_end.Year, 12, 31);
1335 dateEnd = new DateTime (dst_end.Year, 6, 30);
1338 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
1339 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
1340 if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
1341 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
1343 dst_observed = false;
1345 if (daylightDisplayName != ttype.Name)
1346 daylightDisplayName = ttype.Name;
1347 if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
1348 // Round to nearest minute, since it's not possible to create an adjustment rule
1349 // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
1350 // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
1351 dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset;
1352 if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0)
1353 dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f));
1357 dst_observed = true;
1362 if (adjustmentRules.Count == 0 && !storeTransition) {
1363 TimeType t = (TimeType)time_types [0];
1364 if (standardDisplayName == null) {
1365 standardDisplayName = t.Name;
1366 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
1368 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
1370 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
1373 if (storeTransition && transitions.Count > 0) {
1374 tz.transitions = transitions;
1375 tz.supportsDaylightSavingTime = true;
1381 static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
1383 var abbrevs = new Dictionary<int, string> ();
1384 int abbrev_index = 0;
1385 var sb = new StringBuilder ();
1386 for (int i = 0; i < count; i++) {
1387 char c = (char) buffer [index + i];
1391 abbrevs.Add (abbrev_index, sb.ToString ());
1392 //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
1393 //j == sb.Length empty substring also needs to be added #31432
1394 for (int j = 1; j <= sb.Length; j++)
1395 abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
1396 abbrev_index = i + 1;
1397 sb = new StringBuilder ();
1403 static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
1405 var types = new Dictionary<int, TimeType> (count);
1406 for (int i = 0; i < count; i++) {
1407 int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
1408 byte is_dst = buffer [index + 6 * i + 4];
1409 byte abbrev = buffer [index + 6 * i + 5];
1410 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
1415 static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
1417 var list = new List<KeyValuePair<DateTime, TimeType>> (count);
1418 for (int i = 0; i < count; i++) {
1419 int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
1420 DateTime ttime = DateTimeFromUnixTime (unixtime);
1421 byte ttype = buffer [index + 4 * count + i];
1422 list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
1427 static DateTime DateTimeFromUnixTime (long unix_time)
1429 DateTime date_time = new DateTime (1970, 1, 1);
1430 return date_time.AddSeconds (unix_time);
1433 #region reference sources
1434 // Shortcut for TimeZoneInfo.Local.GetUtcOffset
1435 internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1438 return Local.GetUtcOffset (dateTime, out dst);
1441 internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1444 return GetUtcOffset (dateTime, out dst);
1447 static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
1449 isDaylightSavings = false;
1450 isAmbiguousLocalDst = false;
1451 TimeSpan baseOffset = zone.BaseUtcOffset;
1453 if (zone.IsAmbiguousTime (time)) {
1454 isAmbiguousLocalDst = true;
1458 return zone.GetUtcOffset (time, out isDaylightSavings);
1464 public readonly int Offset;
1465 public readonly bool IsDst;
1468 public TimeType (int offset, bool is_dst, string abbrev)
1470 this.Offset = offset;
1471 this.IsDst = is_dst;
1475 public override string ToString ()
1477 return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;