Fix pragma warning restore (dotnet/coreclr#26389)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / TimeZoneInfo.AdjustmentRule.cs
bloba698a68207aae841e3a494e24853a62cfa70edec
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 using System.Runtime.Serialization;
7 namespace System
9 public sealed partial class TimeZoneInfo
11 [Serializable]
12 public sealed class AdjustmentRule :
13 #nullable disable // see comment on String
14 IEquatable<AdjustmentRule>,
15 #nullable restore
16 ISerializable, IDeserializationCallback
18 private static readonly TimeSpan DaylightDeltaAdjustment = TimeSpan.FromHours(24.0);
19 private static readonly TimeSpan MaxDaylightDelta = TimeSpan.FromHours(12.0);
20 private readonly DateTime _dateStart;
21 private readonly DateTime _dateEnd;
22 private readonly TimeSpan _daylightDelta;
23 private readonly TransitionTime _daylightTransitionStart;
24 private readonly TransitionTime _daylightTransitionEnd;
25 private readonly TimeSpan _baseUtcOffsetDelta; // delta from the default Utc offset (utcOffset = defaultUtcOffset + _baseUtcOffsetDelta)
26 private readonly bool _noDaylightTransitions;
28 public DateTime DateStart => _dateStart;
30 public DateTime DateEnd => _dateEnd;
32 public TimeSpan DaylightDelta => _daylightDelta;
34 public TransitionTime DaylightTransitionStart => _daylightTransitionStart;
36 public TransitionTime DaylightTransitionEnd => _daylightTransitionEnd;
38 internal TimeSpan BaseUtcOffsetDelta => _baseUtcOffsetDelta;
40 /// <summary>
41 /// Gets a value indicating that this AdjustmentRule fixes the time zone offset
42 /// from DateStart to DateEnd without any daylight transitions in between.
43 /// </summary>
44 internal bool NoDaylightTransitions => _noDaylightTransitions;
46 internal bool HasDaylightSaving =>
47 DaylightDelta != TimeSpan.Zero ||
48 (DaylightTransitionStart != default && DaylightTransitionStart.TimeOfDay != DateTime.MinValue) ||
49 (DaylightTransitionEnd != default && DaylightTransitionEnd.TimeOfDay != DateTime.MinValue.AddMilliseconds(1));
51 public bool Equals(AdjustmentRule? other) =>
52 other != null &&
53 _dateStart == other._dateStart &&
54 _dateEnd == other._dateEnd &&
55 _daylightDelta == other._daylightDelta &&
56 _baseUtcOffsetDelta == other._baseUtcOffsetDelta &&
57 _daylightTransitionEnd.Equals(other._daylightTransitionEnd) &&
58 _daylightTransitionStart.Equals(other._daylightTransitionStart);
60 public override int GetHashCode() => _dateStart.GetHashCode();
62 private AdjustmentRule(
63 DateTime dateStart,
64 DateTime dateEnd,
65 TimeSpan daylightDelta,
66 TransitionTime daylightTransitionStart,
67 TransitionTime daylightTransitionEnd,
68 TimeSpan baseUtcOffsetDelta,
69 bool noDaylightTransitions)
71 ValidateAdjustmentRule(dateStart, dateEnd, daylightDelta,
72 daylightTransitionStart, daylightTransitionEnd, noDaylightTransitions);
74 _dateStart = dateStart;
75 _dateEnd = dateEnd;
76 _daylightDelta = daylightDelta;
77 _daylightTransitionStart = daylightTransitionStart;
78 _daylightTransitionEnd = daylightTransitionEnd;
79 _baseUtcOffsetDelta = baseUtcOffsetDelta;
80 _noDaylightTransitions = noDaylightTransitions;
83 public static AdjustmentRule CreateAdjustmentRule(
84 DateTime dateStart,
85 DateTime dateEnd,
86 TimeSpan daylightDelta,
87 TransitionTime daylightTransitionStart,
88 TransitionTime daylightTransitionEnd)
90 return new AdjustmentRule(
91 dateStart,
92 dateEnd,
93 daylightDelta,
94 daylightTransitionStart,
95 daylightTransitionEnd,
96 baseUtcOffsetDelta: TimeSpan.Zero,
97 noDaylightTransitions: false);
100 internal static AdjustmentRule CreateAdjustmentRule(
101 DateTime dateStart,
102 DateTime dateEnd,
103 TimeSpan daylightDelta,
104 TransitionTime daylightTransitionStart,
105 TransitionTime daylightTransitionEnd,
106 TimeSpan baseUtcOffsetDelta,
107 bool noDaylightTransitions)
109 AdjustDaylightDeltaToExpectedRange(ref daylightDelta, ref baseUtcOffsetDelta);
110 return new AdjustmentRule(
111 dateStart,
112 dateEnd,
113 daylightDelta,
114 daylightTransitionStart,
115 daylightTransitionEnd,
116 baseUtcOffsetDelta,
117 noDaylightTransitions);
121 // When Windows sets the daylight transition start Jan 1st at 12:00 AM, it means the year starts with the daylight saving on.
122 // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
124 internal bool IsStartDateMarkerForBeginningOfYear() =>
125 !NoDaylightTransitions &&
126 DaylightTransitionStart.Month == 1 && DaylightTransitionStart.Day == 1 && DaylightTransitionStart.TimeOfDay.Hour == 0 &&
127 DaylightTransitionStart.TimeOfDay.Minute == 0 && DaylightTransitionStart.TimeOfDay.Second == 0 &&
128 _dateStart.Year == _dateEnd.Year;
131 // When Windows sets the daylight transition end Jan 1st at 12:00 AM, it means the year ends with the daylight saving on.
132 // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
134 internal bool IsEndDateMarkerForEndOfYear() =>
135 !NoDaylightTransitions &&
136 DaylightTransitionEnd.Month == 1 && DaylightTransitionEnd.Day == 1 && DaylightTransitionEnd.TimeOfDay.Hour == 0 &&
137 DaylightTransitionEnd.TimeOfDay.Minute == 0 && DaylightTransitionEnd.TimeOfDay.Second == 0 &&
138 _dateStart.Year == _dateEnd.Year;
140 /// <summary>
141 /// Helper function that performs all of the validation checks for the factory methods and deserialization callback.
142 /// </summary>
143 private static void ValidateAdjustmentRule(
144 DateTime dateStart,
145 DateTime dateEnd,
146 TimeSpan daylightDelta,
147 TransitionTime daylightTransitionStart,
148 TransitionTime daylightTransitionEnd,
149 bool noDaylightTransitions)
151 if (dateStart.Kind != DateTimeKind.Unspecified && dateStart.Kind != DateTimeKind.Utc)
153 throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateStart));
156 if (dateEnd.Kind != DateTimeKind.Unspecified && dateEnd.Kind != DateTimeKind.Utc)
158 throw new ArgumentException(SR.Argument_DateTimeKindMustBeUnspecifiedOrUtc, nameof(dateEnd));
161 if (daylightTransitionStart.Equals(daylightTransitionEnd) && !noDaylightTransitions)
163 throw new ArgumentException(SR.Argument_TransitionTimesAreIdentical, nameof(daylightTransitionEnd));
166 if (dateStart > dateEnd)
168 throw new ArgumentException(SR.Argument_OutOfOrderDateTimes, nameof(dateStart));
171 // This cannot use UtcOffsetOutOfRange to account for the scenario where Samoa moved across the International Date Line,
172 // which caused their current BaseUtcOffset to be +13. But on the other side of the line it was UTC-11 (+1 for daylight).
173 // So when trying to describe DaylightDeltas for those times, the DaylightDelta needs
174 // to be -23 (what it takes to go from UTC+13 to UTC-10)
175 if (daylightDelta.TotalHours < -23.0 || daylightDelta.TotalHours > 14.0)
177 throw new ArgumentOutOfRangeException(nameof(daylightDelta), daylightDelta, SR.ArgumentOutOfRange_UtcOffset);
180 if (daylightDelta.Ticks % TimeSpan.TicksPerMinute != 0)
182 throw new ArgumentException(SR.Argument_TimeSpanHasSeconds, nameof(daylightDelta));
185 if (dateStart != DateTime.MinValue && dateStart.Kind == DateTimeKind.Unspecified && dateStart.TimeOfDay != TimeSpan.Zero)
187 throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateStart));
190 if (dateEnd != DateTime.MaxValue && dateEnd.Kind == DateTimeKind.Unspecified && dateEnd.TimeOfDay != TimeSpan.Zero)
192 throw new ArgumentException(SR.Argument_DateTimeHasTimeOfDay, nameof(dateEnd));
196 /// <summary>
197 /// Ensures the daylight delta is within [-12, 12] hours
198 /// </summary>>
199 private static void AdjustDaylightDeltaToExpectedRange(ref TimeSpan daylightDelta, ref TimeSpan baseUtcOffsetDelta)
201 if (daylightDelta > MaxDaylightDelta)
203 daylightDelta -= DaylightDeltaAdjustment;
204 baseUtcOffsetDelta += DaylightDeltaAdjustment;
206 else if (daylightDelta < -MaxDaylightDelta)
208 daylightDelta += DaylightDeltaAdjustment;
209 baseUtcOffsetDelta -= DaylightDeltaAdjustment;
212 System.Diagnostics.Debug.Assert(daylightDelta <= MaxDaylightDelta && daylightDelta >= -MaxDaylightDelta,
213 "DaylightDelta should not ever be more than 24h");
216 void IDeserializationCallback.OnDeserialization(object? sender)
218 // OnDeserialization is called after each instance of this class is deserialized.
219 // This callback method performs AdjustmentRule validation after being deserialized.
223 ValidateAdjustmentRule(_dateStart, _dateEnd, _daylightDelta,
224 _daylightTransitionStart, _daylightTransitionEnd, _noDaylightTransitions);
226 catch (ArgumentException e)
228 throw new SerializationException(SR.Serialization_InvalidData, e);
232 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
234 if (info == null)
236 throw new ArgumentNullException(nameof(info));
239 info.AddValue("DateStart", _dateStart); // Do not rename (binary serialization)
240 info.AddValue("DateEnd", _dateEnd); // Do not rename (binary serialization)
241 info.AddValue("DaylightDelta", _daylightDelta); // Do not rename (binary serialization)
242 info.AddValue("DaylightTransitionStart", _daylightTransitionStart); // Do not rename (binary serialization)
243 info.AddValue("DaylightTransitionEnd", _daylightTransitionEnd); // Do not rename (binary serialization)
244 info.AddValue("BaseUtcOffsetDelta", _baseUtcOffsetDelta); // Do not rename (binary serialization)
245 info.AddValue("NoDaylightTransitions", _noDaylightTransitions); // Do not rename (binary serialization)
248 private AdjustmentRule(SerializationInfo info, StreamingContext context)
250 if (info == null)
252 throw new ArgumentNullException(nameof(info));
255 _dateStart = (DateTime)info.GetValue("DateStart", typeof(DateTime))!; // Do not rename (binary serialization)
256 _dateEnd = (DateTime)info.GetValue("DateEnd", typeof(DateTime))!; // Do not rename (binary serialization)
257 _daylightDelta = (TimeSpan)info.GetValue("DaylightDelta", typeof(TimeSpan))!; // Do not rename (binary serialization)
258 _daylightTransitionStart = (TransitionTime)info.GetValue("DaylightTransitionStart", typeof(TransitionTime))!; // Do not rename (binary serialization)
259 _daylightTransitionEnd = (TransitionTime)info.GetValue("DaylightTransitionEnd", typeof(TransitionTime))!; // Do not rename (binary serialization)
261 object? o = info.GetValueNoThrow("BaseUtcOffsetDelta", typeof(TimeSpan)); // Do not rename (binary serialization)
262 if (o != null)
264 _baseUtcOffsetDelta = (TimeSpan)o;
267 o = info.GetValueNoThrow("NoDaylightTransitions", typeof(bool)); // Do not rename (binary serialization)
268 if (o != null)
270 _noDaylightTransitions = (bool)o;