5 // Duncan Mak (duncan@ximian.com)
6 // Ajay Kumar Dwivedi (adwiv@yahoo.com)
7 // Martin Baulig (martin@gnome.org)
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
42 public abstract class TimeZone
45 private static TimeZone currentTimeZone;
53 public static TimeZone CurrentTimeZone {
55 if (currentTimeZone == null)
56 currentTimeZone = new CurrentTimeZone ();
58 return currentTimeZone;
62 internal static void ClearCurrentTimeZone ()
64 currentTimeZone = null;
67 public abstract string DaylightName {
71 public abstract string StandardName {
76 public abstract DaylightTime GetDaylightChanges (int year);
78 public abstract TimeSpan GetUtcOffset (DateTime time);
80 public virtual bool IsDaylightSavingTime (DateTime time)
82 return IsDaylightSavingTime (time, GetDaylightChanges (time.Year));
85 public static bool IsDaylightSavingTime (DateTime time, DaylightTime daylightTimes)
87 if (daylightTimes == null)
88 throw new ArgumentNullException ("daylightTimes");
90 // If Start == End, then DST is off
91 if (daylightTimes.Start.Ticks == daylightTimes.End.Ticks)
94 //We are in the northern hemisphere.
95 if (daylightTimes.Start.Ticks < daylightTimes.End.Ticks) {
96 if (daylightTimes.Start.Ticks < time.Ticks && daylightTimes.End.Ticks > time.Ticks)
97 return true; // time lies between Start and End
100 else { // We are in the southern hemisphere.
101 if (time.Year == daylightTimes.Start.Year && time.Year == daylightTimes.End.Year)
102 if (time.Ticks < daylightTimes.End.Ticks || time.Ticks > daylightTimes.Start.Ticks)
103 return true; // time is less than End OR more than Start
109 public virtual DateTime ToLocalTime (DateTime time)
111 DaylightTime dlt = GetDaylightChanges (time.Year);
112 TimeSpan utcOffset = GetUtcOffset (time);
113 if (utcOffset.Ticks > 0) {
114 if (DateTime.MaxValue - utcOffset < time)
115 return DateTime.MaxValue;
116 //} else if (utcOffset.Ticks < 0) {
117 // LAMESPEC: MS.NET fails to check validity here
118 // it may throw ArgumentOutOfRangeException.
121 DateTime local = time.Add (utcOffset);
122 if (dlt.Delta.Ticks == 0)
125 // FIXME: check all of the combination of
126 // - basis: local-based or UTC-based
127 // - hemisphere: Northern or Southern
128 // - offset: positive or negative
130 // PST should work fine here.
131 if (local < dlt.End && dlt.End.Subtract (dlt.Delta) <= local)
133 if (local >= dlt.Start && dlt.Start.Add (dlt.Delta) > local)
134 return local.Subtract (dlt.Delta);
136 TimeSpan localOffset = GetUtcOffset (local);
137 return time.Add (localOffset);
140 public virtual DateTime ToUniversalTime (DateTime time)
142 TimeSpan offset = GetUtcOffset (time);
144 if (offset.Ticks < 0) {
145 if (DateTime.MaxValue + offset < time)
146 return DateTime.MaxValue;
147 } else if (offset.Ticks > 0) {
148 if (DateTime.MinValue + offset > time)
149 return DateTime.MinValue;
152 return new DateTime (time.Ticks - offset.Ticks);
156 internal class CurrentTimeZone : TimeZone
159 private static string daylightName;
160 private static string standardName;
162 // A yearwise cache of DaylightTime.
163 private static Hashtable daylightCache = new Hashtable (1);
165 // the offset when daylightsaving is not on.
166 private static TimeSpan utcOffsetWithOutDLS;
168 // the offset when daylightsaving is on.
169 private static TimeSpan utcOffsetWithDLS;
171 internal enum TimeZoneData
173 DaylightSavingStartIdx,
174 DaylightSavingEndIdx,
176 AdditionalDaylightOffsetIdx
179 internal enum TimeZoneNames
185 // Internal method to get timezone data.
186 // data[0]: start of daylight saving time (in DateTime ticks).
187 // data[1]: end of daylight saving time (in DateTime ticks).
188 // data[2]: utcoffset (in TimeSpan ticks).
189 // data[3]: additional offset when daylight saving (in TimeSpan ticks).
190 // name[0]: name of this timezone when not daylight saving.
191 // name[1]: name of this timezone when daylight saving.
192 [MethodImplAttribute(MethodImplOptions.InternalCall)]
193 private static extern bool GetTimeZoneData (int year, out Int64[] data, out string[] names);
196 internal CurrentTimeZone ()
202 DateTime now = new DateTime(DateTime.GetNow ());
203 if (!GetTimeZoneData (now.Year, out data, out names))
204 throw new NotSupportedException (Locale.GetText ("Can't get timezone name."));
206 standardName = Locale.GetText (names[(int)TimeZoneNames.StandardNameIdx]);
207 daylightName = Locale.GetText (names[(int)TimeZoneNames.DaylightNameIdx]);
209 utcOffsetWithOutDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]);
210 utcOffsetWithDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]
211 + data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]);
215 public override string DaylightName {
216 get { return daylightName; }
219 public override string StandardName {
220 get { return standardName; }
225 public override DaylightTime GetDaylightChanges (int year)
227 if (year < 1 || year > 9999)
228 throw new ArgumentOutOfRangeException ("year", year +
229 Locale.GetText (" is not in a range between 1 and 9999."));
231 if (daylightCache [year] == null) {
232 lock (daylightCache) {
233 if (daylightCache [year] == null) {
237 if (!GetTimeZoneData (year, out data, out names))
238 throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + year));
240 DaylightTime dlt = new DaylightTime (new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]),
241 new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]),
242 new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]));
243 daylightCache.Add (year, dlt);
248 return (DaylightTime) daylightCache [year];
251 public override TimeSpan GetUtcOffset (DateTime time)
253 if (IsDaylightSavingTime (time))
254 return utcOffsetWithDLS;
256 return utcOffsetWithOutDLS;