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