CurrentSystemTimeZone.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 represents the current system timezone. It is
  10. ** the only meaningful implementation of the TimeZone class
  11. ** available in this version.
  12. **
  13. ** The only TimeZone that we support in version 1 is the
  14. ** CurrentTimeZone as determined by the system timezone.
  15. **
  16. **
  17. ============================================================*/
  18. using System.Collections;
  19. using System.Globalization;
  20. namespace System
  21. {
  22. [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")]
  23. internal class CurrentSystemTimeZone : TimeZone
  24. {
  25. // Standard offset in ticks to the Universal time if
  26. // no daylight saving is in used.
  27. // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000.
  28. // (1 millisecond = 10000 ticks)
  29. private long m_ticksOffset;
  30. private string m_standardName;
  31. private string m_daylightName;
  32. internal CurrentSystemTimeZone()
  33. {
  34. TimeZoneInfo local = TimeZoneInfo.Local;
  35. m_ticksOffset = local.BaseUtcOffset.Ticks;
  36. m_standardName = local.StandardName;
  37. m_daylightName = local.DaylightName;
  38. }
  39. public override string StandardName
  40. {
  41. get
  42. {
  43. return m_standardName;
  44. }
  45. }
  46. public override string DaylightName
  47. {
  48. get
  49. {
  50. return m_daylightName;
  51. }
  52. }
  53. internal long GetUtcOffsetFromUniversalTime(DateTime time, ref bool isAmbiguousLocalDst)
  54. {
  55. // Get the daylight changes for the year of the specified time.
  56. TimeSpan offset = new TimeSpan(m_ticksOffset);
  57. DaylightTime daylightTime = GetDaylightChanges(time.Year);
  58. isAmbiguousLocalDst = false;
  59. if (daylightTime == null || daylightTime.Delta.Ticks == 0)
  60. {
  61. return offset.Ticks;
  62. }
  63. // The start and end times represent the range of universal times that are in DST for that year.
  64. // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
  65. // the unusual case of a negative daylight savings delta.
  66. DateTime startTime = daylightTime.Start - offset;
  67. DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
  68. DateTime ambiguousStart;
  69. DateTime ambiguousEnd;
  70. if (daylightTime.Delta.Ticks > 0)
  71. {
  72. ambiguousStart = endTime - daylightTime.Delta;
  73. ambiguousEnd = endTime;
  74. }
  75. else
  76. {
  77. ambiguousStart = startTime;
  78. ambiguousEnd = startTime - daylightTime.Delta;
  79. }
  80. bool isDst = false;
  81. if (startTime > endTime)
  82. {
  83. // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
  84. // Note, the summer in the southern hemisphere begins late in the year.
  85. isDst = (time < endTime || time >= startTime);
  86. }
  87. else
  88. {
  89. // In northern hemisphere, the daylight saving time starts in the middle of the year.
  90. isDst = (time >= startTime && time < endTime);
  91. }
  92. if (isDst)
  93. {
  94. offset += daylightTime.Delta;
  95. // See if the resulting local time becomes ambiguous. This must be captured here or the
  96. // DateTime will not be able to round-trip back to UTC accurately.
  97. if (time >= ambiguousStart && time < ambiguousEnd)
  98. {
  99. isAmbiguousLocalDst = true;
  100. }
  101. }
  102. return offset.Ticks;
  103. }
  104. public override DateTime ToLocalTime(DateTime time)
  105. {
  106. if (time.Kind == DateTimeKind.Local)
  107. {
  108. return time;
  109. }
  110. bool isAmbiguousLocalDst = false;
  111. long offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
  112. long tick = time.Ticks + offset;
  113. if (tick > DateTime.MaxTicks)
  114. {
  115. return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
  116. }
  117. if (tick < DateTime.MinTicks)
  118. {
  119. return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
  120. }
  121. return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
  122. }
  123. public override DaylightTime GetDaylightChanges(int year)
  124. {
  125. if (year < 1 || year > 9999)
  126. {
  127. throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999));
  128. }
  129. return GetCachedDaylightChanges(year);
  130. }
  131. private static DaylightTime CreateDaylightChanges(int year)
  132. {
  133. DaylightTime currentDaylightChanges = null;
  134. if (TimeZoneInfo.Local.SupportsDaylightSavingTime)
  135. {
  136. DateTime start;
  137. DateTime end;
  138. TimeSpan delta;
  139. foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules())
  140. {
  141. if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero)
  142. {
  143. start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
  144. end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
  145. delta = rule.DaylightDelta;
  146. currentDaylightChanges = new DaylightTime(start, end, delta);
  147. break;
  148. }
  149. }
  150. }
  151. if (currentDaylightChanges == null)
  152. {
  153. currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero);
  154. }
  155. return currentDaylightChanges;
  156. }
  157. public override TimeSpan GetUtcOffset(DateTime time)
  158. {
  159. if (time.Kind == DateTimeKind.Utc)
  160. {
  161. return TimeSpan.Zero;
  162. }
  163. else
  164. {
  165. return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
  166. }
  167. }
  168. private DaylightTime GetCachedDaylightChanges(int year)
  169. {
  170. object objYear = (object)year;
  171. if (!m_CachedDaylightChanges.Contains(objYear))
  172. {
  173. DaylightTime currentDaylightChanges = CreateDaylightChanges(year);
  174. lock (m_CachedDaylightChanges)
  175. {
  176. if (!m_CachedDaylightChanges.Contains(objYear))
  177. {
  178. m_CachedDaylightChanges.Add(objYear, currentDaylightChanges);
  179. }
  180. }
  181. }
  182. return (DaylightTime)m_CachedDaylightChanges[objYear];
  183. }
  184. // The per-year information is cached in in this instance value. As a result it can
  185. // be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
  186. private readonly Hashtable m_CachedDaylightChanges = new Hashtable();
  187. } // class CurrentSystemTimeZone
  188. }