2002-09-13 Nick Drochak <ndrochak@gol.com>
[mono.git] / mcs / class / corlib / System / TimeZone.cs
1 //
2 // System.TimeZone.cs
3 //
4 // Author: Duncan Mak (duncan@ximian.com)
5 //         Ajay Kumar Dwivedi (adwiv@yahoo.com)
6 //         Martin Baulig (martin@gnome.org)
7 //
8 // (C) Ximian, Inc.
9 //
10
11 using System.Collections;
12 using System.Globalization;
13 using System.Runtime.CompilerServices;
14
15 namespace System {
16         
17         [Serializable]
18         public abstract class TimeZone
19         {
20                 // Fields
21                 private static TimeZone currentTimeZone;
22                 
23                 // Constructor
24                 protected TimeZone ()
25                 {
26                 }
27
28                 // Properties
29                 public static TimeZone CurrentTimeZone
30                 {
31                         get {
32                                 if (currentTimeZone == null)
33                                         currentTimeZone = new CurrentTimeZone ();
34
35                                 return currentTimeZone;
36                         }
37                 }
38                 
39                 public abstract string DaylightName
40                 {
41                         get;
42                 }
43
44                 public abstract string StandardName
45                 {
46                         get;
47                 }
48
49                 // Methods
50                 public abstract DaylightTime GetDaylightChanges (int year);
51
52                 public abstract TimeSpan GetUtcOffset (DateTime time);
53
54                 public virtual bool IsDaylightSavingTime (DateTime time)
55                 {
56                         return IsDaylightSavingTime (time, GetDaylightChanges (time.Year));
57                 }
58
59                 public static bool IsDaylightSavingTime (DateTime time, DaylightTime daylightTimes)
60                 {
61                         if (daylightTimes == null)
62                                 throw new ArgumentNullException ("daylightTimes");
63
64                         // If Start == End, then DST is off
65                         if (daylightTimes.Start.Ticks == daylightTimes.End.Ticks)
66                                 return false;
67
68                         //We are in the northern hemisphere.                           
69                         if (daylightTimes.Start.Ticks < daylightTimes.End.Ticks) {
70                                 if (daylightTimes.Start.Ticks < time.Ticks
71                                     && daylightTimes.End.Ticks > time.Ticks)
72                                         return true; // time lies between Start and End
73
74                         } else {  // We are in the southern hemisphere.
75                                 if (time.Year == daylightTimes.Start.Year && time.Year == daylightTimes.End.Year)
76                                         if (time.Ticks < daylightTimes.End.Ticks
77                                             || time.Ticks > daylightTimes.Start.Ticks)
78                                                 return true; // time is less than End OR more than Start 
79                         }
80
81                         return false;
82                 }
83                 
84                 public virtual DateTime ToLocalTime (DateTime time)
85                 {
86                         return time + GetUtcOffset (time);
87                 }
88
89                 public virtual DateTime ToUniversalTime (DateTime time)
90                 {
91                         return time - GetUtcOffset (time);
92                 }
93         }
94
95         internal class CurrentTimeZone : TimeZone
96         {               
97                 // Fields
98                 private static string daylightName;
99                 private static string standardName;
100
101                 // A yearwise cache of DaylightTime.                 
102                 private static Hashtable daylightCache = new Hashtable (1);
103                 
104                 // the offset when daylightsaving is not on.
105                 private static TimeSpan utcOffsetWithOutDLS;
106
107                 // the offset when daylightsaving is on.
108                 private static TimeSpan utcOffsetWithDLS;
109
110                 internal enum TimeZoneData {
111                         DaylightSavingStartIdx,
112                         DaylightSavingEndIdx,
113                         UtcOffsetIdx,
114                         AdditionalDaylightOffsetIdx
115                 };
116
117                 internal enum TimeZoneNames {
118                         StandardNameIdx,
119                         DaylightNameIdx
120                 };
121
122                 // Internal method to get timezone data.
123                 //    data[0]:  start of daylight saving time (in DateTime ticks).
124                 //    data[1]:  end of daylight saving time (in DateTime ticks).
125                 //    data[2]:  utcoffset (in TimeSpan ticks).
126                 //    data[3]:  additional offset when daylight saving (in TimeSpan ticks).
127                 //    name[0]:  name of this timezone when not daylight saving.
128                 //    name[1]:  name of this timezone when daylight saving.
129                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
130                 private static extern bool GetTimeZoneData (int year,
131                                                             out Int64[] data,
132                                                             out string[] names);
133
134                 // Constructor
135                 internal CurrentTimeZone ()
136                         : base ()
137                 {
138                         Int64[] data;
139                         string[] names;
140
141                         DateTime now = new DateTime(DateTime.GetNow ());                        
142                         if (!GetTimeZoneData (now.Year, out data, out names))
143                                 throw new NotSupportedException (Locale.GetText ("Can't get timezone name"));
144
145                         standardName = Locale.GetText (names[(int)TimeZoneNames.StandardNameIdx]);
146                         daylightName = Locale.GetText (names[(int)TimeZoneNames.DaylightNameIdx]);
147
148                         utcOffsetWithOutDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx]);
149                         utcOffsetWithDLS = new TimeSpan (data[(int)TimeZoneData.UtcOffsetIdx] + data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]);
150                 }
151
152                 // Properties
153                 public override string DaylightName
154                 {
155                         get { return daylightName; }
156                 }
157                 
158                 public override string StandardName 
159                 {
160                         get { return standardName; }
161                 }
162
163                 // Methods
164                 [MonoTODO]
165                 public override DaylightTime GetDaylightChanges (int year)
166                 {
167                         if (year < 1 || year > 9999)
168                                 throw new ArgumentOutOfRangeException (year + " is not in a range between 1 and 9999.");
169
170                         if (daylightCache [year] == null) {
171                                 lock (this) {
172                                         if (daylightCache [year] == null) {
173                                                 Int64[] data;
174                                                 string[] names;
175
176                                                 if (!GetTimeZoneData (year, out data, out names))
177                                                         throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + year));
178
179                                                 DaylightTime dlt = new DaylightTime (new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]),
180                                                                                      new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]),
181                                                                                      new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]));
182                                                 daylightCache.Add (year, dlt);
183                                         };
184                                 };
185                         }
186
187                         return (DaylightTime) daylightCache [year];
188                 }
189
190                 public override TimeSpan GetUtcOffset (DateTime time)
191                 {
192                         if (IsDaylightSavingTime (time))
193                                 return utcOffsetWithDLS;
194                         
195                         return utcOffsetWithOutDLS;
196                 }
197         }
198 }