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.
5 /*============================================================
10 ** This class represents the current system timezone. It is
11 ** the only meaningful implementation of the TimeZone class
12 ** available in this version.
14 ** The only TimeZone that we support in version 1 is the
15 ** CurrentTimeZone as determined by the system timezone.
18 ============================================================*/
20 using System
.Collections
;
21 using System
.Globalization
;
25 [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")]
26 internal class CurrentSystemTimeZone
: TimeZone
28 // Standard offset in ticks to the Universal time if
29 // no daylight saving is in used.
30 // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000.
31 // (1 millisecond = 10000 ticks)
32 private readonly long m_ticksOffset
;
33 private readonly string m_standardName
;
34 private readonly string m_daylightName
;
36 internal CurrentSystemTimeZone()
38 TimeZoneInfo local
= TimeZoneInfo
.Local
;
40 m_ticksOffset
= local
.BaseUtcOffset
.Ticks
;
41 m_standardName
= local
.StandardName
;
42 m_daylightName
= local
.DaylightName
;
45 public override string StandardName
=> m_standardName
;
47 public override string DaylightName
=> m_daylightName
;
49 internal long GetUtcOffsetFromUniversalTime(DateTime time
, ref bool isAmbiguousLocalDst
)
51 // Get the daylight changes for the year of the specified time.
52 TimeSpan offset
= new TimeSpan(m_ticksOffset
);
53 DaylightTime daylightTime
= GetDaylightChanges(time
.Year
);
54 isAmbiguousLocalDst
= false;
56 if (daylightTime
== null || daylightTime
.Delta
.Ticks
== 0)
61 // The start and end times represent the range of universal times that are in DST for that year.
62 // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
63 // the unusual case of a negative daylight savings delta.
64 DateTime startTime
= daylightTime
.Start
- offset
;
65 DateTime endTime
= daylightTime
.End
- offset
- daylightTime
.Delta
;
66 DateTime ambiguousStart
;
67 DateTime ambiguousEnd
;
69 if (daylightTime
.Delta
.Ticks
> 0)
71 ambiguousStart
= endTime
- daylightTime
.Delta
;
72 ambiguousEnd
= endTime
;
76 ambiguousStart
= startTime
;
77 ambiguousEnd
= startTime
- daylightTime
.Delta
;
81 if (startTime
> endTime
)
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
);
89 // In northern hemisphere, the daylight saving time starts in the middle of the year.
90 isDst
= (time
>= startTime
&& time
< endTime
);
95 offset
+= daylightTime
.Delta
;
97 // See if the resulting local time becomes ambiguous. This must be captured here or the
98 // DateTime will not be able to round-trip back to UTC accurately.
99 if (time
>= ambiguousStart
&& time
< ambiguousEnd
)
101 isAmbiguousLocalDst
= true;
107 public override DateTime
ToLocalTime(DateTime time
)
109 if (time
.Kind
== DateTimeKind
.Local
)
113 bool isAmbiguousLocalDst
= false;
114 long offset
= GetUtcOffsetFromUniversalTime(time
, ref isAmbiguousLocalDst
);
115 long tick
= time
.Ticks
+ offset
;
116 if (tick
> DateTime
.MaxTicks
)
118 return new DateTime(DateTime
.MaxTicks
, DateTimeKind
.Local
);
120 if (tick
< DateTime
.MinTicks
)
122 return new DateTime(DateTime
.MinTicks
, DateTimeKind
.Local
);
124 return new DateTime(tick
, DateTimeKind
.Local
, isAmbiguousLocalDst
);
127 public override DaylightTime
GetDaylightChanges(int year
)
129 if (year
< 1 || year
> 9999)
131 throw new ArgumentOutOfRangeException(nameof(year
), SR
.Format(SR
.ArgumentOutOfRange_Range
, 1, 9999));
134 return GetCachedDaylightChanges(year
);
137 private static DaylightTime
CreateDaylightChanges(int year
)
139 DaylightTime
? currentDaylightChanges
= null;
141 if (TimeZoneInfo
.Local
.SupportsDaylightSavingTime
)
147 foreach (TimeZoneInfo
.AdjustmentRule rule
in TimeZoneInfo
.Local
.GetAdjustmentRules())
149 if (rule
.DateStart
.Year
<= year
&& rule
.DateEnd
.Year
>= year
&& rule
.DaylightDelta
!= TimeSpan
.Zero
)
151 start
= TimeZoneInfo
.TransitionTimeToDateTime(year
, rule
.DaylightTransitionStart
);
152 end
= TimeZoneInfo
.TransitionTimeToDateTime(year
, rule
.DaylightTransitionEnd
);
153 delta
= rule
.DaylightDelta
;
155 currentDaylightChanges
= new DaylightTime(start
, end
, delta
);
161 if (currentDaylightChanges
== null)
163 currentDaylightChanges
= new DaylightTime(DateTime
.MinValue
, DateTime
.MinValue
, TimeSpan
.Zero
);
166 return currentDaylightChanges
;
169 public override TimeSpan
GetUtcOffset(DateTime time
)
171 if (time
.Kind
== DateTimeKind
.Utc
)
173 return TimeSpan
.Zero
;
177 return new TimeSpan(TimeZone
.CalculateUtcOffset(time
, GetDaylightChanges(time
.Year
)).Ticks
+ m_ticksOffset
);
181 private DaylightTime
GetCachedDaylightChanges(int year
)
183 object objYear
= (object)year
;
185 if (!m_CachedDaylightChanges
.Contains(objYear
))
187 DaylightTime currentDaylightChanges
= CreateDaylightChanges(year
);
188 lock (m_CachedDaylightChanges
)
190 if (!m_CachedDaylightChanges
.Contains(objYear
))
192 m_CachedDaylightChanges
.Add(objYear
, currentDaylightChanges
);
197 return (DaylightTime
)m_CachedDaylightChanges
[objYear
]!;
200 // The per-year information is cached in this instance value. As a result it can
201 // be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
202 private readonly Hashtable m_CachedDaylightChanges
= new Hashtable();
204 } // class CurrentSystemTimeZone