summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs')
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs b/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
new file mode 100644
index 0000000000..d27d2386e2
--- /dev/null
+++ b/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
@@ -0,0 +1,251 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ public sealed partial class TimeZoneInfo
+ {
+ [Serializable]
+ public sealed class AdjustmentRule : IEquatable<AdjustmentRule>, ISerializable, IDeserializationCallback
+ {
+ private readonly DateTime _dateStart;
+ private readonly DateTime _dateEnd;
+ private readonly TimeSpan _daylightDelta;
+ private readonly TransitionTime _daylightTransitionStart;
+ private readonly TransitionTime _daylightTransitionEnd;
+ private readonly TimeSpan _baseUtcOffsetDelta; // delta from the default Utc offset (utcOffset = defaultUtcOffset + _baseUtcOffsetDelta)
+ private readonly bool _noDaylightTransitions;
+
+ public DateTime DateStart => _dateStart;
+
+ public DateTime DateEnd => _dateEnd;
+
+ public TimeSpan DaylightDelta => _daylightDelta;
+
+ public TransitionTime DaylightTransitionStart => _daylightTransitionStart;
+
+ public TransitionTime DaylightTransitionEnd => _daylightTransitionEnd;
+
+ internal TimeSpan BaseUtcOffsetDelta => _baseUtcOffsetDelta;
+
+ /// <summary>
+ /// Gets a value indicating that this AdjustmentRule fixes the time zone offset
+ /// from DateStart to DateEnd without any daylight transitions in between.
+ /// </summary>
+ internal bool NoDaylightTransitions => _noDaylightTransitions;
+
+ internal bool HasDaylightSaving =>
+ DaylightDelta != TimeSpan.Zero ||
+ (DaylightTransitionStart != default(TransitionTime) && DaylightTransitionStart.TimeOfDay != DateTime.MinValue) ||
+ (DaylightTransitionEnd != default(TransitionTime) && DaylightTransitionEnd.TimeOfDay != DateTime.MinValue.AddMilliseconds(1));
+
+ public bool Equals(AdjustmentRule other) =>
+ other != null &&
+ _dateStart == other._dateStart &&
+ _dateEnd == other._dateEnd &&
+ _daylightDelta == other._daylightDelta &&
+ _baseUtcOffsetDelta == other._baseUtcOffsetDelta &&
+ _daylightTransitionEnd.Equals(other._daylightTransitionEnd) &&
+ _daylightTransitionStart.Equals(other._daylightTransitionStart);
+
+ public override int GetHashCode() => _dateStart.GetHashCode();
+
+ private AdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ TimeSpan baseUtcOffsetDelta,
+ bool noDaylightTransitions)
+ {
+ ValidateAdjustmentRule(dateStart, dateEnd, daylightDelta,
+ daylightTransitionStart, daylightTransitionEnd, noDaylightTransitions);
+
+ _dateStart = dateStart;
+ _dateEnd = dateEnd;
+ _daylightDelta = daylightDelta;
+ _daylightTransitionStart = daylightTransitionStart;
+ _daylightTransitionEnd = daylightTransitionEnd;
+ _baseUtcOffsetDelta = baseUtcOffsetDelta;
+ _noDaylightTransitions = noDaylightTransitions;
+ }
+
+ public static AdjustmentRule CreateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd)
+ {
+ return new AdjustmentRule(
+ dateStart,
+ dateEnd,
+ daylightDelta,
+ daylightTransitionStart,
+ daylightTransitionEnd,
+ baseUtcOffsetDelta: TimeSpan.Zero,
+ noDaylightTransitions: false);
+ }
+
+ internal static AdjustmentRule CreateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ TimeSpan baseUtcOffsetDelta,
+ bool noDaylightTransitions)
+ {
+ return new AdjustmentRule(
+ dateStart,
+ dateEnd,
+ daylightDelta,
+ daylightTransitionStart,
+ daylightTransitionEnd,
+ baseUtcOffsetDelta,
+ noDaylightTransitions);
+ }
+
+ //
+ // When Windows sets the daylight transition start Jan 1st at 12:00 AM, it means the year starts with the daylight saving on.
+ // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
+ //
+ internal bool IsStartDateMarkerForBeginningOfYear() =>
+ !NoDaylightTransitions &&
+ DaylightTransitionStart.Month == 1 && DaylightTransitionStart.Day == 1 && DaylightTransitionStart.TimeOfDay.Hour == 0 &&
+ DaylightTransitionStart.TimeOfDay.Minute == 0 && DaylightTransitionStart.TimeOfDay.Second == 0 &&
+ _dateStart.Year == _dateEnd.Year;
+
+ //
+ // When Windows sets the daylight transition end Jan 1st at 12:00 AM, it means the year ends with the daylight saving on.
+ // We have to special case this value and not adjust it when checking if any date is in the daylight saving period.
+ //
+ internal bool IsEndDateMarkerForEndOfYear() =>
+ !NoDaylightTransitions &&
+ DaylightTransitionEnd.Month == 1 && DaylightTransitionEnd.Day == 1 && DaylightTransitionEnd.TimeOfDay.Hour == 0 &&
+ DaylightTransitionEnd.TimeOfDay.Minute == 0 && DaylightTransitionEnd.TimeOfDay.Second == 0 &&
+ _dateStart.Year == _dateEnd.Year;
+
+ /// <summary>
+ /// Helper function that performs all of the validation checks for the actory methods and deserialization callback.
+ /// </summary>
+ private static void ValidateAdjustmentRule(
+ DateTime dateStart,
+ DateTime dateEnd,
+ TimeSpan daylightDelta,
+ TransitionTime daylightTransitionStart,
+ TransitionTime daylightTransitionEnd,
+ bool noDaylightTransitions)
+ {
+ if (dateStart.Kind != DateTimeKind.Unspecified && dateStart.Kind != DateTimeKind.Utc)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeKindMustBeUnspecifiedOrUtc"), nameof(dateStart));
+ }
+
+ if (dateEnd.Kind != DateTimeKind.Unspecified && dateEnd.Kind != DateTimeKind.Utc)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeKindMustBeUnspecifiedOrUtc"), nameof(dateEnd));
+ }
+
+ if (daylightTransitionStart.Equals(daylightTransitionEnd) && !noDaylightTransitions)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_TransitionTimesAreIdentical"), nameof(daylightTransitionEnd));
+ }
+
+ if (dateStart > dateEnd)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_OutOfOrderDateTimes"), nameof(dateStart));
+ }
+
+ // This cannot use UtcOffsetOutOfRange to account for the scenario where Samoa moved across the International Date Line,
+ // which caused their current BaseUtcOffset to be +13. But on the other side of the line it was UTC-11 (+1 for daylight).
+ // So when trying to describe DaylightDeltas for those times, the DaylightDelta needs
+ // to be -23 (what it takes to go from UTC+13 to UTC-10)
+ if (daylightDelta.TotalHours < -23.0 || daylightDelta.TotalHours > 14.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(daylightDelta), daylightDelta, Environment.GetResourceString("ArgumentOutOfRange_UtcOffset"));
+ }
+
+ if (daylightDelta.Ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_TimeSpanHasSeconds"), nameof(daylightDelta));
+ }
+
+ if (dateStart != DateTime.MinValue && dateStart.Kind == DateTimeKind.Unspecified && dateStart.TimeOfDay != TimeSpan.Zero)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeHasTimeOfDay"), nameof(dateStart));
+ }
+
+ if (dateEnd != DateTime.MaxValue && dateEnd.Kind == DateTimeKind.Unspecified && dateEnd.TimeOfDay != TimeSpan.Zero)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeHasTimeOfDay"), nameof(dateEnd));
+ }
+ Contract.EndContractBlock();
+ }
+
+ void IDeserializationCallback.OnDeserialization(object sender)
+ {
+ // OnDeserialization is called after each instance of this class is deserialized.
+ // This callback method performs AdjustmentRule validation after being deserialized.
+
+ try
+ {
+ ValidateAdjustmentRule(_dateStart, _dateEnd, _daylightDelta,
+ _daylightTransitionStart, _daylightTransitionEnd, _noDaylightTransitions);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(Environment.GetResourceString("Serialization_InvalidData"), e);
+ }
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+ Contract.EndContractBlock();
+
+ info.AddValue("DateStart", _dateStart);
+ info.AddValue("DateEnd", _dateEnd);
+ info.AddValue("DaylightDelta", _daylightDelta);
+ info.AddValue("DaylightTransitionStart", _daylightTransitionStart);
+ info.AddValue("DaylightTransitionEnd", _daylightTransitionEnd);
+ info.AddValue("BaseUtcOffsetDelta", _baseUtcOffsetDelta);
+ info.AddValue("NoDaylightTransitions", _noDaylightTransitions);
+ }
+
+ AdjustmentRule(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ _dateStart = (DateTime)info.GetValue("DateStart", typeof(DateTime));
+ _dateEnd = (DateTime)info.GetValue("DateEnd", typeof(DateTime));
+ _daylightDelta = (TimeSpan)info.GetValue("DaylightDelta", typeof(TimeSpan));
+ _daylightTransitionStart = (TransitionTime)info.GetValue("DaylightTransitionStart", typeof(TransitionTime));
+ _daylightTransitionEnd = (TransitionTime)info.GetValue("DaylightTransitionEnd", typeof(TransitionTime));
+
+ object o = info.GetValueNoThrow("BaseUtcOffsetDelta", typeof(TimeSpan));
+ if (o != null)
+ {
+ _baseUtcOffsetDelta = (TimeSpan)o;
+ }
+
+ o = info.GetValueNoThrow("NoDaylightTransitions", typeof(bool));
+ if (o != null)
+ {
+ _noDaylightTransitions = (bool)o;
+ }
+ }
+ }
+ }
+}