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 libcNotFound;
96 private static extern int readlink (string path, byte[] buffer, int buflen);
98 private static string readlink (string path)
103 byte[] buf = new byte [512];
107 ret = readlink (path, buf, buf.Length);
108 } catch (DllNotFoundException e) {
113 if (ret == -1) return null;
114 char[] cbuf = new char [512];
115 int chars = System.Text.Encoding.Default.GetChars (buf, 0, ret, cbuf, 0);
116 return new String (cbuf, 0, chars);
119 private static bool TryGetNameFromPath (string path, out string name)
122 var linkPath = readlink (path);
123 if (linkPath != null) {
124 if (Path.IsPathRooted(linkPath))
127 path = Path.Combine(Path.GetDirectoryName(path), linkPath);
130 path = Path.GetFullPath (path);
132 if (string.IsNullOrEmpty (TimeZoneDirectory))
135 var baseDir = TimeZoneDirectory;
136 if (baseDir [baseDir.Length-1] != Path.DirectorySeparatorChar)
137 baseDir += Path.DirectorySeparatorChar;
139 if (!path.StartsWith (baseDir, StringComparison.InvariantCulture))
142 name = path.Substring (baseDir.Length);
143 if (name == "localtime")
149 #if !MOBILE || MOBILE_STATIC
150 static TimeZoneInfo CreateLocal ()
153 if (IsWindows && LocalZoneKey != null) {
154 string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
156 name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp
157 name = TrimSpecial (name);
159 return TimeZoneInfo.FindSystemTimeZoneById (name);
163 var tz = Environment.GetEnvironmentVariable ("TZ");
165 if (tz == String.Empty)
168 return FindSystemTimeZoneByFileName (tz, Path.Combine (TimeZoneDirectory, tz));
174 var tzFilePaths = new string [] {
176 Path.Combine (TimeZoneDirectory, "localtime")};
178 foreach (var tzFilePath in tzFilePaths) {
180 string tzName = null;
181 if (!TryGetNameFromPath (tzFilePath, out tzName))
183 return FindSystemTimeZoneByFileName (tzName, tzFilePath);
184 } catch (TimeZoneNotFoundException) {
192 static TimeZoneInfo FindSystemTimeZoneByIdCore (string id)
195 string filepath = Path.Combine (TimeZoneDirectory, id);
196 return FindSystemTimeZoneByFileName (id, filepath);
198 throw new NotImplementedException ();
202 static void GetSystemTimeZonesCore (List<TimeZoneInfo> systemTimeZones)
205 if (TimeZoneKey != null) {
206 foreach (string id in TimeZoneKey.GetSubKeyNames ()) {
208 systemTimeZones.Add (FindSystemTimeZoneById (id));
217 string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"};
218 foreach (string continent in continents) {
220 foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) {
222 string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath));
223 systemTimeZones.Add (FindSystemTimeZoneById (id));
224 } catch (ArgumentNullException) {
225 } catch (TimeZoneNotFoundException) {
226 } catch (InvalidTimeZoneException) {
227 } catch (Exception) {
234 throw new NotImplementedException ("This method is not implemented for this platform");
239 string standardDisplayName;
240 public string StandardName {
241 get { return standardDisplayName; }
244 bool supportsDaylightSavingTime;
245 public bool SupportsDaylightSavingTime {
246 get { return supportsDaylightSavingTime; }
249 static TimeZoneInfo utc;
250 public static TimeZoneInfo Utc {
253 utc = CreateCustomTimeZone ("UTC", new TimeSpan (0), "UTC", "UTC");
258 static string timeZoneDirectory;
259 static string TimeZoneDirectory {
261 if (timeZoneDirectory == null)
262 timeZoneDirectory = "/usr/share/zoneinfo";
263 return timeZoneDirectory;
267 timeZoneDirectory = value;
271 private AdjustmentRule [] adjustmentRules;
273 #if !NET_2_1 || MOBILE_STATIC
275 /// Determine whether windows of not (taken Stephane Delcroix's code)
277 private static bool IsWindows
280 int platform = (int) Environment.OSVersion.Platform;
281 return ((platform != 4) && (platform != 6) && (platform != 128));
286 /// Needed to trim misc garbage in MS registry keys
288 private static string TrimSpecial (string str)
293 while (Istart < str.Length && !char.IsLetterOrDigit(str[Istart])) Istart++;
294 var Iend = str.Length - 1;
295 while (Iend > Istart && !char.IsLetterOrDigit(str[Iend])) Iend--;
297 return str.Substring (Istart, Iend-Istart+1);
301 static RegistryKey timeZoneKey;
302 static RegistryKey TimeZoneKey {
304 if (timeZoneKey != null)
309 return timeZoneKey = Registry.LocalMachine.OpenSubKey (
310 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
315 static RegistryKey localZoneKey;
316 static RegistryKey LocalZoneKey {
318 if (localZoneKey != null)
324 return localZoneKey = Registry.LocalMachine.OpenSubKey (
325 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", false);
331 private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified)
333 var resultTicks = date.Ticks + ticks;
334 if (resultTicks < DateTime.MinValue.Ticks) {
335 result = DateTime.SpecifyKind (DateTime.MinValue, kind);
339 if (resultTicks > DateTime.MaxValue.Ticks) {
340 result = DateTime.SpecifyKind (DateTime.MaxValue, kind);
344 result = new DateTime (resultTicks, kind);
348 public static void ClearCachedData ()
352 systemTimeZones = null;
355 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
357 return ConvertTime (dateTime, dateTime.Kind == DateTimeKind.Utc ? TimeZoneInfo.Utc : TimeZoneInfo.Local, destinationTimeZone);
360 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
362 if (sourceTimeZone == null)
363 throw new ArgumentNullException ("sourceTimeZone");
365 if (destinationTimeZone == null)
366 throw new ArgumentNullException ("destinationTimeZone");
368 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
369 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
371 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
372 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
374 if (sourceTimeZone.IsInvalidTime (dateTime))
375 throw new ArgumentException ("dateTime parameter is an invalid time");
377 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
380 DateTime utc = ConvertTimeToUtc (dateTime, sourceTimeZone);
382 if (destinationTimeZone != TimeZoneInfo.Utc) {
383 utc = ConvertTimeFromUtc (utc, destinationTimeZone);
384 if (dateTime.Kind == DateTimeKind.Unspecified)
385 return DateTime.SpecifyKind (utc, DateTimeKind.Unspecified);
391 public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
393 if (destinationTimeZone == null)
394 throw new ArgumentNullException("destinationTimeZone");
396 var utcDateTime = dateTimeOffset.UtcDateTime;
399 var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst);
401 return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset);
404 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
406 return ConvertTime (dateTime, FindSystemTimeZoneById (destinationTimeZoneId));
409 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
411 return ConvertTime (dateTime, FindSystemTimeZoneById (sourceTimeZoneId), FindSystemTimeZoneById (destinationTimeZoneId));
414 public static DateTimeOffset ConvertTimeBySystemTimeZoneId (DateTimeOffset dateTimeOffset, string destinationTimeZoneId)
416 return ConvertTime (dateTimeOffset, FindSystemTimeZoneById (destinationTimeZoneId));
419 private DateTime ConvertTimeFromUtc (DateTime dateTime)
421 if (dateTime.Kind == DateTimeKind.Local)
422 throw new ArgumentException ("Kind property of dateTime is Local");
424 if (this == TimeZoneInfo.Utc)
425 return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
427 var utcOffset = GetUtcOffset (dateTime);
429 var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified;
432 if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind))
433 return DateTime.SpecifyKind (DateTime.MaxValue, kind);
438 public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
440 if (destinationTimeZone == null)
441 throw new ArgumentNullException ("destinationTimeZone");
443 return destinationTimeZone.ConvertTimeFromUtc (dateTime);
446 public static DateTime ConvertTimeToUtc (DateTime dateTime)
448 if (dateTime.Kind == DateTimeKind.Utc)
451 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local);
454 static internal DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
456 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local, flags);
459 public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
461 return ConvertTimeToUtc (dateTime, sourceTimeZone, TimeZoneInfoOptions.None);
464 static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfoOptions flags)
466 if ((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) {
467 if (sourceTimeZone == null)
468 throw new ArgumentNullException ("sourceTimeZone");
470 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
471 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
473 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
474 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
476 if (sourceTimeZone.IsInvalidTime (dateTime))
477 throw new ArgumentException ("dateTime parameter is an invalid time");
480 if (dateTime.Kind == DateTimeKind.Utc)
484 var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst);
486 DateTime utcDateTime;
487 TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc);
491 static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
493 bool isDaylightSavings;
494 return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
497 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
499 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, null, null, true);
502 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules)
504 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, false);
507 public static TimeZoneInfo CreateCustomTimeZone ( string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
509 return new TimeZoneInfo (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, disableDaylightSavingTime);
512 public override bool Equals (object obj)
514 return Equals (obj as TimeZoneInfo);
517 public bool Equals (TimeZoneInfo other)
522 return other.Id == this.Id && HasSameRules (other);
525 public static TimeZoneInfo FindSystemTimeZoneById (string id)
527 //FIXME: this method should check for cached values in systemTimeZones
529 throw new ArgumentNullException ("id");
531 if (TimeZoneKey != null)
533 if (id == "Coordinated Universal Time")
534 id = "UTC"; //windows xp exception for "StandardName" property
535 RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
537 throw new TimeZoneNotFoundException ();
538 return FromRegistryKey(id, key);
541 // Local requires special logic that already exists in the Local property (bug #326)
545 return FindSystemTimeZoneByIdCore (id);
549 private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
551 if (!File.Exists (filepath))
552 throw new TimeZoneNotFoundException ();
554 using (FileStream stream = File.OpenRead (filepath)) {
555 return BuildFromStream (id, stream);
561 private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key)
563 byte [] reg_tzi = (byte []) key.GetValue ("TZI");
566 throw new InvalidTimeZoneException ();
568 int bias = BitConverter.ToInt32 (reg_tzi, 0);
569 TimeSpan baseUtcOffset = new TimeSpan (0, -bias, 0);
571 string display_name = (string) key.GetValue ("Display");
572 string standard_name = (string) key.GetValue ("Std");
573 string daylight_name = (string) key.GetValue ("Dlt");
575 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
577 RegistryKey dst_key = key.OpenSubKey ("Dynamic DST", false);
578 if (dst_key != null) {
579 int first_year = (int) dst_key.GetValue ("FirstEntry");
580 int last_year = (int) dst_key.GetValue ("LastEntry");
583 for (year=first_year; year<=last_year; year++) {
584 byte [] dst_tzi = (byte []) dst_key.GetValue (year.ToString ());
585 if (dst_tzi != null) {
586 int start_year = year == first_year ? 1 : year;
587 int end_year = year == last_year ? 9999 : year;
588 ParseRegTzi(adjustmentRules, start_year, end_year, dst_tzi);
593 ParseRegTzi(adjustmentRules, 1, 9999, reg_tzi);
595 return CreateCustomTimeZone (id, baseUtcOffset, display_name, standard_name, daylight_name, ValidateRules (adjustmentRules).ToArray ());
598 private static void ParseRegTzi (List<AdjustmentRule> adjustmentRules, int start_year, int end_year, byte [] buffer)
600 //int standard_bias = BitConverter.ToInt32 (buffer, 4); /* not sure how to handle this */
601 int daylight_bias = BitConverter.ToInt32 (buffer, 8);
603 int standard_year = BitConverter.ToInt16 (buffer, 12);
604 int standard_month = BitConverter.ToInt16 (buffer, 14);
605 int standard_dayofweek = BitConverter.ToInt16 (buffer, 16);
606 int standard_day = BitConverter.ToInt16 (buffer, 18);
607 int standard_hour = BitConverter.ToInt16 (buffer, 20);
608 int standard_minute = BitConverter.ToInt16 (buffer, 22);
609 int standard_second = BitConverter.ToInt16 (buffer, 24);
610 int standard_millisecond = BitConverter.ToInt16 (buffer, 26);
612 int daylight_year = BitConverter.ToInt16 (buffer, 28);
613 int daylight_month = BitConverter.ToInt16 (buffer, 30);
614 int daylight_dayofweek = BitConverter.ToInt16 (buffer, 32);
615 int daylight_day = BitConverter.ToInt16 (buffer, 34);
616 int daylight_hour = BitConverter.ToInt16 (buffer, 36);
617 int daylight_minute = BitConverter.ToInt16 (buffer, 38);
618 int daylight_second = BitConverter.ToInt16 (buffer, 40);
619 int daylight_millisecond = BitConverter.ToInt16 (buffer, 42);
621 if (standard_month == 0 || daylight_month == 0)
625 DateTime start_timeofday = new DateTime (1, 1, 1, daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
626 TransitionTime start_transition_time;
628 if (daylight_year == 0) {
629 start_date = new DateTime (start_year, 1, 1);
630 start_transition_time = TransitionTime.CreateFloatingDateRule (
631 start_timeofday, daylight_month, daylight_day,
632 (DayOfWeek) daylight_dayofweek);
635 start_date = new DateTime (daylight_year, daylight_month, daylight_day,
636 daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
637 start_transition_time = TransitionTime.CreateFixedDateRule (
638 start_timeofday, daylight_month, daylight_day);
642 DateTime end_timeofday = new DateTime (1, 1, 1, standard_hour, standard_minute, standard_second, standard_millisecond);
643 TransitionTime end_transition_time;
645 if (standard_year == 0) {
646 end_date = new DateTime (end_year, 12, 31);
647 end_transition_time = TransitionTime.CreateFloatingDateRule (
648 end_timeofday, standard_month, standard_day,
649 (DayOfWeek) standard_dayofweek);
652 end_date = new DateTime (standard_year, standard_month, standard_day,
653 standard_hour, standard_minute, standard_second, standard_millisecond);
654 end_transition_time = TransitionTime.CreateFixedDateRule (
655 end_timeofday, standard_month, standard_day);
658 TimeSpan daylight_delta = new TimeSpan(0, -daylight_bias, 0);
660 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (
661 start_date, end_date, daylight_delta,
662 start_transition_time, end_transition_time));
666 public AdjustmentRule [] GetAdjustmentRules ()
668 if (!supportsDaylightSavingTime || adjustmentRules == null)
669 return new AdjustmentRule [0];
671 return (AdjustmentRule []) adjustmentRules.Clone ();
674 public TimeSpan [] GetAmbiguousTimeOffsets (DateTime dateTime)
676 if (!IsAmbiguousTime (dateTime))
677 throw new ArgumentException ("dateTime is not an ambiguous time");
679 AdjustmentRule rule = GetApplicableRule (dateTime);
681 return new TimeSpan[] {baseUtcOffset, baseUtcOffset + rule.DaylightDelta};
683 return new TimeSpan[] {baseUtcOffset, baseUtcOffset};
686 public TimeSpan [] GetAmbiguousTimeOffsets (DateTimeOffset dateTimeOffset)
688 if (!IsAmbiguousTime (dateTimeOffset))
689 throw new ArgumentException ("dateTimeOffset is not an ambiguous time");
691 throw new NotImplementedException ();
694 public override int GetHashCode ()
696 int hash_code = Id.GetHashCode ();
697 foreach (AdjustmentRule rule in GetAdjustmentRules ())
698 hash_code ^= rule.GetHashCode ();
702 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
705 throw new ArgumentNullException ("info");
706 info.AddValue ("Id", id);
707 info.AddValue ("DisplayName", displayName);
708 info.AddValue ("StandardName", standardDisplayName);
709 info.AddValue ("DaylightName", daylightDisplayName);
710 info.AddValue ("BaseUtcOffset", baseUtcOffset);
711 info.AddValue ("AdjustmentRules", adjustmentRules);
712 info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime);
715 static ReadOnlyCollection<TimeZoneInfo> systemTimeZones;
717 public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones ()
719 if (systemTimeZones == null) {
720 var tz = new List<TimeZoneInfo> ();
721 GetSystemTimeZonesCore (tz);
722 Interlocked.CompareExchange (ref systemTimeZones, new ReadOnlyCollection<TimeZoneInfo> (tz), null);
725 return systemTimeZones;
728 public TimeSpan GetUtcOffset (DateTime dateTime)
731 return GetUtcOffset (dateTime, out isDST);
734 public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
736 throw new NotImplementedException ();
739 private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
743 TimeZoneInfo tz = this;
744 if (dateTime.Kind == DateTimeKind.Utc)
745 tz = TimeZoneInfo.Utc;
747 if (dateTime.Kind == DateTimeKind.Local)
748 tz = TimeZoneInfo.Local;
751 var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
758 DateTime utcDateTime;
759 if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
760 return BaseUtcOffset;
762 return GetUtcOffsetHelper (utcDateTime, this, out isDST);
765 // This is an helper method used by the method above, do not use this on its own.
766 private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
768 if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
769 throw new Exception ();
773 if (tz == TimeZoneInfo.Utc)
774 return TimeSpan.Zero;
777 if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
780 if (dateTime.Kind == DateTimeKind.Utc) {
781 var utcRule = tz.GetApplicableRule (dateTime);
782 if (utcRule != null && tz.IsInDST (utcRule, dateTime)) {
784 return tz.BaseUtcOffset + utcRule.DaylightDelta;
787 return tz.BaseUtcOffset;
790 DateTime stdUtcDateTime;
791 if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc))
792 return tz.BaseUtcOffset;
794 var tzRule = tz.GetApplicableRule (stdUtcDateTime);
796 DateTime dstUtcDateTime = DateTime.MinValue;
797 if (tzRule != null) {
798 if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc))
799 return tz.BaseUtcOffset;
802 if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
804 return tz.BaseUtcOffset + tzRule.DaylightDelta;
807 return tz.BaseUtcOffset;
810 public bool HasSameRules (TimeZoneInfo other)
813 throw new ArgumentNullException ("other");
815 if ((this.adjustmentRules == null) != (other.adjustmentRules == null))
818 if (this.adjustmentRules == null)
821 if (this.BaseUtcOffset != other.BaseUtcOffset)
824 if (this.adjustmentRules.Length != other.adjustmentRules.Length)
827 for (int i = 0; i < adjustmentRules.Length; i++) {
828 if (! (this.adjustmentRules [i]).Equals (other.adjustmentRules [i]))
835 public bool IsAmbiguousTime (DateTime dateTime)
837 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
838 throw new ArgumentException ("Kind is Local and time is Invalid");
840 if (this == TimeZoneInfo.Utc)
843 if (dateTime.Kind == DateTimeKind.Utc)
844 dateTime = ConvertTimeFromUtc (dateTime);
846 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
847 dateTime = ConvertTime (dateTime, TimeZoneInfo.Local, this);
849 AdjustmentRule rule = GetApplicableRule (dateTime);
851 DateTime tpoint = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year);
852 if (dateTime > tpoint - rule.DaylightDelta && dateTime <= tpoint)
859 public bool IsAmbiguousTime (DateTimeOffset dateTimeOffset)
861 throw new NotImplementedException ();
864 private bool IsInDST (AdjustmentRule rule, DateTime dateTime)
866 // Check whether we're in the dateTime year's DST period
867 if (IsInDSTForYear (rule, dateTime, dateTime.Year))
870 // We might be in the dateTime previous year's DST period
871 return IsInDSTForYear (rule, dateTime, dateTime.Year - 1);
874 bool IsInDSTForYear (AdjustmentRule rule, DateTime dateTime, int year)
876 DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, year);
877 DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
878 if (dateTime.Kind == DateTimeKind.Utc) {
879 DST_start -= BaseUtcOffset;
880 DST_end -= (BaseUtcOffset + rule.DaylightDelta);
883 return (dateTime >= DST_start && dateTime < DST_end);
886 public bool IsDaylightSavingTime (DateTime dateTime)
888 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
889 throw new ArgumentException ("dateTime is invalid and Kind is Local");
891 if (this == TimeZoneInfo.Utc)
894 if (!SupportsDaylightSavingTime)
898 GetUtcOffset (dateTime, out isDst);
903 internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags)
905 return IsDaylightSavingTime (dateTime);
908 public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
910 throw new NotImplementedException ();
913 internal DaylightTime GetDaylightChanges (int year)
915 DateTime start = DateTime.MinValue, end = DateTime.MinValue;
916 TimeSpan delta = new TimeSpan ();
918 if (transitions != null) {
919 end = DateTime.MaxValue;
920 for (var i = transitions.Count - 1; i >= 0; i--) {
921 var pair = transitions [i];
922 DateTime ttime = pair.Key;
923 TimeType ttype = pair.Value;
925 if (ttime.Year > year)
927 if (ttime.Year < year)
931 // DaylightTime.Delta is relative to the current BaseUtcOffset.
932 delta = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset;
939 // DaylightTime.Start is relative to the Standard time.
940 if (!TryAddTicks (start, BaseUtcOffset.Ticks, out start))
941 start = DateTime.MinValue;
943 // DaylightTime.End is relative to the DST time.
944 if (!TryAddTicks (end, BaseUtcOffset.Ticks + delta.Ticks, out end))
945 end = DateTime.MinValue;
947 AdjustmentRule first = null, last = null;
949 foreach (var rule in GetAdjustmentRules ()) {
950 if (rule.DateStart.Year != year && rule.DateEnd.Year != year)
952 if (rule.DateStart.Year == year)
954 if (rule.DateEnd.Year == year)
958 if (first == null || last == null)
959 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
961 start = TransitionPoint (first.DaylightTransitionStart, year);
962 end = TransitionPoint (last.DaylightTransitionEnd, year);
963 delta = first.DaylightDelta;
966 if (start == DateTime.MinValue || end == DateTime.MinValue)
967 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
969 return new DaylightTime (start, end, delta);
972 public bool IsInvalidTime (DateTime dateTime)
974 if (dateTime.Kind == DateTimeKind.Utc)
976 if (dateTime.Kind == DateTimeKind.Local && this != Local)
979 AdjustmentRule rule = GetApplicableRule (dateTime);
981 DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
982 if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
989 void IDeserializationCallback.OnDeserialization (object sender)
992 TimeZoneInfo.Validate (id, baseUtcOffset, adjustmentRules);
993 } catch (ArgumentException ex) {
994 throw new SerializationException ("invalid serialization data", ex);
998 private static void Validate (string id, TimeSpan baseUtcOffset, AdjustmentRule [] adjustmentRules)
1001 throw new ArgumentNullException ("id");
1003 if (id == String.Empty)
1004 throw new ArgumentException ("id parameter is an empty string");
1006 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1007 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1009 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1010 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1014 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1017 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1018 AdjustmentRule prev = null;
1019 foreach (AdjustmentRule current in adjustmentRules) {
1020 if (current == null)
1021 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1023 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1024 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1025 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;");
1027 if (prev != null && prev.DateStart > current.DateStart)
1028 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1030 if (prev != null && prev.DateEnd > current.DateStart)
1031 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1033 if (prev != null && prev.DateEnd == current.DateStart)
1034 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1041 public override string ToString ()
1046 private TimeZoneInfo (SerializationInfo info, StreamingContext context)
1049 throw new ArgumentNullException ("info");
1050 id = (string) info.GetValue ("Id", typeof (string));
1051 displayName = (string) info.GetValue ("DisplayName", typeof (string));
1052 standardDisplayName = (string) info.GetValue ("StandardName", typeof (string));
1053 daylightDisplayName = (string) info.GetValue ("DaylightName", typeof (string));
1054 baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", typeof (TimeSpan));
1055 adjustmentRules = (TimeZoneInfo.AdjustmentRule []) info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
1056 supportsDaylightSavingTime = (bool) info.GetValue ("SupportsDaylightSavingTime", typeof (bool));
1059 private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
1062 throw new ArgumentNullException ("id");
1064 if (id == String.Empty)
1065 throw new ArgumentException ("id parameter is an empty string");
1067 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1068 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1070 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1071 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1075 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1078 bool supportsDaylightSavingTime = !disableDaylightSavingTime;
1080 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1081 AdjustmentRule prev = null;
1082 foreach (AdjustmentRule current in adjustmentRules) {
1083 if (current == null)
1084 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1086 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1087 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1088 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;");
1090 if (prev != null && prev.DateStart > current.DateStart)
1091 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1093 if (prev != null && prev.DateEnd > current.DateStart)
1094 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1096 if (prev != null && prev.DateEnd == current.DateStart)
1097 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1102 supportsDaylightSavingTime = false;
1106 this.baseUtcOffset = baseUtcOffset;
1107 this.displayName = displayName ?? id;
1108 this.standardDisplayName = standardDisplayName ?? id;
1109 this.daylightDisplayName = daylightDisplayName;
1110 this.supportsDaylightSavingTime = supportsDaylightSavingTime;
1111 this.adjustmentRules = adjustmentRules;
1114 private AdjustmentRule GetApplicableRule (DateTime dateTime)
1116 //Applicable rules are in standard time
1117 DateTime date = dateTime;
1119 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1120 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date))
1122 } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) {
1123 if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date))
1127 // get the date component of the datetime
1130 if (adjustmentRules != null) {
1131 foreach (AdjustmentRule rule in adjustmentRules) {
1132 if (rule.DateStart > date)
1134 if (rule.DateEnd < date)
1142 private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out bool isDst)
1144 offset = BaseUtcOffset;
1147 if (transitions == null)
1150 //Transitions are in UTC
1151 DateTime date = dateTime;
1153 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1154 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1158 if (dateTime.Kind != DateTimeKind.Utc) {
1159 if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1163 for (var i = transitions.Count - 1; i >= 0; i--) {
1164 var pair = transitions [i];
1165 DateTime ttime = pair.Key;
1166 TimeType ttype = pair.Value;
1171 offset = new TimeSpan (0, 0, ttype.Offset);
1172 isDst = ttype.IsDst;
1180 private static DateTime TransitionPoint (TransitionTime transition, int year)
1182 if (transition.IsFixedDateRule)
1183 return new DateTime (year, transition.Month, transition.Day) + transition.TimeOfDay.TimeOfDay;
1185 DayOfWeek first = (new DateTime (year, transition.Month, 1)).DayOfWeek;
1186 int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first + 7) % 7;
1187 if (day > DateTime.DaysInMonth (year, transition.Month))
1191 return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
1194 static List<AdjustmentRule> ValidateRules (List<AdjustmentRule> adjustmentRules)
1196 AdjustmentRule prev = null;
1197 foreach (AdjustmentRule current in adjustmentRules.ToArray ()) {
1198 if (prev != null && prev.DateEnd > current.DateStart) {
1199 adjustmentRules.Remove (current);
1203 return adjustmentRules;
1206 #if LIBC || MONOTOUCH
1207 const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k)
1209 private static TimeZoneInfo BuildFromStream (string id, Stream stream)
1211 byte [] buffer = new byte [BUFFER_SIZE];
1212 int length = stream.Read (buffer, 0, BUFFER_SIZE);
1214 if (!ValidTZFile (buffer, length))
1215 throw new InvalidTimeZoneException ("TZ file too big for the buffer");
1218 return ParseTZBuffer (id, buffer, length);
1219 } catch (Exception e) {
1220 throw new InvalidTimeZoneException (e.Message);
1224 private static bool ValidTZFile (byte [] buffer, int length)
1226 StringBuilder magic = new StringBuilder ();
1228 for (int i = 0; i < 4; i++)
1229 magic.Append ((char)buffer [i]);
1231 if (magic.ToString () != "TZif")
1234 if (length >= BUFFER_SIZE)
1240 static int SwapInt32 (int i)
1242 return (((i >> 24) & 0xff)
1243 | ((i >> 8) & 0xff00)
1244 | ((i << 8) & 0xff0000)
1245 | (((i & 0xff) << 24)));
1248 static int ReadBigEndianInt32 (byte [] buffer, int start)
1250 int i = BitConverter.ToInt32 (buffer, start);
1251 if (!BitConverter.IsLittleEndian)
1254 return SwapInt32 (i);
1257 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
1259 //Reading the header. 4 bytes for magic, 16 are reserved
1260 int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
1261 int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
1262 int leapcnt = ReadBigEndianInt32 (buffer, 28);
1263 int timecnt = ReadBigEndianInt32 (buffer, 32);
1264 int typecnt = ReadBigEndianInt32 (buffer, 36);
1265 int charcnt = ReadBigEndianInt32 (buffer, 40);
1267 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
1268 throw new InvalidTimeZoneException ();
1270 Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
1271 Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
1272 List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
1274 if (time_types.Count == 0)
1275 throw new InvalidTimeZoneException ();
1277 if (time_types.Count == 1 && ((TimeType)time_types[0]).IsDst)
1278 throw new InvalidTimeZoneException ();
1280 TimeSpan baseUtcOffset = new TimeSpan (0);
1281 TimeSpan dstDelta = new TimeSpan (0);
1282 string standardDisplayName = null;
1283 string daylightDisplayName = null;
1284 bool dst_observed = false;
1285 DateTime dst_start = DateTime.MinValue;
1286 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
1287 bool storeTransition = false;
1289 for (int i = 0; i < transitions.Count; i++) {
1290 var pair = transitions [i];
1291 DateTime ttime = pair.Key;
1292 TimeType ttype = pair.Value;
1294 if (standardDisplayName != ttype.Name)
1295 standardDisplayName = ttype.Name;
1296 if (baseUtcOffset.TotalSeconds != ttype.Offset) {
1297 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
1298 if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions.
1299 storeTransition = true;
1300 adjustmentRules = new List<AdjustmentRule> ();
1301 dst_observed = false;
1304 //FIXME: check additional fields for this:
1305 //most of the transitions are expressed in GMT
1306 dst_start += baseUtcOffset;
1307 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
1309 //some weird timezone (America/Phoenix) have end dates on Jan 1st
1310 if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
1311 dst_end -= new TimeSpan (24, 0, 0);
1314 * AdjustmentRule specifies a DST period that starts and ends within a year.
1315 * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
1316 * Thus we fallback to the transitions.
1318 if (dst_start.AddYears (1) < dst_end)
1319 storeTransition = true;
1321 DateTime dateStart, dateEnd;
1322 if (dst_start.Month < 7)
1323 dateStart = new DateTime (dst_start.Year, 1, 1);
1325 dateStart = new DateTime (dst_start.Year, 7, 1);
1327 if (dst_end.Month >= 7)
1328 dateEnd = new DateTime (dst_end.Year, 12, 31);
1330 dateEnd = new DateTime (dst_end.Year, 6, 30);
1333 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
1334 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
1335 if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
1336 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
1338 dst_observed = false;
1340 if (daylightDisplayName != ttype.Name)
1341 daylightDisplayName = ttype.Name;
1342 if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
1343 // Round to nearest minute, since it's not possible to create an adjustment rule
1344 // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
1345 // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
1346 dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset;
1347 if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0)
1348 dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f));
1352 dst_observed = true;
1357 if (adjustmentRules.Count == 0 && !storeTransition) {
1358 TimeType t = (TimeType)time_types [0];
1359 if (standardDisplayName == null) {
1360 standardDisplayName = t.Name;
1361 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
1363 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
1365 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
1368 if (storeTransition && transitions.Count > 0) {
1369 tz.transitions = transitions;
1370 tz.supportsDaylightSavingTime = true;
1376 static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
1378 var abbrevs = new Dictionary<int, string> ();
1379 int abbrev_index = 0;
1380 var sb = new StringBuilder ();
1381 for (int i = 0; i < count; i++) {
1382 char c = (char) buffer [index + i];
1386 abbrevs.Add (abbrev_index, sb.ToString ());
1387 //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
1388 //j == sb.Length empty substring also needs to be added #31432
1389 for (int j = 1; j <= sb.Length; j++)
1390 abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
1391 abbrev_index = i + 1;
1392 sb = new StringBuilder ();
1398 static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
1400 var types = new Dictionary<int, TimeType> (count);
1401 for (int i = 0; i < count; i++) {
1402 int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
1403 byte is_dst = buffer [index + 6 * i + 4];
1404 byte abbrev = buffer [index + 6 * i + 5];
1405 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
1410 static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
1412 var list = new List<KeyValuePair<DateTime, TimeType>> (count);
1413 for (int i = 0; i < count; i++) {
1414 int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
1415 DateTime ttime = DateTimeFromUnixTime (unixtime);
1416 byte ttype = buffer [index + 4 * count + i];
1417 list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
1422 static DateTime DateTimeFromUnixTime (long unix_time)
1424 DateTime date_time = new DateTime (1970, 1, 1);
1425 return date_time.AddSeconds (unix_time);
1428 #region reference sources
1429 // Shortcut for TimeZoneInfo.Local.GetUtcOffset
1430 internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1433 return Local.GetUtcOffset (dateTime, out dst);
1436 internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1439 return GetUtcOffset (dateTime, out dst);
1442 static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
1444 isDaylightSavings = false;
1445 isAmbiguousLocalDst = false;
1446 TimeSpan baseOffset = zone.BaseUtcOffset;
1448 if (zone.IsAmbiguousTime (time)) {
1449 isAmbiguousLocalDst = true;
1453 return zone.GetUtcOffset (time, out isDaylightSavings);
1459 public readonly int Offset;
1460 public readonly bool IsDst;
1463 public TimeType (int offset, bool is_dst, string abbrev)
1465 this.Offset = offset;
1466 this.IsDst = is_dst;
1470 public override string ToString ()
1472 return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;