TimeZone.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. /*============================================================
  5. **
  6. **
  7. **
  8. ** Purpose:
  9. ** This class is used to represent a TimeZone. It
  10. ** has methods for converting a DateTime to UTC from local time
  11. ** and to local time from UTC and methods for getting the
  12. ** standard name and daylight name of the time zone.
  13. **
  14. ** The only TimeZone that we support in version 1 is the
  15. ** CurrentTimeZone as determined by the system timezone.
  16. **
  17. **
  18. ============================================================*/
  19. using System;
  20. using System.Text;
  21. using System.Threading;
  22. using System.Collections;
  23. using System.Globalization;
  24. namespace System
  25. {
  26. [Obsolete("System.TimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo instead.")]
  27. public abstract class TimeZone
  28. {
  29. private static volatile TimeZone currentTimeZone = null;
  30. // Private object for locking instead of locking on a public type for SQL reliability work.
  31. private static object s_InternalSyncObject;
  32. private static object InternalSyncObject
  33. {
  34. get
  35. {
  36. if (s_InternalSyncObject == null)
  37. {
  38. object o = new object();
  39. Interlocked.CompareExchange<object>(ref s_InternalSyncObject, o, null);
  40. }
  41. return s_InternalSyncObject;
  42. }
  43. }
  44. protected TimeZone()
  45. {
  46. }
  47. public static TimeZone CurrentTimeZone
  48. {
  49. get
  50. {
  51. //Grabbing the cached value is required at the top of this function so that
  52. //we don't incur a race condition with the ResetTimeZone method below.
  53. TimeZone tz = currentTimeZone;
  54. if (tz == null)
  55. {
  56. lock (InternalSyncObject)
  57. {
  58. if (currentTimeZone == null)
  59. {
  60. currentTimeZone = new CurrentSystemTimeZone();
  61. }
  62. tz = currentTimeZone;
  63. }
  64. }
  65. return (tz);
  66. }
  67. }
  68. //This method is called by CultureInfo.ClearCachedData in response to control panel
  69. //change events. It must be synchronized because otherwise there is a race condition
  70. //with the CurrentTimeZone property above.
  71. internal static void ResetTimeZone()
  72. {
  73. if (currentTimeZone != null)
  74. {
  75. lock (InternalSyncObject)
  76. {
  77. currentTimeZone = null;
  78. }
  79. }
  80. }
  81. public abstract string StandardName
  82. {
  83. get;
  84. }
  85. public abstract string DaylightName
  86. {
  87. get;
  88. }
  89. public abstract TimeSpan GetUtcOffset(DateTime time);
  90. //
  91. // Converts the specified datatime to the Universal time base on the current timezone
  92. //
  93. public virtual DateTime ToUniversalTime(DateTime time)
  94. {
  95. if (time.Kind == DateTimeKind.Utc)
  96. {
  97. return time;
  98. }
  99. long tickCount = time.Ticks - GetUtcOffset(time).Ticks;
  100. if (tickCount > DateTime.MaxTicks)
  101. {
  102. return new DateTime(DateTime.MaxTicks, DateTimeKind.Utc);
  103. }
  104. if (tickCount < DateTime.MinTicks)
  105. {
  106. return new DateTime(DateTime.MinTicks, DateTimeKind.Utc);
  107. }
  108. return new DateTime(tickCount, DateTimeKind.Utc);
  109. }
  110. //
  111. // Convert the specified datetime value from UTC to the local time based on the time zone.
  112. //
  113. public virtual DateTime ToLocalTime(DateTime time)
  114. {
  115. if (time.Kind == DateTimeKind.Local)
  116. {
  117. return time;
  118. }
  119. bool isAmbiguousLocalDst = false;
  120. long offset = ((CurrentSystemTimeZone)(TimeZone.CurrentTimeZone)).GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
  121. return new DateTime(time.Ticks + offset, DateTimeKind.Local, isAmbiguousLocalDst);
  122. }
  123. // Return an array of DaylightTime which reflects the daylight saving periods in a particular year.
  124. // We currently only support having one DaylightSavingTime per year.
  125. // If daylight saving time is not used in this timezone, null will be returned.
  126. public abstract DaylightTime GetDaylightChanges(int year);
  127. public virtual bool IsDaylightSavingTime(DateTime time)
  128. {
  129. return (IsDaylightSavingTime(time, GetDaylightChanges(time.Year)));
  130. }
  131. // Check if the specified time is in a daylight saving time. Allows the user to
  132. // specify the array of Daylight Saving Times.
  133. public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes)
  134. {
  135. return CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero;
  136. }
  137. //
  138. // NOTENOTE: Implementation detail
  139. // In the transition from standard time to daylight saving time,
  140. // if we convert local time to Universal time, we can have the
  141. // following (take PST as an example):
  142. // Local Universal UTC Offset
  143. // ----- --------- ----------
  144. // 01:00AM 09:00 -8:00
  145. // 02:00 (=> 03:00) 10:00 -8:00 [This time doesn't actually exist, but it can be created from DateTime]
  146. // 03:00 10:00 -7:00
  147. // 04:00 11:00 -7:00
  148. // 05:00 12:00 -7:00
  149. //
  150. // So from 02:00 - 02:59:59, we should return the standard offset, instead of the daylight saving offset.
  151. //
  152. // In the transition from daylight saving time to standard time,
  153. // if we convert local time to Universal time, we can have the
  154. // following (take PST as an example):
  155. // Local Universal UTC Offset
  156. // ----- --------- ----------
  157. // 01:00AM 08:00 -7:00
  158. // 02:00 (=> 01:00) 09:00 -8:00
  159. // 02:00 10:00 -8:00
  160. // 03:00 11:00 -8:00
  161. // 04:00 12:00 -8:00
  162. //
  163. // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this.
  164. // But note that there are two 01:00 in the local time.
  165. //
  166. // And imagine if the daylight saving offset is negative (although this does not exist in real life)
  167. // In the transition from standard time to daylight saving time,
  168. // if we convert local time to Universal time, we can have the
  169. // following (take PST as an example, but the daylight saving offset is -01:00):
  170. // Local Universal UTC Offset
  171. // ----- --------- ----------
  172. // 01:00AM 09:00 -8:00
  173. // 02:00 (=> 01:00) 10:00 -9:00
  174. // 02:00 11:00 -9:00
  175. // 03:00 12:00 -9:00
  176. // 04:00 13:00 -9:00
  177. // 05:00 14:00 -9:00
  178. //
  179. // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this.
  180. //
  181. // In the transition from daylight saving time to standard time,
  182. // if we convert local time to Universal time, we can have the
  183. // following (take PST as an example, daylight saving offset is -01:00):
  184. //
  185. // Local Universal UTC Offset
  186. // ----- --------- ----------
  187. // 01:00AM 10:00 -9:00
  188. // 02:00 (=> 03:00) 11:00 -9:00
  189. // 03:00 11:00 -8:00
  190. // 04:00 12:00 -8:00
  191. // 05:00 13:00 -8:00
  192. // 06:00 14:00 -8:00
  193. //
  194. // So from 02:00 - 02:59:59, we should return the daylight saving offset, instead of the standard offset.
  195. //
  196. internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes)
  197. {
  198. if (daylightTimes == null)
  199. {
  200. return TimeSpan.Zero;
  201. }
  202. DateTimeKind kind = time.Kind;
  203. if (kind == DateTimeKind.Utc)
  204. {
  205. return TimeSpan.Zero;
  206. }
  207. DateTime startTime;
  208. DateTime endTime;
  209. // startTime and endTime represent the period from either the start of DST to the end and includes the
  210. // potentially overlapped times
  211. startTime = daylightTimes.Start + daylightTimes.Delta;
  212. endTime = daylightTimes.End;
  213. // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the
  214. // clock back. It is theoretically possible to have a positive delta, (which would really be daylight
  215. // reduction time), where you would have to wind the clock back in the begnning.
  216. DateTime ambiguousStart;
  217. DateTime ambiguousEnd;
  218. if (daylightTimes.Delta.Ticks > 0)
  219. {
  220. ambiguousStart = endTime - daylightTimes.Delta;
  221. ambiguousEnd = endTime;
  222. }
  223. else
  224. {
  225. ambiguousStart = startTime;
  226. ambiguousEnd = startTime - daylightTimes.Delta;
  227. }
  228. bool isDst = false;
  229. if (startTime > endTime)
  230. {
  231. // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
  232. // Note, the summer in the southern hemisphere begins late in the year.
  233. if (time >= startTime || time < endTime)
  234. {
  235. isDst = true;
  236. }
  237. }
  238. else if (time >= startTime && time < endTime)
  239. {
  240. // In northern hemisphere, the daylight saving time starts in the middle of the year.
  241. isDst = true;
  242. }
  243. // If this date was previously converted from a UTC date and we were able to detect that the local
  244. // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity.
  245. if (isDst && time >= ambiguousStart && time < ambiguousEnd)
  246. {
  247. isDst = time.IsAmbiguousDaylightSavingTime();
  248. }
  249. if (isDst)
  250. {
  251. return daylightTimes.Delta;
  252. }
  253. return TimeSpan.Zero;
  254. }
  255. }
  256. }