2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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                 public abstract string DaylightName {
63                         get;
64                 }
65
66                 public abstract string StandardName {
67                         get;
68                 }
69
70                 // Methods
71                 public abstract DaylightTime GetDaylightChanges (int year);
72
73                 public abstract TimeSpan GetUtcOffset (DateTime time);
74
75                 public virtual bool IsDaylightSavingTime (DateTime time)
76                 {
77                         return IsDaylightSavingTime (time, GetDaylightChanges (time.Year));
78                 }
79
80                 public static bool IsDaylightSavingTime (DateTime time, DaylightTime daylightTimes)
81                 {
82                         if (daylightTimes == null)
83                                 throw new ArgumentNullException ("daylightTimes");
84
85                         // If Start == End, then DST is off
86                         if (daylightTimes.Start.Ticks == daylightTimes.End.Ticks)
87                                 return false;
88
89                         //We are in the northern hemisphere.
90                         if (daylightTimes.Start.Ticks < daylightTimes.End.Ticks) {
91                                 if (daylightTimes.Start.Ticks < time.Ticks && daylightTimes.End.Ticks > time.Ticks)
92                                         return true; // time lies between Start and End
93
94                         }
95                         else {  // We are in the southern hemisphere.
96                                 if (time.Year == daylightTimes.Start.Year && time.Year == daylightTimes.End.Year)
97                                         if (time.Ticks < daylightTimes.End.Ticks || time.Ticks > daylightTimes.Start.Ticks)
98                                                 return true; // time is less than End OR more than Start 
99                         }
100
101                         return false;
102                 }
103
104                 public virtual DateTime ToLocalTime (DateTime time)
105                 {
106                         return time + GetUtcOffset (time);
107                 }
108
109                 public virtual DateTime ToUniversalTime (DateTime time)
110                 {
111                         return time - GetUtcOffset (time);
112                 }
113         }
114
115         internal class CurrentTimeZone : TimeZone
116         {
117                 // Fields
118                 private static string daylightName;
119                 private static string standardName;
120
121                 // A yearwise cache of DaylightTime.
122                 private static Hashtable daylightCache = new Hashtable (1);
123
124                 // the offset when daylightsaving is not on.
125                 private static TimeSpan utcOffsetWithOutDLS;
126
127                 // the offset when daylightsaving is on.
128                 private static TimeSpan utcOffsetWithDLS;
129
130                 internal enum TimeZoneData
131                 {
132                         DaylightSavingStartIdx,
133                         DaylightSavingEndIdx,
134                         UtcOffsetIdx,
135                         AdditionalDaylightOffsetIdx
136                 };
137
138                 internal enum TimeZoneNames
139                 {
140                         StandardNameIdx,
141                         DaylightNameIdx
142                 };
143
144                 // Internal method to get timezone data.
145                 //    data[0]:  start of daylight saving time (in DateTime ticks).
146                 //    data[1]:  end of daylight saving time (in DateTime ticks).
147                 //    data[2]:  utcoffset (in TimeSpan ticks).
148                 //    data[3]:  additional offset when daylight saving (in TimeSpan ticks).
149                 //    name[0]:  name of this timezone when not daylight saving.
150                 //    name[1]:  name of this timezone when daylight saving.
151                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
152                 private static extern bool GetTimeZoneData (int year, out Int64[] data, out string[] names);
153
154                 // Constructor
155                 internal CurrentTimeZone ()
156                         : base ()
157                 {
158                         Int64[] data;
159                         string[] names;
160
161                         DateTime now = new DateTime(DateTime.GetNow ());                        
162                         if (!GetTimeZoneData (now.Year, out data, out names))
163                                 throw new NotSupportedException (Locale.GetText ("Can't get timezone name."));
164
165                         standardName = Locale.GetText (names[(int)TimeZoneNames.StandardNameIdx]);
166                         daylightName = Locale.GetText (names[(int)TimeZoneNames.DaylightNameIdx]);
167
168                         utcOffsetWithOutDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]);
169                         utcOffsetWithDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]
170                                 + data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]);
171                 }
172
173                 // Properties
174                 public override string DaylightName {
175                         get { return daylightName; }
176                 }
177
178                 public override string StandardName {
179                         get { return standardName; }
180                 }
181
182                 // Methods
183                 [MonoTODO]
184                 public override DaylightTime GetDaylightChanges (int year)
185                 {
186                         if (year < 1 || year > 9999)
187                                 throw new ArgumentOutOfRangeException ("year", year +
188                                         Locale.GetText (" is not in a range between 1 and 9999."));
189
190                         if (daylightCache [year] == null) {
191                                 lock (this) {
192                                         if (daylightCache [year] == null) {
193                                                 Int64[] data;
194                                                 string[] names;
195
196                                                 if (!GetTimeZoneData (year, out data, out names))
197                                                         throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + year));
198
199                                                 DaylightTime dlt = new DaylightTime (new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]),
200                                                                                      new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]),
201                                                                                      new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]));
202                                                 daylightCache.Add (year, dlt);
203                                         };
204                                 };
205                         }
206
207                         return (DaylightTime) daylightCache [year];
208                 }
209
210                 public override TimeSpan GetUtcOffset (DateTime time)
211                 {
212                         if (IsDaylightSavingTime (time))
213                                 return utcOffsetWithDLS;
214
215                         return utcOffsetWithOutDLS;
216                 }
217         }
218 }