CurrentSystemTimeZone.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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 readonly long m_ticksOffset;
  30. private readonly string m_standardName;
  31. private readonly 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 => m_standardName;
  40. public override string DaylightName => m_daylightName;
  41. internal long GetUtcOffsetFromUniversalTime(DateTime time, ref bool isAmbiguousLocalDst)
  42. {
  43. // Get the daylight changes for the year of the specified time.
  44. TimeSpan offset = new TimeSpan(m_ticksOffset);
  45. DaylightTime daylightTime = GetDaylightChanges(time.Year);
  46. isAmbiguousLocalDst = false;
  47. if (daylightTime == null || daylightTime.Delta.Ticks == 0)
  48. {
  49. return offset.Ticks;
  50. }
  51. // The start and end times represent the range of universal times that are in DST for that year.
  52. // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
  53. // the unusual case of a negative daylight savings delta.
  54. DateTime startTime = daylightTime.Start - offset;
  55. DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
  56. DateTime ambiguousStart;
  57. DateTime ambiguousEnd;
  58. if (daylightTime.Delta.Ticks > 0)
  59. {
  60. ambiguousStart = endTime - daylightTime.Delta;
  61. ambiguousEnd = endTime;
  62. }
  63. else
  64. {
  65. ambiguousStart = startTime;
  66. ambiguousEnd = startTime - daylightTime.Delta;
  67. }
  68. bool isDst;
  69. if (startTime > endTime)
  70. {
  71. // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
  72. // Note, the summer in the southern hemisphere begins late in the year.
  73. isDst = (time < endTime || time >= startTime);
  74. }
  75. else
  76. {
  77. // In northern hemisphere, the daylight saving time starts in the middle of the year.
  78. isDst = (time >= startTime && time < endTime);
  79. }
  80. if (isDst)
  81. {
  82. offset += daylightTime.Delta;
  83. // See if the resulting local time becomes ambiguous. This must be captured here or the
  84. // DateTime will not be able to round-trip back to UTC accurately.
  85. if (time >= ambiguousStart && time < ambiguousEnd)
  86. {
  87. isAmbiguousLocalDst = true;
  88. }
  89. }
  90. return offset.Ticks;
  91. }
  92. public override DateTime ToLocalTime(DateTime time)
  93. {
  94. if (time.Kind == DateTimeKind.Local)
  95. {
  96. return time;
  97. }
  98. bool isAmbiguousLocalDst = false;
  99. long offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
  100. long tick = time.Ticks + offset;
  101. if (tick > DateTime.MaxTicks)
  102. {
  103. return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
  104. }
  105. if (tick < DateTime.MinTicks)
  106. {
  107. return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
  108. }
  109. return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
  110. }
  111. public override DaylightTime GetDaylightChanges(int year)
  112. {
  113. if (year < 1 || year > 9999)
  114. {
  115. throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999));
  116. }
  117. return GetCachedDaylightChanges(year);
  118. }
  119. private static DaylightTime CreateDaylightChanges(int year)
  120. {
  121. DateTime start = DateTime.MinValue;
  122. DateTime end = DateTime.MinValue;
  123. TimeSpan delta = TimeSpan.Zero;
  124. if (TimeZoneInfo.Local.SupportsDaylightSavingTime)
  125. {
  126. foreach (TimeZoneInfo.AdjustmentRule rule in TimeZoneInfo.Local.GetAdjustmentRules())
  127. {
  128. if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero)
  129. {
  130. start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
  131. end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
  132. delta = rule.DaylightDelta;
  133. break;
  134. }
  135. }
  136. }
  137. return new DaylightTime(start, end, delta);
  138. }
  139. public override TimeSpan GetUtcOffset(DateTime time)
  140. {
  141. if (time.Kind == DateTimeKind.Utc)
  142. {
  143. return TimeSpan.Zero;
  144. }
  145. else
  146. {
  147. return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
  148. }
  149. }
  150. private DaylightTime GetCachedDaylightChanges(int year)
  151. {
  152. object objYear = (object)year;
  153. if (!m_CachedDaylightChanges.Contains(objYear))
  154. {
  155. DaylightTime currentDaylightChanges = CreateDaylightChanges(year);
  156. lock (m_CachedDaylightChanges)
  157. {
  158. if (!m_CachedDaylightChanges.Contains(objYear))
  159. {
  160. m_CachedDaylightChanges.Add(objYear, currentDaylightChanges);
  161. }
  162. }
  163. }
  164. return (DaylightTime)m_CachedDaylightChanges[objYear]!;
  165. }
  166. // The per-year information is cached in this instance value. As a result it can
  167. // be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
  168. private readonly Hashtable m_CachedDaylightChanges = new Hashtable();
  169. } // class CurrentSystemTimeZone
  170. }