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])) Iend--;
300 return str.Substring (Istart, Iend-Istart+1);
304 static RegistryKey timeZoneKey;
305 static RegistryKey TimeZoneKey {
307 if (timeZoneKey != null)
312 return timeZoneKey = Registry.LocalMachine.OpenSubKey (
313 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
318 static RegistryKey localZoneKey;
319 static RegistryKey LocalZoneKey {
321 if (localZoneKey != null)
327 return localZoneKey = Registry.LocalMachine.OpenSubKey (
328 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", false);
334 private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified)
336 var resultTicks = date.Ticks + ticks;
337 if (resultTicks < DateTime.MinValue.Ticks) {
338 result = DateTime.SpecifyKind (DateTime.MinValue, kind);
342 if (resultTicks > DateTime.MaxValue.Ticks) {
343 result = DateTime.SpecifyKind (DateTime.MaxValue, kind);
347 result = new DateTime (resultTicks, kind);
351 public static void ClearCachedData ()
355 systemTimeZones = null;
358 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
360 return ConvertTime (dateTime, dateTime.Kind == DateTimeKind.Utc ? TimeZoneInfo.Utc : TimeZoneInfo.Local, destinationTimeZone);
363 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
365 if (sourceTimeZone == null)
366 throw new ArgumentNullException ("sourceTimeZone");
368 if (destinationTimeZone == null)
369 throw new ArgumentNullException ("destinationTimeZone");
371 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
372 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
374 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
375 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
377 if (sourceTimeZone.IsInvalidTime (dateTime))
378 throw new ArgumentException ("dateTime parameter is an invalid time");
380 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
383 DateTime utc = ConvertTimeToUtc (dateTime, sourceTimeZone);
385 if (destinationTimeZone != TimeZoneInfo.Utc) {
386 utc = ConvertTimeFromUtc (utc, destinationTimeZone);
387 if (dateTime.Kind == DateTimeKind.Unspecified)
388 return DateTime.SpecifyKind (utc, DateTimeKind.Unspecified);
394 public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
396 if (destinationTimeZone == null)
397 throw new ArgumentNullException("destinationTimeZone");
399 var utcDateTime = dateTimeOffset.UtcDateTime;
402 var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst);
404 return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset);
407 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
409 return ConvertTime (dateTime, FindSystemTimeZoneById (destinationTimeZoneId));
412 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
414 return ConvertTime (dateTime, FindSystemTimeZoneById (sourceTimeZoneId), FindSystemTimeZoneById (destinationTimeZoneId));
417 public static DateTimeOffset ConvertTimeBySystemTimeZoneId (DateTimeOffset dateTimeOffset, string destinationTimeZoneId)
419 return ConvertTime (dateTimeOffset, FindSystemTimeZoneById (destinationTimeZoneId));
422 private DateTime ConvertTimeFromUtc (DateTime dateTime)
424 if (dateTime.Kind == DateTimeKind.Local)
425 throw new ArgumentException ("Kind property of dateTime is Local");
427 if (this == TimeZoneInfo.Utc)
428 return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
430 var utcOffset = GetUtcOffset (dateTime);
432 var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified;
435 if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind))
436 return DateTime.SpecifyKind (DateTime.MaxValue, kind);
441 public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
443 if (destinationTimeZone == null)
444 throw new ArgumentNullException ("destinationTimeZone");
446 return destinationTimeZone.ConvertTimeFromUtc (dateTime);
449 public static DateTime ConvertTimeToUtc (DateTime dateTime)
451 if (dateTime.Kind == DateTimeKind.Utc)
454 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local);
457 static internal DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
459 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local, flags);
462 public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
464 return ConvertTimeToUtc (dateTime, sourceTimeZone, TimeZoneInfoOptions.None);
467 static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfoOptions flags)
469 if ((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) {
470 if (sourceTimeZone == null)
471 throw new ArgumentNullException ("sourceTimeZone");
473 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
474 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
476 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
477 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
479 if (sourceTimeZone.IsInvalidTime (dateTime))
480 throw new ArgumentException ("dateTime parameter is an invalid time");
483 if (dateTime.Kind == DateTimeKind.Utc)
487 var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst);
489 DateTime utcDateTime;
490 TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc);
494 static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
496 bool isDaylightSavings;
497 return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
500 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
502 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, null, null, true);
505 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules)
507 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, false);
510 public static TimeZoneInfo CreateCustomTimeZone ( string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
512 return new TimeZoneInfo (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, disableDaylightSavingTime);
515 public override bool Equals (object obj)
517 return Equals (obj as TimeZoneInfo);
520 public bool Equals (TimeZoneInfo other)
525 return other.Id == this.Id && HasSameRules (other);
528 public static TimeZoneInfo FindSystemTimeZoneById (string id)
530 //FIXME: this method should check for cached values in systemTimeZones
532 throw new ArgumentNullException ("id");
534 if (TimeZoneKey != null)
536 if (id == "Coordinated Universal Time")
537 id = "UTC"; //windows xp exception for "StandardName" property
538 RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
540 throw new TimeZoneNotFoundException ();
541 return FromRegistryKey(id, key);
544 // Local requires special logic that already exists in the Local property (bug #326)
548 return FindSystemTimeZoneByIdCore (id);
552 private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
554 if (!File.Exists (filepath))
555 throw new TimeZoneNotFoundException ();
557 using (FileStream stream = File.OpenRead (filepath)) {
558 return BuildFromStream (id, stream);
564 private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key)
566 byte [] reg_tzi = (byte []) key.GetValue ("TZI");
569 throw new InvalidTimeZoneException ();
571 int bias = BitConverter.ToInt32 (reg_tzi, 0);
572 TimeSpan baseUtcOffset = new TimeSpan (0, -bias, 0);
574 string display_name = (string) key.GetValue ("Display");
575 string standard_name = (string) key.GetValue ("Std");
576 string daylight_name = (string) key.GetValue ("Dlt");
578 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
580 RegistryKey dst_key = key.OpenSubKey ("Dynamic DST", false);
581 if (dst_key != null) {
582 int first_year = (int) dst_key.GetValue ("FirstEntry");
583 int last_year = (int) dst_key.GetValue ("LastEntry");
586 for (year=first_year; year<=last_year; year++) {
587 byte [] dst_tzi = (byte []) dst_key.GetValue (year.ToString ());
588 if (dst_tzi != null) {
589 int start_year = year == first_year ? 1 : year;
590 int end_year = year == last_year ? 9999 : year;
591 ParseRegTzi(adjustmentRules, start_year, end_year, dst_tzi);
596 ParseRegTzi(adjustmentRules, 1, 9999, reg_tzi);
598 return CreateCustomTimeZone (id, baseUtcOffset, display_name, standard_name, daylight_name, ValidateRules (adjustmentRules).ToArray ());
601 private static void ParseRegTzi (List<AdjustmentRule> adjustmentRules, int start_year, int end_year, byte [] buffer)
603 //int standard_bias = BitConverter.ToInt32 (buffer, 4); /* not sure how to handle this */
604 int daylight_bias = BitConverter.ToInt32 (buffer, 8);
606 int standard_year = BitConverter.ToInt16 (buffer, 12);
607 int standard_month = BitConverter.ToInt16 (buffer, 14);
608 int standard_dayofweek = BitConverter.ToInt16 (buffer, 16);
609 int standard_day = BitConverter.ToInt16 (buffer, 18);
610 int standard_hour = BitConverter.ToInt16 (buffer, 20);
611 int standard_minute = BitConverter.ToInt16 (buffer, 22);
612 int standard_second = BitConverter.ToInt16 (buffer, 24);
613 int standard_millisecond = BitConverter.ToInt16 (buffer, 26);
615 int daylight_year = BitConverter.ToInt16 (buffer, 28);
616 int daylight_month = BitConverter.ToInt16 (buffer, 30);
617 int daylight_dayofweek = BitConverter.ToInt16 (buffer, 32);
618 int daylight_day = BitConverter.ToInt16 (buffer, 34);
619 int daylight_hour = BitConverter.ToInt16 (buffer, 36);
620 int daylight_minute = BitConverter.ToInt16 (buffer, 38);
621 int daylight_second = BitConverter.ToInt16 (buffer, 40);
622 int daylight_millisecond = BitConverter.ToInt16 (buffer, 42);
624 if (standard_month == 0 || daylight_month == 0)
628 DateTime start_timeofday = new DateTime (1, 1, 1, daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
629 TransitionTime start_transition_time;
631 if (daylight_year == 0) {
632 start_date = new DateTime (start_year, 1, 1);
633 start_transition_time = TransitionTime.CreateFloatingDateRule (
634 start_timeofday, daylight_month, daylight_day,
635 (DayOfWeek) daylight_dayofweek);
638 start_date = new DateTime (daylight_year, daylight_month, daylight_day,
639 daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
640 start_transition_time = TransitionTime.CreateFixedDateRule (
641 start_timeofday, daylight_month, daylight_day);
645 DateTime end_timeofday = new DateTime (1, 1, 1, standard_hour, standard_minute, standard_second, standard_millisecond);
646 TransitionTime end_transition_time;
648 if (standard_year == 0) {
649 end_date = new DateTime (end_year, 12, 31);
650 end_transition_time = TransitionTime.CreateFloatingDateRule (
651 end_timeofday, standard_month, standard_day,
652 (DayOfWeek) standard_dayofweek);
655 end_date = new DateTime (standard_year, standard_month, standard_day,
656 standard_hour, standard_minute, standard_second, standard_millisecond);
657 end_transition_time = TransitionTime.CreateFixedDateRule (
658 end_timeofday, standard_month, standard_day);
661 TimeSpan daylight_delta = new TimeSpan(0, -daylight_bias, 0);
663 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (
664 start_date, end_date, daylight_delta,
665 start_transition_time, end_transition_time));
669 public AdjustmentRule [] GetAdjustmentRules ()
671 if (!supportsDaylightSavingTime || adjustmentRules == null)
672 return new AdjustmentRule [0];
674 return (AdjustmentRule []) adjustmentRules.Clone ();
677 public TimeSpan [] GetAmbiguousTimeOffsets (DateTime dateTime)
679 if (!IsAmbiguousTime (dateTime))
680 throw new ArgumentException ("dateTime is not an ambiguous time");
682 AdjustmentRule rule = GetApplicableRule (dateTime);
684 return new TimeSpan[] {baseUtcOffset, baseUtcOffset + rule.DaylightDelta};
686 return new TimeSpan[] {baseUtcOffset, baseUtcOffset};
689 public TimeSpan [] GetAmbiguousTimeOffsets (DateTimeOffset dateTimeOffset)
691 if (!IsAmbiguousTime (dateTimeOffset))
692 throw new ArgumentException ("dateTimeOffset is not an ambiguous time");
694 throw new NotImplementedException ();
697 public override int GetHashCode ()
699 int hash_code = Id.GetHashCode ();
700 foreach (AdjustmentRule rule in GetAdjustmentRules ())
701 hash_code ^= rule.GetHashCode ();
705 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
708 throw new ArgumentNullException ("info");
709 info.AddValue ("Id", id);
710 info.AddValue ("DisplayName", displayName);
711 info.AddValue ("StandardName", standardDisplayName);
712 info.AddValue ("DaylightName", daylightDisplayName);
713 info.AddValue ("BaseUtcOffset", baseUtcOffset);
714 info.AddValue ("AdjustmentRules", adjustmentRules);
715 info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime);
718 static ReadOnlyCollection<TimeZoneInfo> systemTimeZones;
720 public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones ()
722 if (systemTimeZones == null) {
723 var tz = new List<TimeZoneInfo> ();
724 GetSystemTimeZonesCore (tz);
725 Interlocked.CompareExchange (ref systemTimeZones, new ReadOnlyCollection<TimeZoneInfo> (tz), null);
728 return systemTimeZones;
731 public TimeSpan GetUtcOffset (DateTime dateTime)
734 return GetUtcOffset (dateTime, out isDST);
737 public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
740 return GetUtcOffset (dateTimeOffset.UtcDateTime, out isDST);
743 private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
747 TimeZoneInfo tz = this;
748 if (dateTime.Kind == DateTimeKind.Utc)
749 tz = TimeZoneInfo.Utc;
751 if (dateTime.Kind == DateTimeKind.Local)
752 tz = TimeZoneInfo.Local;
755 var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
762 DateTime utcDateTime;
763 if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
764 return BaseUtcOffset;
766 return GetUtcOffsetHelper (utcDateTime, this, out isDST);
769 // This is an helper method used by the method above, do not use this on its own.
770 private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
772 if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
773 throw new Exception ();
777 if (tz == TimeZoneInfo.Utc)
778 return TimeSpan.Zero;
781 if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
784 if (dateTime.Kind == DateTimeKind.Utc) {
785 var utcRule = tz.GetApplicableRule (dateTime);
786 if (utcRule != null && tz.IsInDST (utcRule, dateTime)) {
788 return tz.BaseUtcOffset + utcRule.DaylightDelta;
791 return tz.BaseUtcOffset;
794 DateTime stdUtcDateTime;
795 if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc))
796 return tz.BaseUtcOffset;
798 var tzRule = tz.GetApplicableRule (stdUtcDateTime);
800 DateTime dstUtcDateTime = DateTime.MinValue;
801 if (tzRule != null) {
802 if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc))
803 return tz.BaseUtcOffset;
806 if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
808 return tz.BaseUtcOffset + tzRule.DaylightDelta;
811 return tz.BaseUtcOffset;
814 public bool HasSameRules (TimeZoneInfo other)
817 throw new ArgumentNullException ("other");
819 if ((this.adjustmentRules == null) != (other.adjustmentRules == null))
822 if (this.adjustmentRules == null)
825 if (this.BaseUtcOffset != other.BaseUtcOffset)
828 if (this.adjustmentRules.Length != other.adjustmentRules.Length)
831 for (int i = 0; i < adjustmentRules.Length; i++) {
832 if (! (this.adjustmentRules [i]).Equals (other.adjustmentRules [i]))
839 public bool IsAmbiguousTime (DateTime dateTime)
841 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
842 throw new ArgumentException ("Kind is Local and time is Invalid");
844 if (this == TimeZoneInfo.Utc)
847 if (dateTime.Kind == DateTimeKind.Utc)
848 dateTime = ConvertTimeFromUtc (dateTime);
850 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
851 dateTime = ConvertTime (dateTime, TimeZoneInfo.Local, this);
853 AdjustmentRule rule = GetApplicableRule (dateTime);
855 DateTime tpoint = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year);
856 if (dateTime > tpoint - rule.DaylightDelta && dateTime <= tpoint)
863 public bool IsAmbiguousTime (DateTimeOffset dateTimeOffset)
865 throw new NotImplementedException ();
868 private bool IsInDST (AdjustmentRule rule, DateTime dateTime)
870 // Check whether we're in the dateTime year's DST period
871 if (IsInDSTForYear (rule, dateTime, dateTime.Year))
874 // We might be in the dateTime previous year's DST period
875 return IsInDSTForYear (rule, dateTime, dateTime.Year - 1);
878 bool IsInDSTForYear (AdjustmentRule rule, DateTime dateTime, int year)
880 DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, year);
881 DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
882 if (dateTime.Kind == DateTimeKind.Utc) {
883 DST_start -= BaseUtcOffset;
884 DST_end -= (BaseUtcOffset + rule.DaylightDelta);
887 return (dateTime >= DST_start && dateTime < DST_end);
890 public bool IsDaylightSavingTime (DateTime dateTime)
892 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
893 throw new ArgumentException ("dateTime is invalid and Kind is Local");
895 if (this == TimeZoneInfo.Utc)
898 if (!SupportsDaylightSavingTime)
902 GetUtcOffset (dateTime, out isDst);
907 internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags)
909 return IsDaylightSavingTime (dateTime);
912 public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
914 throw new NotImplementedException ();
917 internal DaylightTime GetDaylightChanges (int year)
919 DateTime start = DateTime.MinValue, end = DateTime.MinValue;
920 TimeSpan delta = new TimeSpan ();
922 if (transitions != null) {
923 end = DateTime.MaxValue;
924 for (var i = transitions.Count - 1; i >= 0; i--) {
925 var pair = transitions [i];
926 DateTime ttime = pair.Key;
927 TimeType ttype = pair.Value;
929 if (ttime.Year > year)
931 if (ttime.Year < year)
935 // DaylightTime.Delta is relative to the current BaseUtcOffset.
936 delta = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset;
943 // DaylightTime.Start is relative to the Standard time.
944 if (!TryAddTicks (start, BaseUtcOffset.Ticks, out start))
945 start = DateTime.MinValue;
947 // DaylightTime.End is relative to the DST time.
948 if (!TryAddTicks (end, BaseUtcOffset.Ticks + delta.Ticks, out end))
949 end = DateTime.MinValue;
951 AdjustmentRule first = null, last = null;
953 foreach (var rule in GetAdjustmentRules ()) {
954 if (rule.DateStart.Year != year && rule.DateEnd.Year != year)
956 if (rule.DateStart.Year == year)
958 if (rule.DateEnd.Year == year)
962 if (first == null || last == null)
963 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
965 start = TransitionPoint (first.DaylightTransitionStart, year);
966 end = TransitionPoint (last.DaylightTransitionEnd, year);
967 delta = first.DaylightDelta;
970 if (start == DateTime.MinValue || end == DateTime.MinValue)
971 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
973 return new DaylightTime (start, end, delta);
976 public bool IsInvalidTime (DateTime dateTime)
978 if (dateTime.Kind == DateTimeKind.Utc)
980 if (dateTime.Kind == DateTimeKind.Local && this != Local)
983 AdjustmentRule rule = GetApplicableRule (dateTime);
985 DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
986 if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
993 void IDeserializationCallback.OnDeserialization (object sender)
996 TimeZoneInfo.Validate (id, baseUtcOffset, adjustmentRules);
997 } catch (ArgumentException ex) {
998 throw new SerializationException ("invalid serialization data", ex);
1002 private static void Validate (string id, TimeSpan baseUtcOffset, AdjustmentRule [] adjustmentRules)
1005 throw new ArgumentNullException ("id");
1007 if (id == String.Empty)
1008 throw new ArgumentException ("id parameter is an empty string");
1010 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1011 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1013 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1014 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1018 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1021 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1022 AdjustmentRule prev = null;
1023 foreach (AdjustmentRule current in adjustmentRules) {
1024 if (current == null)
1025 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1027 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1028 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1029 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;");
1031 if (prev != null && prev.DateStart > current.DateStart)
1032 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1034 if (prev != null && prev.DateEnd > current.DateStart)
1035 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1037 if (prev != null && prev.DateEnd == current.DateStart)
1038 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1045 public override string ToString ()
1050 private TimeZoneInfo (SerializationInfo info, StreamingContext context)
1053 throw new ArgumentNullException ("info");
1054 id = (string) info.GetValue ("Id", typeof (string));
1055 displayName = (string) info.GetValue ("DisplayName", typeof (string));
1056 standardDisplayName = (string) info.GetValue ("StandardName", typeof (string));
1057 daylightDisplayName = (string) info.GetValue ("DaylightName", typeof (string));
1058 baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", typeof (TimeSpan));
1059 adjustmentRules = (TimeZoneInfo.AdjustmentRule []) info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
1060 supportsDaylightSavingTime = (bool) info.GetValue ("SupportsDaylightSavingTime", typeof (bool));
1063 private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
1066 throw new ArgumentNullException ("id");
1068 if (id == String.Empty)
1069 throw new ArgumentException ("id parameter is an empty string");
1071 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1072 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1074 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1075 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1079 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1082 bool supportsDaylightSavingTime = !disableDaylightSavingTime;
1084 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1085 AdjustmentRule prev = null;
1086 foreach (AdjustmentRule current in adjustmentRules) {
1087 if (current == null)
1088 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1090 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1091 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1092 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;");
1094 if (prev != null && prev.DateStart > current.DateStart)
1095 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1097 if (prev != null && prev.DateEnd > current.DateStart)
1098 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1100 if (prev != null && prev.DateEnd == current.DateStart)
1101 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1106 supportsDaylightSavingTime = false;
1110 this.baseUtcOffset = baseUtcOffset;
1111 this.displayName = displayName ?? id;
1112 this.standardDisplayName = standardDisplayName ?? id;
1113 this.daylightDisplayName = daylightDisplayName;
1114 this.supportsDaylightSavingTime = supportsDaylightSavingTime;
1115 this.adjustmentRules = adjustmentRules;
1118 private AdjustmentRule GetApplicableRule (DateTime dateTime)
1120 //Applicable rules are in standard time
1121 DateTime date = dateTime;
1123 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1124 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date))
1126 } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) {
1127 if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date))
1131 // get the date component of the datetime
1134 if (adjustmentRules != null) {
1135 foreach (AdjustmentRule rule in adjustmentRules) {
1136 if (rule.DateStart > date)
1138 if (rule.DateEnd < date)
1146 private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out bool isDst)
1148 offset = BaseUtcOffset;
1151 if (transitions == null)
1154 //Transitions are in UTC
1155 DateTime date = dateTime;
1157 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1158 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1162 if (dateTime.Kind != DateTimeKind.Utc) {
1163 if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1167 for (var i = transitions.Count - 1; i >= 0; i--) {
1168 var pair = transitions [i];
1169 DateTime ttime = pair.Key;
1170 TimeType ttype = pair.Value;
1175 offset = new TimeSpan (0, 0, ttype.Offset);
1176 isDst = ttype.IsDst;
1184 private static DateTime TransitionPoint (TransitionTime transition, int year)
1186 if (transition.IsFixedDateRule)
1187 return new DateTime (year, transition.Month, transition.Day) + transition.TimeOfDay.TimeOfDay;
1189 DayOfWeek first = (new DateTime (year, transition.Month, 1)).DayOfWeek;
1190 int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first + 7) % 7;
1191 if (day > DateTime.DaysInMonth (year, transition.Month))
1195 return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
1198 static List<AdjustmentRule> ValidateRules (List<AdjustmentRule> adjustmentRules)
1200 AdjustmentRule prev = null;
1201 foreach (AdjustmentRule current in adjustmentRules.ToArray ()) {
1202 if (prev != null && prev.DateEnd > current.DateStart) {
1203 adjustmentRules.Remove (current);
1207 return adjustmentRules;
1210 #if LIBC || MONOTOUCH
1211 const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k)
1213 private static TimeZoneInfo BuildFromStream (string id, Stream stream)
1215 byte [] buffer = new byte [BUFFER_SIZE];
1216 int length = stream.Read (buffer, 0, BUFFER_SIZE);
1218 if (!ValidTZFile (buffer, length))
1219 throw new InvalidTimeZoneException ("TZ file too big for the buffer");
1222 return ParseTZBuffer (id, buffer, length);
1223 } catch (Exception e) {
1224 throw new InvalidTimeZoneException (e.Message);
1228 private static bool ValidTZFile (byte [] buffer, int length)
1230 StringBuilder magic = new StringBuilder ();
1232 for (int i = 0; i < 4; i++)
1233 magic.Append ((char)buffer [i]);
1235 if (magic.ToString () != "TZif")
1238 if (length >= BUFFER_SIZE)
1244 static int SwapInt32 (int i)
1246 return (((i >> 24) & 0xff)
1247 | ((i >> 8) & 0xff00)
1248 | ((i << 8) & 0xff0000)
1249 | (((i & 0xff) << 24)));
1252 static int ReadBigEndianInt32 (byte [] buffer, int start)
1254 int i = BitConverter.ToInt32 (buffer, start);
1255 if (!BitConverter.IsLittleEndian)
1258 return SwapInt32 (i);
1261 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
1263 //Reading the header. 4 bytes for magic, 16 are reserved
1264 int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
1265 int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
1266 int leapcnt = ReadBigEndianInt32 (buffer, 28);
1267 int timecnt = ReadBigEndianInt32 (buffer, 32);
1268 int typecnt = ReadBigEndianInt32 (buffer, 36);
1269 int charcnt = ReadBigEndianInt32 (buffer, 40);
1271 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
1272 throw new InvalidTimeZoneException ();
1274 Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
1275 Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
1276 List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
1278 if (time_types.Count == 0)
1279 throw new InvalidTimeZoneException ();
1281 if (time_types.Count == 1 && ((TimeType)time_types[0]).IsDst)
1282 throw new InvalidTimeZoneException ();
1284 TimeSpan baseUtcOffset = new TimeSpan (0);
1285 TimeSpan dstDelta = new TimeSpan (0);
1286 string standardDisplayName = null;
1287 string daylightDisplayName = null;
1288 bool dst_observed = false;
1289 DateTime dst_start = DateTime.MinValue;
1290 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
1291 bool storeTransition = false;
1293 for (int i = 0; i < transitions.Count; i++) {
1294 var pair = transitions [i];
1295 DateTime ttime = pair.Key;
1296 TimeType ttype = pair.Value;
1298 if (standardDisplayName != ttype.Name)
1299 standardDisplayName = ttype.Name;
1300 if (baseUtcOffset.TotalSeconds != ttype.Offset) {
1301 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
1302 if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions.
1303 storeTransition = true;
1304 adjustmentRules = new List<AdjustmentRule> ();
1305 dst_observed = false;
1308 //FIXME: check additional fields for this:
1309 //most of the transitions are expressed in GMT
1310 dst_start += baseUtcOffset;
1311 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
1313 //some weird timezone (America/Phoenix) have end dates on Jan 1st
1314 if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
1315 dst_end -= new TimeSpan (24, 0, 0);
1318 * AdjustmentRule specifies a DST period that starts and ends within a year.
1319 * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
1320 * Thus we fallback to the transitions.
1322 if (dst_start.AddYears (1) < dst_end)
1323 storeTransition = true;
1325 DateTime dateStart, dateEnd;
1326 if (dst_start.Month < 7)
1327 dateStart = new DateTime (dst_start.Year, 1, 1);
1329 dateStart = new DateTime (dst_start.Year, 7, 1);
1331 if (dst_end.Month >= 7)
1332 dateEnd = new DateTime (dst_end.Year, 12, 31);
1334 dateEnd = new DateTime (dst_end.Year, 6, 30);
1337 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
1338 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
1339 if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
1340 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
1342 dst_observed = false;
1344 if (daylightDisplayName != ttype.Name)
1345 daylightDisplayName = ttype.Name;
1346 if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
1347 // Round to nearest minute, since it's not possible to create an adjustment rule
1348 // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
1349 // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
1350 dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset;
1351 if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0)
1352 dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f));
1356 dst_observed = true;
1361 if (adjustmentRules.Count == 0 && !storeTransition) {
1362 TimeType t = (TimeType)time_types [0];
1363 if (standardDisplayName == null) {
1364 standardDisplayName = t.Name;
1365 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
1367 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
1369 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
1372 if (storeTransition && transitions.Count > 0) {
1373 tz.transitions = transitions;
1374 tz.supportsDaylightSavingTime = true;
1380 static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
1382 var abbrevs = new Dictionary<int, string> ();
1383 int abbrev_index = 0;
1384 var sb = new StringBuilder ();
1385 for (int i = 0; i < count; i++) {
1386 char c = (char) buffer [index + i];
1390 abbrevs.Add (abbrev_index, sb.ToString ());
1391 //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
1392 //j == sb.Length empty substring also needs to be added #31432
1393 for (int j = 1; j <= sb.Length; j++)
1394 abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
1395 abbrev_index = i + 1;
1396 sb = new StringBuilder ();
1402 static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
1404 var types = new Dictionary<int, TimeType> (count);
1405 for (int i = 0; i < count; i++) {
1406 int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
1407 byte is_dst = buffer [index + 6 * i + 4];
1408 byte abbrev = buffer [index + 6 * i + 5];
1409 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
1414 static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
1416 var list = new List<KeyValuePair<DateTime, TimeType>> (count);
1417 for (int i = 0; i < count; i++) {
1418 int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
1419 DateTime ttime = DateTimeFromUnixTime (unixtime);
1420 byte ttype = buffer [index + 4 * count + i];
1421 list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
1426 static DateTime DateTimeFromUnixTime (long unix_time)
1428 DateTime date_time = new DateTime (1970, 1, 1);
1429 return date_time.AddSeconds (unix_time);
1432 #region reference sources
1433 // Shortcut for TimeZoneInfo.Local.GetUtcOffset
1434 internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1437 return Local.GetUtcOffset (dateTime, out dst);
1440 internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1443 return GetUtcOffset (dateTime, out dst);
1446 static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
1448 isDaylightSavings = false;
1449 isAmbiguousLocalDst = false;
1450 TimeSpan baseOffset = zone.BaseUtcOffset;
1452 if (zone.IsAmbiguousTime (time)) {
1453 isAmbiguousLocalDst = true;
1457 return zone.GetUtcOffset (time, out isDaylightSavings);
1463 public readonly int Offset;
1464 public readonly bool IsDst;
1467 public TimeType (int offset, bool is_dst, string abbrev)
1469 this.Offset = offset;
1470 this.IsDst = is_dst;
1474 public override string ToString ()
1476 return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;