Make a copy of the old ZipLib
[mono.git] / mcs / class / corlib / System / TimeZone.cs
1 //
2 // System.TimeZone.cs
3 //
4 // Authors:
5 //   Duncan Mak (duncan@ximian.com)
6 //   Ajay Kumar Dwivedi (adwiv@yahoo.com)
7 //   Martin Baulig (martin@gnome.org)
8 //
9 // (C) Ximian, Inc.
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
38
39 namespace System
40 {
41         [Serializable]
42         public abstract class TimeZone
43         {
44                 // Fields
45                 private static TimeZone currentTimeZone;
46
47                 // Constructor
48                 protected TimeZone ()
49                 {
50                 }
51
52                 // Properties
53                 public static TimeZone CurrentTimeZone {
54                         get {
55                                 if (currentTimeZone == null)
56                                         currentTimeZone = new CurrentTimeZone ();
57
58                                 return currentTimeZone;
59                         }
60                 }
61
62                 internal static void ClearCurrentTimeZone ()
63                 {
64                         currentTimeZone = null;
65                 }
66
67                 public abstract string DaylightName {
68                         get;
69                 }
70
71                 public abstract string StandardName {
72                         get;
73                 }
74
75                 // Methods
76                 public abstract DaylightTime GetDaylightChanges (int year);
77
78                 public abstract TimeSpan GetUtcOffset (DateTime time);
79
80                 public virtual bool IsDaylightSavingTime (DateTime time)
81                 {
82                         return IsDaylightSavingTime (time, GetDaylightChanges (time.Year));
83                 }
84
85                 public static bool IsDaylightSavingTime (DateTime time, DaylightTime daylightTimes)
86                 {
87                         if (daylightTimes == null)
88                                 throw new ArgumentNullException ("daylightTimes");
89
90                         // If Start == End, then DST is off
91                         if (daylightTimes.Start.Ticks == daylightTimes.End.Ticks)
92                                 return false;
93
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
98
99                         }
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 
104                         }
105
106                         return false;
107                 }
108
109                 public virtual DateTime ToLocalTime (DateTime time)
110                 {
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.
119                         }
120
121                         DateTime local = time.Add (utcOffset);
122                         if (dlt.Delta.Ticks == 0)
123                                 return local;
124
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
129
130                         // PST should work fine here.
131                         if (local < dlt.End && dlt.End.Subtract (dlt.Delta) <= local)
132                                 return local;
133                         if (local >= dlt.Start && dlt.Start.Add (dlt.Delta) > local)
134                                 return local.Subtract (dlt.Delta);
135
136                         TimeSpan localOffset = GetUtcOffset (local);
137                         return time.Add (localOffset);
138                 }
139
140                 public virtual DateTime ToUniversalTime (DateTime time)
141                 {
142                         TimeSpan offset = GetUtcOffset (time);
143
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;
150                         }
151
152                         return new DateTime (time.Ticks - offset.Ticks);
153                 }
154         }
155
156         internal class CurrentTimeZone : TimeZone
157         {
158                 // Fields
159                 private static string daylightName;
160                 private static string standardName;
161
162                 // A yearwise cache of DaylightTime.
163                 private static Hashtable daylightCache = new Hashtable (1);
164
165                 // the offset when daylightsaving is not on.
166                 private static TimeSpan utcOffsetWithOutDLS;
167
168                 // the offset when daylightsaving is on.
169                 private static TimeSpan utcOffsetWithDLS;
170
171                 internal enum TimeZoneData
172                 {
173                         DaylightSavingStartIdx,
174                         DaylightSavingEndIdx,
175                         UtcOffsetIdx,
176                         AdditionalDaylightOffsetIdx
177                 };
178
179                 internal enum TimeZoneNames
180                 {
181                         StandardNameIdx,
182                         DaylightNameIdx
183                 };
184
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);
194
195                 // Constructor
196                 internal CurrentTimeZone ()
197                         : base ()
198                 {
199                         Int64[] data;
200                         string[] names;
201
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."));
205
206                         standardName = Locale.GetText (names[(int)TimeZoneNames.StandardNameIdx]);
207                         daylightName = Locale.GetText (names[(int)TimeZoneNames.DaylightNameIdx]);
208
209                         utcOffsetWithOutDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]);
210                         utcOffsetWithDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]
211                                 + data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]);
212                 }
213
214                 // Properties
215                 public override string DaylightName {
216                         get { return daylightName; }
217                 }
218
219                 public override string StandardName {
220                         get { return standardName; }
221                 }
222
223                 // Methods
224                 [MonoTODO]
225                 public override DaylightTime GetDaylightChanges (int year)
226                 {
227                         if (year < 1 || year > 9999)
228                                 throw new ArgumentOutOfRangeException ("year", year +
229                                         Locale.GetText (" is not in a range between 1 and 9999."));
230
231                         if (daylightCache [year] == null) {
232                                 lock (daylightCache) {
233                                         if (daylightCache [year] == null) {
234                                                 Int64[] data;
235                                                 string[] names;
236
237                                                 if (!GetTimeZoneData (year, out data, out names))
238                                                         throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + year));
239
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);
244                                         };
245                                 };
246                         }
247
248                         return (DaylightTime) daylightCache [year];
249                 }
250
251                 public override TimeSpan GetUtcOffset (DateTime time)
252                 {
253                         if (IsDaylightSavingTime (time))
254                                 return utcOffsetWithDLS;
255
256                         return utcOffsetWithOutDLS;
257                 }
258         }
259 }