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)
126 path = Path.GetFullPath (path);
128 if (string.IsNullOrEmpty (TimeZoneDirectory))
131 var baseDir = TimeZoneDirectory;
132 if (baseDir [baseDir.Length-1] != Path.DirectorySeparatorChar)
133 baseDir += Path.DirectorySeparatorChar;
135 if (!path.StartsWith (baseDir, StringComparison.InvariantCulture))
138 name = path.Substring (baseDir.Length);
139 if (name == "localtime")
145 #if !MOBILE || MOBILE_STATIC
146 static TimeZoneInfo CreateLocal ()
149 if (IsWindows && LocalZoneKey != null) {
150 string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
152 name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp
153 name = TrimSpecial (name);
155 return TimeZoneInfo.FindSystemTimeZoneById (name);
159 var tz = Environment.GetEnvironmentVariable ("TZ");
161 if (tz == String.Empty)
164 return FindSystemTimeZoneByFileName (tz, Path.Combine (TimeZoneDirectory, tz));
170 var tzFilePaths = new string [] {
172 Path.Combine (TimeZoneDirectory, "localtime")};
174 foreach (var tzFilePath in tzFilePaths) {
176 string tzName = null;
177 if (!TryGetNameFromPath (tzFilePath, out tzName))
179 return FindSystemTimeZoneByFileName (tzName, tzFilePath);
180 } catch (TimeZoneNotFoundException) {
188 static TimeZoneInfo FindSystemTimeZoneByIdCore (string id)
191 string filepath = Path.Combine (TimeZoneDirectory, id);
192 return FindSystemTimeZoneByFileName (id, filepath);
194 throw new NotImplementedException ();
198 static void GetSystemTimeZonesCore (List<TimeZoneInfo> systemTimeZones)
201 if (TimeZoneKey != null) {
202 foreach (string id in TimeZoneKey.GetSubKeyNames ()) {
204 systemTimeZones.Add (FindSystemTimeZoneById (id));
213 string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"};
214 foreach (string continent in continents) {
216 foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) {
218 string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath));
219 systemTimeZones.Add (FindSystemTimeZoneById (id));
220 } catch (ArgumentNullException) {
221 } catch (TimeZoneNotFoundException) {
222 } catch (InvalidTimeZoneException) {
223 } catch (Exception) {
230 throw new NotImplementedException ("This method is not implemented for this platform");
235 string standardDisplayName;
236 public string StandardName {
237 get { return standardDisplayName; }
240 bool supportsDaylightSavingTime;
241 public bool SupportsDaylightSavingTime {
242 get { return supportsDaylightSavingTime; }
245 static TimeZoneInfo utc;
246 public static TimeZoneInfo Utc {
249 utc = CreateCustomTimeZone ("UTC", new TimeSpan (0), "UTC", "UTC");
254 static string timeZoneDirectory;
255 static string TimeZoneDirectory {
257 if (timeZoneDirectory == null)
258 timeZoneDirectory = "/usr/share/zoneinfo";
259 return timeZoneDirectory;
263 timeZoneDirectory = value;
267 private AdjustmentRule [] adjustmentRules;
269 #if !NET_2_1 || MOBILE_STATIC
271 /// Determine whether windows of not (taken Stephane Delcroix's code)
273 private static bool IsWindows
276 int platform = (int) Environment.OSVersion.Platform;
277 return ((platform != 4) && (platform != 6) && (platform != 128));
282 /// Needed to trim misc garbage in MS registry keys
284 private static string TrimSpecial (string str)
289 while (Istart < str.Length && !char.IsLetterOrDigit(str[Istart])) Istart++;
290 var Iend = str.Length - 1;
291 while (Iend > Istart && !char.IsLetterOrDigit(str[Iend])) Iend--;
293 return str.Substring (Istart, Iend-Istart+1);
297 static RegistryKey timeZoneKey;
298 static RegistryKey TimeZoneKey {
300 if (timeZoneKey != null)
305 return timeZoneKey = Registry.LocalMachine.OpenSubKey (
306 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
311 static RegistryKey localZoneKey;
312 static RegistryKey LocalZoneKey {
314 if (localZoneKey != null)
320 return localZoneKey = Registry.LocalMachine.OpenSubKey (
321 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", false);
327 private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified)
329 var resultTicks = date.Ticks + ticks;
330 if (resultTicks < DateTime.MinValue.Ticks || resultTicks > DateTime.MaxValue.Ticks) {
331 result = default (DateTime);
335 result = new DateTime (resultTicks, kind);
339 public static void ClearCachedData ()
343 systemTimeZones = null;
346 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
348 return ConvertTime (dateTime, dateTime.Kind == DateTimeKind.Utc ? TimeZoneInfo.Utc : TimeZoneInfo.Local, destinationTimeZone);
351 public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
353 if (sourceTimeZone == null)
354 throw new ArgumentNullException ("sourceTimeZone");
356 if (destinationTimeZone == null)
357 throw new ArgumentNullException ("destinationTimeZone");
359 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
360 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
362 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
363 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
365 if (sourceTimeZone.IsInvalidTime (dateTime))
366 throw new ArgumentException ("dateTime parameter is an invalid time");
368 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
371 DateTime utc = ConvertTimeToUtc (dateTime, sourceTimeZone);
373 if (destinationTimeZone != TimeZoneInfo.Utc) {
374 utc = ConvertTimeFromUtc (utc, destinationTimeZone);
375 if (dateTime.Kind == DateTimeKind.Unspecified)
376 return DateTime.SpecifyKind (utc, DateTimeKind.Unspecified);
382 public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
384 if (destinationTimeZone == null)
385 throw new ArgumentNullException("destinationTimeZone");
387 var utcDateTime = dateTimeOffset.UtcDateTime;
390 var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst);
392 return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset);
395 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
397 return ConvertTime (dateTime, FindSystemTimeZoneById (destinationTimeZoneId));
400 public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string sourceTimeZoneId, string destinationTimeZoneId)
402 return ConvertTime (dateTime, FindSystemTimeZoneById (sourceTimeZoneId), FindSystemTimeZoneById (destinationTimeZoneId));
405 public static DateTimeOffset ConvertTimeBySystemTimeZoneId (DateTimeOffset dateTimeOffset, string destinationTimeZoneId)
407 return ConvertTime (dateTimeOffset, FindSystemTimeZoneById (destinationTimeZoneId));
410 private DateTime ConvertTimeFromUtc (DateTime dateTime)
412 if (dateTime.Kind == DateTimeKind.Local)
413 throw new ArgumentException ("Kind property of dateTime is Local");
415 if (this == TimeZoneInfo.Utc)
416 return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
418 var utcOffset = GetUtcOffset (dateTime);
420 var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified;
423 if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind))
424 return DateTime.SpecifyKind (DateTime.MaxValue, kind);
429 public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
431 if (destinationTimeZone == null)
432 throw new ArgumentNullException ("destinationTimeZone");
434 return destinationTimeZone.ConvertTimeFromUtc (dateTime);
437 public static DateTime ConvertTimeToUtc (DateTime dateTime)
439 if (dateTime.Kind == DateTimeKind.Utc)
442 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local);
445 static internal DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
447 return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local, flags);
450 public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
452 return ConvertTimeToUtc (dateTime, sourceTimeZone, TimeZoneInfoOptions.None);
455 static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfoOptions flags)
457 if ((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) {
458 if (sourceTimeZone == null)
459 throw new ArgumentNullException ("sourceTimeZone");
461 if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
462 throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
464 if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
465 throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
467 if (sourceTimeZone.IsInvalidTime (dateTime))
468 throw new ArgumentException ("dateTime parameter is an invalid time");
471 if (dateTime.Kind == DateTimeKind.Utc)
475 var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst);
477 DateTime utcDateTime;
478 if (!TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
479 return DateTime.SpecifyKind (DateTime.MinValue, DateTimeKind.Utc);
484 static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
486 bool isDaylightSavings;
487 return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
490 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
492 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, null, null, true);
495 public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules)
497 return CreateCustomTimeZone (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, false);
500 public static TimeZoneInfo CreateCustomTimeZone ( string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
502 return new TimeZoneInfo (id, baseUtcOffset, displayName, standardDisplayName, daylightDisplayName, adjustmentRules, disableDaylightSavingTime);
505 public override bool Equals (object obj)
507 return Equals (obj as TimeZoneInfo);
510 public bool Equals (TimeZoneInfo other)
515 return other.Id == this.Id && HasSameRules (other);
518 public static TimeZoneInfo FindSystemTimeZoneById (string id)
520 //FIXME: this method should check for cached values in systemTimeZones
522 throw new ArgumentNullException ("id");
524 if (TimeZoneKey != null)
526 if (id == "Coordinated Universal Time")
527 id = "UTC"; //windows xp exception for "StandardName" property
528 RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
530 throw new TimeZoneNotFoundException ();
531 return FromRegistryKey(id, key);
534 // Local requires special logic that already exists in the Local property (bug #326)
538 return FindSystemTimeZoneByIdCore (id);
542 private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
544 if (!File.Exists (filepath))
545 throw new TimeZoneNotFoundException ();
547 using (FileStream stream = File.OpenRead (filepath)) {
548 return BuildFromStream (id, stream);
554 private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key)
556 byte [] reg_tzi = (byte []) key.GetValue ("TZI");
559 throw new InvalidTimeZoneException ();
561 int bias = BitConverter.ToInt32 (reg_tzi, 0);
562 TimeSpan baseUtcOffset = new TimeSpan (0, -bias, 0);
564 string display_name = (string) key.GetValue ("Display");
565 string standard_name = (string) key.GetValue ("Std");
566 string daylight_name = (string) key.GetValue ("Dlt");
568 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
570 RegistryKey dst_key = key.OpenSubKey ("Dynamic DST", false);
571 if (dst_key != null) {
572 int first_year = (int) dst_key.GetValue ("FirstEntry");
573 int last_year = (int) dst_key.GetValue ("LastEntry");
576 for (year=first_year; year<=last_year; year++) {
577 byte [] dst_tzi = (byte []) dst_key.GetValue (year.ToString ());
578 if (dst_tzi != null) {
579 int start_year = year == first_year ? 1 : year;
580 int end_year = year == last_year ? 9999 : year;
581 ParseRegTzi(adjustmentRules, start_year, end_year, dst_tzi);
586 ParseRegTzi(adjustmentRules, 1, 9999, reg_tzi);
588 return CreateCustomTimeZone (id, baseUtcOffset, display_name, standard_name, daylight_name, ValidateRules (adjustmentRules).ToArray ());
591 private static void ParseRegTzi (List<AdjustmentRule> adjustmentRules, int start_year, int end_year, byte [] buffer)
593 //int standard_bias = BitConverter.ToInt32 (buffer, 4); /* not sure how to handle this */
594 int daylight_bias = BitConverter.ToInt32 (buffer, 8);
596 int standard_year = BitConverter.ToInt16 (buffer, 12);
597 int standard_month = BitConverter.ToInt16 (buffer, 14);
598 int standard_dayofweek = BitConverter.ToInt16 (buffer, 16);
599 int standard_day = BitConverter.ToInt16 (buffer, 18);
600 int standard_hour = BitConverter.ToInt16 (buffer, 20);
601 int standard_minute = BitConverter.ToInt16 (buffer, 22);
602 int standard_second = BitConverter.ToInt16 (buffer, 24);
603 int standard_millisecond = BitConverter.ToInt16 (buffer, 26);
605 int daylight_year = BitConverter.ToInt16 (buffer, 28);
606 int daylight_month = BitConverter.ToInt16 (buffer, 30);
607 int daylight_dayofweek = BitConverter.ToInt16 (buffer, 32);
608 int daylight_day = BitConverter.ToInt16 (buffer, 34);
609 int daylight_hour = BitConverter.ToInt16 (buffer, 36);
610 int daylight_minute = BitConverter.ToInt16 (buffer, 38);
611 int daylight_second = BitConverter.ToInt16 (buffer, 40);
612 int daylight_millisecond = BitConverter.ToInt16 (buffer, 42);
614 if (standard_month == 0 || daylight_month == 0)
618 DateTime start_timeofday = new DateTime (1, 1, 1, daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
619 TransitionTime start_transition_time;
621 if (daylight_year == 0) {
622 start_date = new DateTime (start_year, 1, 1);
623 start_transition_time = TransitionTime.CreateFloatingDateRule (
624 start_timeofday, daylight_month, daylight_day,
625 (DayOfWeek) daylight_dayofweek);
628 start_date = new DateTime (daylight_year, daylight_month, daylight_day,
629 daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
630 start_transition_time = TransitionTime.CreateFixedDateRule (
631 start_timeofday, daylight_month, daylight_day);
635 DateTime end_timeofday = new DateTime (1, 1, 1, standard_hour, standard_minute, standard_second, standard_millisecond);
636 TransitionTime end_transition_time;
638 if (standard_year == 0) {
639 end_date = new DateTime (end_year, 12, 31);
640 end_transition_time = TransitionTime.CreateFloatingDateRule (
641 end_timeofday, standard_month, standard_day,
642 (DayOfWeek) standard_dayofweek);
645 end_date = new DateTime (standard_year, standard_month, standard_day,
646 standard_hour, standard_minute, standard_second, standard_millisecond);
647 end_transition_time = TransitionTime.CreateFixedDateRule (
648 end_timeofday, standard_month, standard_day);
651 TimeSpan daylight_delta = new TimeSpan(0, -daylight_bias, 0);
653 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (
654 start_date, end_date, daylight_delta,
655 start_transition_time, end_transition_time));
659 public AdjustmentRule [] GetAdjustmentRules ()
661 if (!supportsDaylightSavingTime || adjustmentRules == null)
662 return new AdjustmentRule [0];
664 return (AdjustmentRule []) adjustmentRules.Clone ();
667 public TimeSpan [] GetAmbiguousTimeOffsets (DateTime dateTime)
669 if (!IsAmbiguousTime (dateTime))
670 throw new ArgumentException ("dateTime is not an ambiguous time");
672 AdjustmentRule rule = GetApplicableRule (dateTime);
674 return new TimeSpan[] {baseUtcOffset, baseUtcOffset + rule.DaylightDelta};
676 return new TimeSpan[] {baseUtcOffset, baseUtcOffset};
679 public TimeSpan [] GetAmbiguousTimeOffsets (DateTimeOffset dateTimeOffset)
681 if (!IsAmbiguousTime (dateTimeOffset))
682 throw new ArgumentException ("dateTimeOffset is not an ambiguous time");
684 throw new NotImplementedException ();
687 public override int GetHashCode ()
689 int hash_code = Id.GetHashCode ();
690 foreach (AdjustmentRule rule in GetAdjustmentRules ())
691 hash_code ^= rule.GetHashCode ();
695 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
698 throw new ArgumentNullException ("info");
699 info.AddValue ("Id", id);
700 info.AddValue ("DisplayName", displayName);
701 info.AddValue ("StandardName", standardDisplayName);
702 info.AddValue ("DaylightName", daylightDisplayName);
703 info.AddValue ("BaseUtcOffset", baseUtcOffset);
704 info.AddValue ("AdjustmentRules", adjustmentRules);
705 info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime);
708 static ReadOnlyCollection<TimeZoneInfo> systemTimeZones;
710 public static ReadOnlyCollection<TimeZoneInfo> GetSystemTimeZones ()
712 if (systemTimeZones == null) {
713 var tz = new List<TimeZoneInfo> ();
714 GetSystemTimeZonesCore (tz);
715 Interlocked.CompareExchange (ref systemTimeZones, new ReadOnlyCollection<TimeZoneInfo> (tz), null);
718 return systemTimeZones;
721 public TimeSpan GetUtcOffset (DateTime dateTime)
724 return GetUtcOffset (dateTime, out isDST);
727 public TimeSpan GetUtcOffset (DateTimeOffset dateTimeOffset)
729 throw new NotImplementedException ();
732 private TimeSpan GetUtcOffset (DateTime dateTime, out bool isDST)
736 TimeZoneInfo tz = this;
737 if (dateTime.Kind == DateTimeKind.Utc)
738 tz = TimeZoneInfo.Utc;
740 if (dateTime.Kind == DateTimeKind.Local)
741 tz = TimeZoneInfo.Local;
744 var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
751 DateTime utcDateTime;
752 if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
753 return BaseUtcOffset;
755 return GetUtcOffsetHelper (utcDateTime, this, out isDST);
758 // This is an helper method used by the method above, do not use this on its own.
759 private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
761 if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
762 throw new Exception ();
766 if (tz == TimeZoneInfo.Utc)
767 return TimeSpan.Zero;
770 if (tz.TryGetTransitionOffset(dateTime, out offset, out isDST))
773 if (dateTime.Kind == DateTimeKind.Utc) {
774 var utcRule = tz.GetApplicableRule (dateTime);
775 if (utcRule != null && tz.IsInDST (utcRule, dateTime)) {
777 return tz.BaseUtcOffset + utcRule.DaylightDelta;
780 return tz.BaseUtcOffset;
783 DateTime stdUtcDateTime;
784 if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc))
785 return tz.BaseUtcOffset;
787 var tzRule = tz.GetApplicableRule (stdUtcDateTime);
789 DateTime dstUtcDateTime = DateTime.MinValue;
790 if (tzRule != null) {
791 if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc))
792 return tz.BaseUtcOffset;
795 if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
797 return tz.BaseUtcOffset + tzRule.DaylightDelta;
800 return tz.BaseUtcOffset;
803 public bool HasSameRules (TimeZoneInfo other)
806 throw new ArgumentNullException ("other");
808 if ((this.adjustmentRules == null) != (other.adjustmentRules == null))
811 if (this.adjustmentRules == null)
814 if (this.BaseUtcOffset != other.BaseUtcOffset)
817 if (this.adjustmentRules.Length != other.adjustmentRules.Length)
820 for (int i = 0; i < adjustmentRules.Length; i++) {
821 if (! (this.adjustmentRules [i]).Equals (other.adjustmentRules [i]))
828 public bool IsAmbiguousTime (DateTime dateTime)
830 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
831 throw new ArgumentException ("Kind is Local and time is Invalid");
833 if (this == TimeZoneInfo.Utc)
836 if (dateTime.Kind == DateTimeKind.Utc)
837 dateTime = ConvertTimeFromUtc (dateTime);
839 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
840 dateTime = ConvertTime (dateTime, TimeZoneInfo.Local, this);
842 AdjustmentRule rule = GetApplicableRule (dateTime);
844 DateTime tpoint = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year);
845 if (dateTime > tpoint - rule.DaylightDelta && dateTime <= tpoint)
852 public bool IsAmbiguousTime (DateTimeOffset dateTimeOffset)
854 throw new NotImplementedException ();
857 private bool IsInDST (AdjustmentRule rule, DateTime dateTime)
859 // Check whether we're in the dateTime year's DST period
860 if (IsInDSTForYear (rule, dateTime, dateTime.Year))
863 // We might be in the dateTime previous year's DST period
864 return IsInDSTForYear (rule, dateTime, dateTime.Year - 1);
867 bool IsInDSTForYear (AdjustmentRule rule, DateTime dateTime, int year)
869 DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, year);
870 DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
871 if (dateTime.Kind == DateTimeKind.Utc) {
872 DST_start -= BaseUtcOffset;
873 DST_end -= (BaseUtcOffset + rule.DaylightDelta);
876 return (dateTime >= DST_start && dateTime < DST_end);
879 public bool IsDaylightSavingTime (DateTime dateTime)
881 if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
882 throw new ArgumentException ("dateTime is invalid and Kind is Local");
884 if (this == TimeZoneInfo.Utc)
887 if (!SupportsDaylightSavingTime)
891 GetUtcOffset (dateTime, out isDst);
896 internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags)
898 return IsDaylightSavingTime (dateTime);
901 public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
903 throw new NotImplementedException ();
906 internal DaylightTime GetDaylightChanges (int year)
908 DateTime start = DateTime.MinValue, end = DateTime.MinValue;
909 TimeSpan delta = new TimeSpan ();
911 if (transitions != null) {
912 end = DateTime.MaxValue;
913 for (var i = transitions.Count - 1; i >= 0; i--) {
914 var pair = transitions [i];
915 DateTime ttime = pair.Key;
916 TimeType ttype = pair.Value;
918 if (ttime.Year > year)
920 if (ttime.Year < year)
924 // DaylightTime.Delta is relative to the current BaseUtcOffset.
925 delta = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset;
932 // DaylightTime.Start is relative to the Standard time.
933 if (!TryAddTicks (start, BaseUtcOffset.Ticks, out start))
934 start = DateTime.MinValue;
936 // DaylightTime.End is relative to the DST time.
937 if (!TryAddTicks (end, BaseUtcOffset.Ticks + delta.Ticks, out end))
938 end = DateTime.MinValue;
940 AdjustmentRule first = null, last = null;
942 foreach (var rule in GetAdjustmentRules ()) {
943 if (rule.DateStart.Year != year && rule.DateEnd.Year != year)
945 if (rule.DateStart.Year == year)
947 if (rule.DateEnd.Year == year)
951 if (first == null || last == null)
952 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
954 start = TransitionPoint (first.DaylightTransitionStart, year);
955 end = TransitionPoint (last.DaylightTransitionEnd, year);
956 delta = first.DaylightDelta;
959 if (start == DateTime.MinValue || end == DateTime.MinValue)
960 return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
962 return new DaylightTime (start, end, delta);
965 public bool IsInvalidTime (DateTime dateTime)
967 if (dateTime.Kind == DateTimeKind.Utc)
969 if (dateTime.Kind == DateTimeKind.Local && this != Local)
972 AdjustmentRule rule = GetApplicableRule (dateTime);
974 DateTime tpoint = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
975 if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
982 void IDeserializationCallback.OnDeserialization (object sender)
985 TimeZoneInfo.Validate (id, baseUtcOffset, adjustmentRules);
986 } catch (ArgumentException ex) {
987 throw new SerializationException ("invalid serialization data", ex);
991 private static void Validate (string id, TimeSpan baseUtcOffset, AdjustmentRule [] adjustmentRules)
994 throw new ArgumentNullException ("id");
996 if (id == String.Empty)
997 throw new ArgumentException ("id parameter is an empty string");
999 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1000 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1002 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1003 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1007 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1010 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1011 AdjustmentRule prev = null;
1012 foreach (AdjustmentRule current in adjustmentRules) {
1013 if (current == null)
1014 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1016 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1017 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1018 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;");
1020 if (prev != null && prev.DateStart > current.DateStart)
1021 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1023 if (prev != null && prev.DateEnd > current.DateStart)
1024 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1026 if (prev != null && prev.DateEnd == current.DateStart)
1027 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1034 public override string ToString ()
1039 private TimeZoneInfo (SerializationInfo info, StreamingContext context)
1042 throw new ArgumentNullException ("info");
1043 id = (string) info.GetValue ("Id", typeof (string));
1044 displayName = (string) info.GetValue ("DisplayName", typeof (string));
1045 standardDisplayName = (string) info.GetValue ("StandardName", typeof (string));
1046 daylightDisplayName = (string) info.GetValue ("DaylightName", typeof (string));
1047 baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", typeof (TimeSpan));
1048 adjustmentRules = (TimeZoneInfo.AdjustmentRule []) info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
1049 supportsDaylightSavingTime = (bool) info.GetValue ("SupportsDaylightSavingTime", typeof (bool));
1052 private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
1055 throw new ArgumentNullException ("id");
1057 if (id == String.Empty)
1058 throw new ArgumentException ("id parameter is an empty string");
1060 if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
1061 throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
1063 if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
1064 throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
1068 throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
1071 bool supportsDaylightSavingTime = !disableDaylightSavingTime;
1073 if (adjustmentRules != null && adjustmentRules.Length != 0) {
1074 AdjustmentRule prev = null;
1075 foreach (AdjustmentRule current in adjustmentRules) {
1076 if (current == null)
1077 throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
1079 if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
1080 (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
1081 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;");
1083 if (prev != null && prev.DateStart > current.DateStart)
1084 throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
1086 if (prev != null && prev.DateEnd > current.DateStart)
1087 throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
1089 if (prev != null && prev.DateEnd == current.DateStart)
1090 throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
1095 supportsDaylightSavingTime = false;
1099 this.baseUtcOffset = baseUtcOffset;
1100 this.displayName = displayName ?? id;
1101 this.standardDisplayName = standardDisplayName ?? id;
1102 this.daylightDisplayName = daylightDisplayName;
1103 this.supportsDaylightSavingTime = supportsDaylightSavingTime;
1104 this.adjustmentRules = adjustmentRules;
1107 private AdjustmentRule GetApplicableRule (DateTime dateTime)
1109 //Applicable rules are in standard time
1110 DateTime date = dateTime;
1112 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1113 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date))
1115 } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) {
1116 if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date))
1120 // get the date component of the datetime
1123 if (adjustmentRules != null) {
1124 foreach (AdjustmentRule rule in adjustmentRules) {
1125 if (rule.DateStart > date)
1127 if (rule.DateEnd < date)
1135 private bool TryGetTransitionOffset (DateTime dateTime, out TimeSpan offset,out bool isDst)
1137 offset = BaseUtcOffset;
1140 if (transitions == null)
1143 //Transitions are in UTC
1144 DateTime date = dateTime;
1146 if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
1147 if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1151 if (dateTime.Kind != DateTimeKind.Utc) {
1152 if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
1156 for (var i = transitions.Count - 1; i >= 0; i--) {
1157 var pair = transitions [i];
1158 DateTime ttime = pair.Key;
1159 TimeType ttype = pair.Value;
1164 offset = new TimeSpan (0, 0, ttype.Offset);
1165 isDst = ttype.IsDst;
1173 private static DateTime TransitionPoint (TransitionTime transition, int year)
1175 if (transition.IsFixedDateRule)
1176 return new DateTime (year, transition.Month, transition.Day) + transition.TimeOfDay.TimeOfDay;
1178 DayOfWeek first = (new DateTime (year, transition.Month, 1)).DayOfWeek;
1179 int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first + 7) % 7;
1180 if (day > DateTime.DaysInMonth (year, transition.Month))
1184 return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
1187 static List<AdjustmentRule> ValidateRules (List<AdjustmentRule> adjustmentRules)
1189 AdjustmentRule prev = null;
1190 foreach (AdjustmentRule current in adjustmentRules.ToArray ()) {
1191 if (prev != null && prev.DateEnd > current.DateStart) {
1192 adjustmentRules.Remove (current);
1196 return adjustmentRules;
1199 #if LIBC || MONOTOUCH
1200 const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k)
1202 private static TimeZoneInfo BuildFromStream (string id, Stream stream)
1204 byte [] buffer = new byte [BUFFER_SIZE];
1205 int length = stream.Read (buffer, 0, BUFFER_SIZE);
1207 if (!ValidTZFile (buffer, length))
1208 throw new InvalidTimeZoneException ("TZ file too big for the buffer");
1211 return ParseTZBuffer (id, buffer, length);
1212 } catch (Exception e) {
1213 throw new InvalidTimeZoneException (e.Message);
1217 private static bool ValidTZFile (byte [] buffer, int length)
1219 StringBuilder magic = new StringBuilder ();
1221 for (int i = 0; i < 4; i++)
1222 magic.Append ((char)buffer [i]);
1224 if (magic.ToString () != "TZif")
1227 if (length >= BUFFER_SIZE)
1233 static int SwapInt32 (int i)
1235 return (((i >> 24) & 0xff)
1236 | ((i >> 8) & 0xff00)
1237 | ((i << 8) & 0xff0000)
1241 static int ReadBigEndianInt32 (byte [] buffer, int start)
1243 int i = BitConverter.ToInt32 (buffer, start);
1244 if (!BitConverter.IsLittleEndian)
1247 return SwapInt32 (i);
1250 private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
1252 //Reading the header. 4 bytes for magic, 16 are reserved
1253 int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
1254 int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
1255 int leapcnt = ReadBigEndianInt32 (buffer, 28);
1256 int timecnt = ReadBigEndianInt32 (buffer, 32);
1257 int typecnt = ReadBigEndianInt32 (buffer, 36);
1258 int charcnt = ReadBigEndianInt32 (buffer, 40);
1260 if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
1261 throw new InvalidTimeZoneException ();
1263 Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
1264 Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
1265 List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
1267 if (time_types.Count == 0)
1268 throw new InvalidTimeZoneException ();
1270 if (time_types.Count == 1 && ((TimeType)time_types[0]).IsDst)
1271 throw new InvalidTimeZoneException ();
1273 TimeSpan baseUtcOffset = new TimeSpan (0);
1274 TimeSpan dstDelta = new TimeSpan (0);
1275 string standardDisplayName = null;
1276 string daylightDisplayName = null;
1277 bool dst_observed = false;
1278 DateTime dst_start = DateTime.MinValue;
1279 List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
1280 bool storeTransition = false;
1282 for (int i = 0; i < transitions.Count; i++) {
1283 var pair = transitions [i];
1284 DateTime ttime = pair.Key;
1285 TimeType ttype = pair.Value;
1287 if (standardDisplayName != ttype.Name)
1288 standardDisplayName = ttype.Name;
1289 if (baseUtcOffset.TotalSeconds != ttype.Offset) {
1290 baseUtcOffset = new TimeSpan (0, 0, ttype.Offset);
1291 if (adjustmentRules.Count > 0) // We ignore AdjustmentRules but store transitions.
1292 storeTransition = true;
1293 adjustmentRules = new List<AdjustmentRule> ();
1294 dst_observed = false;
1297 //FIXME: check additional fields for this:
1298 //most of the transitions are expressed in GMT
1299 dst_start += baseUtcOffset;
1300 DateTime dst_end = ttime + baseUtcOffset + dstDelta;
1302 //some weird timezone (America/Phoenix) have end dates on Jan 1st
1303 if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
1304 dst_end -= new TimeSpan (24, 0, 0);
1307 * AdjustmentRule specifies a DST period that starts and ends within a year.
1308 * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
1309 * Thus we fallback to the transitions.
1311 if (dst_start.AddYears (1) < dst_end)
1312 storeTransition = true;
1314 DateTime dateStart, dateEnd;
1315 if (dst_start.Month < 7)
1316 dateStart = new DateTime (dst_start.Year, 1, 1);
1318 dateStart = new DateTime (dst_start.Year, 7, 1);
1320 if (dst_end.Month >= 7)
1321 dateEnd = new DateTime (dst_end.Year, 12, 31);
1323 dateEnd = new DateTime (dst_end.Year, 6, 30);
1326 TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
1327 TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
1328 if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
1329 adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
1331 dst_observed = false;
1333 if (daylightDisplayName != ttype.Name)
1334 daylightDisplayName = ttype.Name;
1335 if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
1336 // Round to nearest minute, since it's not possible to create an adjustment rule
1337 // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
1338 // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
1339 dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset;
1340 if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0)
1341 dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f));
1345 dst_observed = true;
1350 if (adjustmentRules.Count == 0 && !storeTransition) {
1351 TimeType t = (TimeType)time_types [0];
1352 if (standardDisplayName == null) {
1353 standardDisplayName = t.Name;
1354 baseUtcOffset = new TimeSpan (0, 0, t.Offset);
1356 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
1358 tz = CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
1361 if (storeTransition && transitions.Count > 0) {
1362 tz.transitions = transitions;
1363 tz.supportsDaylightSavingTime = true;
1369 static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
1371 var abbrevs = new Dictionary<int, string> ();
1372 int abbrev_index = 0;
1373 var sb = new StringBuilder ();
1374 for (int i = 0; i < count; i++) {
1375 char c = (char) buffer [index + i];
1379 abbrevs.Add (abbrev_index, sb.ToString ());
1380 //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
1381 for (int j = 1; j < sb.Length; j++)
1382 abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
1383 abbrev_index = i + 1;
1384 sb = new StringBuilder ();
1390 static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
1392 var types = new Dictionary<int, TimeType> (count);
1393 for (int i = 0; i < count; i++) {
1394 int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
1395 byte is_dst = buffer [index + 6 * i + 4];
1396 byte abbrev = buffer [index + 6 * i + 5];
1397 types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
1402 static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
1404 var list = new List<KeyValuePair<DateTime, TimeType>> (count);
1405 for (int i = 0; i < count; i++) {
1406 int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
1407 DateTime ttime = DateTimeFromUnixTime (unixtime);
1408 byte ttype = buffer [index + 4 * count + i];
1409 list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
1414 static DateTime DateTimeFromUnixTime (long unix_time)
1416 DateTime date_time = new DateTime (1970, 1, 1);
1417 return date_time.AddSeconds (unix_time);
1420 #region reference sources
1421 // Shortcut for TimeZoneInfo.Local.GetUtcOffset
1422 internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1425 return Local.GetUtcOffset (dateTime, out dst);
1428 internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
1431 return GetUtcOffset (dateTime, out dst);
1434 static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
1436 isDaylightSavings = false;
1437 isAmbiguousLocalDst = false;
1438 TimeSpan baseOffset = zone.BaseUtcOffset;
1440 if (zone.IsAmbiguousTime (time)) {
1441 isAmbiguousLocalDst = true;
1445 return zone.GetUtcOffset (time, out isDaylightSavings);
1451 public readonly int Offset;
1452 public readonly bool IsDst;
1455 public TimeType (int offset, bool is_dst, string abbrev)
1457 this.Offset = offset;
1458 this.IsDst = is_dst;
1462 public override string ToString ()
1464 return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;