TimeZone.cs 11 KB

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