diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs index 12c4dceca9b663..214d710a17ae66 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs @@ -59,16 +59,17 @@ public readonly partial struct DateTime private const long TicksPerMicrosecond = 10; private const long TicksPerMillisecond = TicksPerMicrosecond * MicrosecondsPerMillisecond; + private const int HoursPerDay = 24; private const long TicksPerSecond = TicksPerMillisecond * 1000; private const long TicksPerMinute = TicksPerSecond * 60; private const long TicksPerHour = TicksPerMinute * 60; - private const long TicksPerDay = TicksPerHour * 24; + private const long TicksPerDay = TicksPerHour * HoursPerDay; // Number of milliseconds per time unit private const int MillisPerSecond = 1000; private const int MillisPerMinute = MillisPerSecond * 60; private const int MillisPerHour = MillisPerMinute * 60; - private const int MillisPerDay = MillisPerHour * 24; + private const int MillisPerDay = MillisPerHour * HoursPerDay; // Number of days in a non-leap year private const int DaysPerYear = 365; @@ -90,8 +91,12 @@ public readonly partial struct DateTime internal const long MinTicks = 0; internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; - private const long MaxMillis = (long)DaysTo10000 * MillisPerDay; - private const long MaxMicroseconds = MaxMillis * MicrosecondsPerMillisecond; + private const long MaxMicroseconds = MaxTicks / TicksPerMicrosecond; + private const long MaxMillis = MaxTicks / TicksPerMillisecond; + private const long MaxSeconds = MaxTicks / TicksPerSecond; + private const long MaxMinutes = MaxTicks / TicksPerMinute; + private const long MaxHours = MaxTicks / TicksPerHour; + private const long MaxDays = (long)DaysTo10000 - 1; internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; private const long FileTimeOffset = DaysTo1601 * TicksPerDay; @@ -178,6 +183,7 @@ internal DateTime(long ticks, DateTimeKind kind, bool isAmbiguousDst) private static void ThrowMillisecondOutOfRange() => throw new ArgumentOutOfRangeException("millisecond", SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); private static void ThrowMicrosecondOutOfRange() => throw new ArgumentOutOfRangeException("microsecond", SR.Format(SR.ArgumentOutOfRange_Range, 0, MicrosecondsPerMillisecond - 1)); private static void ThrowDateArithmetic(int param) => throw new ArgumentOutOfRangeException(param switch { 0 => "value", 1 => "t", _ => "months" }, SR.ArgumentOutOfRange_DateArithmetic); + private static void ThrowAddOutOfRange() => throw new ArgumentOutOfRangeException("value", SR.ArgumentOutOfRange_AddValue); // Constructs a DateTime from a given year, month, and day. The // time-of-day of the resulting DateTime is always midnight. @@ -835,49 +841,48 @@ public DateTime Add(TimeSpan value) return AddTicks(value._ticks); } - // Returns the DateTime resulting from adding a fractional number of - // time units to this DateTime. - private DateTime Add(double value, int scale) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private DateTime AddUnits(double value, long maxUnitCount, long ticksPerUnit) { - double millis_double = value * scale + (value >= 0 ? 0.5 : -0.5); - if (millis_double <= -MaxMillis || millis_double >= MaxMillis) ThrowOutOfRange(); - return AddTicks((long)millis_double * TicksPerMillisecond); + if (Math.Abs(value) > maxUnitCount) + { + ThrowAddOutOfRange(); + } - static void ThrowOutOfRange() => throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); - } + double integralPart = Math.Truncate(value); + double fractionalPart = value - integralPart; + long ticks = (long)(integralPart) * ticksPerUnit; + ticks += (long)(fractionalPart * ticksPerUnit); - // Returns the DateTime resulting from adding a fractional number of - // days to this DateTime. The result is computed by rounding the - // fractional number of days given by value to the nearest - // millisecond, and adding that interval to this DateTime. The - // value argument is permitted to be negative. - // - public DateTime AddDays(double value) - { - return Add(value, MillisPerDay); + return AddTicks(ticks); } - // Returns the DateTime resulting from adding a fractional number of - // hours to this DateTime. The result is computed by rounding the - // fractional number of hours given by value to the nearest - // millisecond, and adding that interval to this DateTime. The - // value argument is permitted to be negative. - // - public DateTime AddHours(double value) - { - return Add(value, MillisPerHour); - } + /// + /// Returns a new that adds the specified number of days to the value of this instance. + /// + /// A number of whole and fractional days. The value parameter can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by this instance and the number of days represented by value. + /// + public DateTime AddDays(double value) => AddUnits(value, MaxDays, TicksPerDay); - // Returns the DateTime resulting from the given number of - // milliseconds to this DateTime. The result is computed by rounding - // the number of milliseconds given by value to the nearest integer, - // and adding that interval to this DateTime. The value - // argument is permitted to be negative. - // - public DateTime AddMilliseconds(double value) - { - return Add(value, 1); - } + /// + /// Returns a new that adds the specified number of hours to the value of this instance. + /// + /// A number of whole and fractional hours. The value parameter can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by this instance and the number of hours represented by value. + /// + public DateTime AddHours(double value) => AddUnits(value, MaxHours, TicksPerHour); + + /// + /// Returns a new that adds the specified number of milliseconds to the value of this instance. + /// + /// A number of whole and fractional milliseconds. The value parameter can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by this instance and the number of milliseconds represented by value. + /// + public DateTime AddMilliseconds(double value) => AddUnits(value, MaxMillis, TicksPerMillisecond); /// /// Returns a new that adds the specified number of microseconds to the value of this instance. @@ -903,28 +908,16 @@ public DateTime AddMilliseconds(double value) /// /// The resulting is less than or greater than . /// - public DateTime AddMicroseconds(double value) - { - if (value < -MaxMicroseconds || value > MaxMicroseconds) - { - ThrowOutOfRange(); - } - - return AddTicks((long)(value * TicksPerMicrosecond)); + public DateTime AddMicroseconds(double value) => AddUnits(value, MaxMicroseconds, TicksPerMicrosecond); - static void ThrowOutOfRange() => throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); - } - - // Returns the DateTime resulting from adding a fractional number of - // minutes to this DateTime. The result is computed by rounding the - // fractional number of minutes given by value to the nearest - // millisecond, and adding that interval to this DateTime. The - // value argument is permitted to be negative. - // - public DateTime AddMinutes(double value) - { - return Add(value, MillisPerMinute); - } + /// + /// Returns a new that adds the specified number of minutes to the value of this instance. + /// + /// A number of whole and fractional minutes. The value parameter can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by value. + /// + public DateTime AddMinutes(double value) => AddUnits(value, MaxMinutes, TicksPerMinute); // Returns the DateTime resulting from adding the given number of // months to this DateTime. The result is computed by incrementing @@ -961,16 +954,14 @@ public DateTime AddMonths(int months) return new DateTime(n * (ulong)TicksPerDay + UTicks % TicksPerDay | InternalKind); } - // Returns the DateTime resulting from adding a fractional number of - // seconds to this DateTime. The result is computed by rounding the - // fractional number of seconds given by value to the nearest - // millisecond, and adding that interval to this DateTime. The - // value argument is permitted to be negative. - // - public DateTime AddSeconds(double value) - { - return Add(value, MillisPerSecond); - } + /// + /// Returns a new that adds the specified number of seconds to the value of this instance. + /// + /// A number of whole and fractional seconds. The value parameter can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by this instance and the number of seconds represented by value. + /// + public DateTime AddSeconds(double value) => AddUnits(value, MaxSeconds, TicksPerSecond); // Returns the DateTime resulting from adding the given number of // 100-nanosecond ticks to this DateTime. The value argument @@ -1161,7 +1152,7 @@ internal static long DoubleDateToTicks(double value) millis += DoubleDateOffset / TicksPerMillisecond; - if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale); + if (millis < 0 || millis > MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale); return millis * TicksPerMillisecond; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs index 61d2a34123a8d9..27b34cc494ebef 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs @@ -835,7 +835,7 @@ public int ToType52() var param = (string)CurVariation.Param; object[] array0 = { "2002-12-30", "23:15:55", "2002-01-09T04:02:08", "2002-01-09T04:02:08Z", "2002-01-09Z", "2002-01-09T04:02:08-05:00", "0002-01", "2016-02-29", "9999", "9999Z", "9999-12-31T12:59:59+14:00", "9999-12-31T12:59:59-11:00", "9999-12-31T12:59:59-10:59", "9999-12-31T12:59:59+13:59", "9999-12-31T23:59:59-00:00", "9999-12-31T23:59:59+14:00", "9998-12-31T12:59:59+14:00", "9998-12-31T12:59:59-14:00", "0002", "0001Z", "0002-01-01T00:00:00-14:00", "0002-01-01T00:00:00-13:59", "0002-01-01T00:00:00+00:00", "0002-01-01T00:00:00-00:00", "2008-02-29T23:59:59-14:00", "2012-02-29T23:59:59+14:00" }; - object[] array1 = { new DateTimeOffset(2002, 12, 30, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 12, 30))), new DateTimeOffset(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55, TimeZoneInfo.Local.GetUtcOffset(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55))), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 1, 9)))).AddMilliseconds(0.1458925435), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 0, 0, 0, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, new TimeSpan(-5, 0, 0))), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(2016, 2, 29, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2016, 2, 29))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(9999, 1, 1))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-11.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-10) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, new TimeSpan(13, 59, 0)), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.Zero), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-13) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2008, 2, 29, 23, 59, 59, TimeSpan.FromHours(-14)), new DateTimeOffset(2012, 2, 29, 23, 59, 59, TimeSpan.FromHours(14)) }; + object[] array1 = { new DateTimeOffset(2002, 12, 30, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 12, 30))), new DateTimeOffset(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55, TimeZoneInfo.Local.GetUtcOffset(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55))), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 1, 9)))), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 0, 0, 0, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, new TimeSpan(-5, 0, 0))), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(2016, 2, 29, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2016, 2, 29))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(9999, 1, 1))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-11.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-10) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, new TimeSpan(13, 59, 0)), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.Zero), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-13) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2008, 2, 29, 23, 59, 59, TimeSpan.FromHours(-14)), new DateTimeOffset(2012, 2, 29, 23, 59, 59, TimeSpan.FromHours(14)) }; string[] format = { "yyyy-MM-dd", "HH:mm:ss", "yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM", "yyyy-MM-dd", "yyyy", "yyyyZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy", "yyyyZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz" }; return TestValid(array0, array1, param, format); } diff --git a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs index 7b619cc081c95d..f4dac6708279dc 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs @@ -2560,5 +2560,150 @@ public static void UnixEpoch() { VerifyDateTime(DateTime.UnixEpoch, 1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); } + + public enum DateTimeUnits + { + Microsecond, + Millisecond, + Second, + Minute, + Hour, + Day + } + + private static double MaxMicroseconds { get; } = GetMaxMicroseconds(); + + // DateTime.MaxValue.Ticks / TimeSpan.TicksPerMicrosecond gives the number 315537897599999999. + // This number cannot represented as a double number and will get rounded to 315537897600000000 which will exceed the Max of Microseconds. + // GetMaxMicroseconds just calculate the greatest double number which can be used as Microseconds Max and not exceeding 315537897599999999. + private static double GetMaxMicroseconds() + { + long max = DateTime.MaxValue.Ticks / TimeSpan.TicksPerMicrosecond; + double maxMicroseconds = max; + + while ((long)Math.Truncate(maxMicroseconds) > max) + { + max--; + maxMicroseconds = max; + }; + + return maxMicroseconds; + } + + private static long MaxMilliseconds = DateTime.MaxValue.Ticks / TimeSpan.TicksPerMillisecond; + private static long MaxSeconds = DateTime.MaxValue.Ticks / TimeSpan.TicksPerSecond; + private static long MaxMinutes = DateTime.MaxValue.Ticks / TimeSpan.TicksPerMinute; + private static long MaxHours = DateTime.MaxValue.Ticks / TimeSpan.TicksPerHour; + private static long MaxDays = DateTime.MaxValue.Ticks / TimeSpan.TicksPerDay; + + public static IEnumerable Precision_TestData() + { + yield return new object[] { 0.9999999, DateTime.MinValue, DateTimeUnits.Microsecond, false, (long)(0.9999999 * TimeSpan.TicksPerMicrosecond) }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Microsecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Millisecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Second, true, 1 /* not important as the call should throws */ }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Minute, true, 1 /* not important as the call should throws */ }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Hour, true, 1 /* not important as the call should throws */ }; + yield return new object[] { 0.9999999, DateTime.MaxValue, DateTimeUnits.Day, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Microsecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Millisecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Second, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Minute, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Hour, true, 1 /* not important as the call should throws */ }; + yield return new object[] { -0.9999999, DateTime.MinValue, DateTimeUnits.Day, true, 1 /* not important as the call should throws */ }; + + long calculatedMaxMicroseconds = (long)Math.Truncate(MaxMicroseconds) * TimeSpan.TicksPerMicrosecond + (long)((MaxMicroseconds - Math.Truncate(MaxMicroseconds)) * TimeSpan.TicksPerMicrosecond); + yield return new object[] { MaxMicroseconds, DateTime.MinValue, DateTimeUnits.Microsecond, false, calculatedMaxMicroseconds }; + yield return new object[] { calculatedMaxMicroseconds + 1, DateTime.MinValue, DateTimeUnits.Microsecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { MaxMilliseconds, DateTime.MinValue, DateTimeUnits.Millisecond, false, (long)(MaxMilliseconds * TimeSpan.TicksPerMillisecond) }; + yield return new object[] { MaxMilliseconds + 1, DateTime.MinValue, DateTimeUnits.Millisecond, true, 1 /* not important as the call should throws */ }; + yield return new object[] { MaxSeconds, DateTime.MinValue, DateTimeUnits.Second, false, (long)(MaxSeconds * TimeSpan.TicksPerSecond) }; + yield return new object[] { MaxSeconds + 1, DateTime.MinValue, DateTimeUnits.Second, true, 1 /* not important as the call should throws */ }; + yield return new object[] { MaxMinutes, DateTime.MinValue, DateTimeUnits.Minute, false, (long)(MaxMinutes * TimeSpan.TicksPerMinute) }; + yield return new object[] { MaxMinutes + 1, DateTime.MinValue, DateTimeUnits.Minute, true, 1 /* not important as the call should throws */ }; + yield return new object[] { MaxHours, DateTime.MinValue, DateTimeUnits.Hour, false, (long)(MaxHours * TimeSpan.TicksPerHour) }; + yield return new object[] { MaxHours + 1, DateTime.MinValue, DateTimeUnits.Hour, true, 1 /* not important as the call should throws */ }; + yield return new object[] { MaxDays, DateTime.MinValue, DateTimeUnits.Day, false, (long)(MaxDays * TimeSpan.TicksPerDay) }; + yield return new object[] { MaxDays + 1, DateTime.MinValue, DateTimeUnits.Day, true, 1 /* not important as the call should throws */ }; + } + + [Theory] + [MemberData(nameof(Precision_TestData))] + public void TestDateTimeCalculationPrecision(double value, DateTime initialValue, DateTimeUnits unit, bool throws, long expectedTicks) + { + // DateTime updated = default; + Assert.True(expectedTicks != 0); + + switch (unit) + { + case DateTimeUnits.Microsecond: + if (throws) + { + Assert.Throws(() => initialValue.AddMicroseconds(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddMicroseconds(value).Ticks); + break; + + case DateTimeUnits.Millisecond: + if (throws) + { + Assert.Throws(() => initialValue.AddMilliseconds(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddMilliseconds(value).Ticks); + break; + + case DateTimeUnits.Second: + if (throws) + { + Assert.Throws(() => initialValue.AddSeconds(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddSeconds(value).Ticks); + break; + + case DateTimeUnits.Minute: + if (throws) + { + Assert.Throws(() => initialValue.AddMinutes(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddMinutes(value).Ticks); + break; + + case DateTimeUnits.Hour: + if (throws) + { + Assert.Throws(() => initialValue.AddHours(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddHours(value).Ticks); + + break; + + case DateTimeUnits.Day: + if (throws) + { + Assert.Throws(() => initialValue.AddDays(value)); + return; + } + + Assert.Equal(expectedTicks, initialValue.AddDays(value).Ticks); + + break; + + default: + { + Assert.True(false, "Unexpected to come here."); + } + break; + } + } } }