diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs index b3cc06c3725fa..f2a64919d341a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs @@ -61,7 +61,10 @@ public readonly partial struct DateTime ISubtractionOperators { // Number of 100ns ticks per time unit - private const long TicksPerMillisecond = 10000; + internal const int MicrosecondsPerMillisecond = 1000; + private const long TicksPerMicrosecond = 10; + private const long TicksPerMillisecond = TicksPerMicrosecond * MicrosecondsPerMillisecond; + private const long TicksPerSecond = TicksPerMillisecond * 1000; private const long TicksPerMinute = TicksPerSecond * 60; private const long TicksPerHour = TicksPerMinute * 60; @@ -94,6 +97,7 @@ 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; internal const long UnixEpochTicks = DaysTo1970 * TicksPerDay; private const long FileTimeOffset = DaysTo1601 * TicksPerDay; @@ -179,6 +183,7 @@ internal DateTime(long ticks, DateTimeKind kind, bool isAmbiguousDst) private static void ThrowTicksOutOfRange() => throw new ArgumentOutOfRangeException("ticks", SR.ArgumentOutOfRange_DateTimeBadTicks); private static void ThrowInvalidKind() => throw new ArgumentException(SR.Argument_InvalidDateTimeKind, "kind"); 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); // Constructs a DateTime from a given year, month, and day. The @@ -198,6 +203,81 @@ public DateTime(int year, int month, int day, Calendar calendar) { } + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through the number of years in ). + /// The month (1 through the number of months in ). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The calendar that is used to interpret , , and . + /// + /// One of the enumeration values that indicates whether , , , + /// , , , and + /// specify a local time, Coordinated Universal Time (UTC), or neither. + /// + /// is + /// + /// + /// is not in the range supported by . + /// + /// -or- + /// + /// is less than 1 or greater than the number of months in . + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// + /// is not one of the values. + /// + /// + /// The allowable values for , , and parameters + /// depend on the parameter. An exception is thrown if the specified date and time cannot + /// be expressed using . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar!!, DateTimeKind kind) + { + if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange(); + if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind(); + + if (second != 60 || !s_systemSupportsLeapSeconds) + { + ulong ticks = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks; + _dateData = ticks | ((ulong)kind << KindShift); + } + else + { + // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. + this = new DateTime(year, month, day, hour, minute, 59, millisecond, calendar, kind); + ValidateLeapSecond(); + } + } + // Constructs a DateTime from a given year, month, day, hour, // minute, and second. // @@ -249,29 +329,438 @@ public DateTime(int year, int month, int day, int hour, int minute, int second, } } - // Constructs a DateTime from a given year, month, day, hour, - // minute, and second. - // - public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// + /// is less than 1 or greater than 9999. + /// + /// -or- + /// + /// is less than 1 or greater than 12. + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// The property is initialized to . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) => + _dateData = Init(year, month, day, hour, minute, second, millisecond); + + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// + /// One of the enumeration values that indicates whether , , , + /// , , , and + /// specify a local time, Coordinated Universal Time (UTC), or neither. + /// + /// is less than 1 or greater than 9999. + /// + /// -or- + /// + /// is less than 1 or greater than 12. + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// + /// is not one of the values. + /// + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) => + _dateData = Init(year, month, day, hour, minute, second, millisecond, kind); + + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through the number of years in ). + /// The month (1 through the number of months in ). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The calendar that is used to interpret , , and . + /// + /// is + /// + /// + /// is not in the range supported by . + /// + /// -or- + /// + /// is less than 1 or greater than the number of months in . + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// + /// The allowable values for , , and parameters + /// depend on the parameter. An exception is thrown if the specified date and time cannot + /// be expressed using . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar!!) { - if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange(); - if (second != 60 || !s_systemSupportsLeapSeconds) { - ulong ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); - ticks += (uint)millisecond * (uint)TicksPerMillisecond; - Debug.Assert(ticks <= MaxTicks, "Input parameters validated already"); - _dateData = ticks; + _dateData = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks; } else { // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. - this = new DateTime(year, month, day, hour, minute, 59, millisecond); + this = new DateTime(year, month, day, hour, minute, 59, millisecond, calendar); ValidateLeapSecond(); } } - public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The milliseconds (0 through 900). + /// + /// is less than 1 or greater than 9999. + /// + /// -or- + /// + /// is less than 1 or greater than 12. + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// -or- + /// + /// is less than 0 or greater than 900. + /// + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// The property is initialized to . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) + : this(year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Unspecified) + { + } + + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The milliseconds (0 through 900). + /// + /// One of the enumeration values that indicates whether , , , + /// , , , and + /// specify a local time, Coordinated Universal Time (UTC), or neither. + /// + /// is less than 1 or greater than 9999. + /// + /// -or- + /// + /// is less than 1 or greater than 12. + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// -or- + /// + /// is less than 0 or greater than 900. + /// + /// + /// is not one of the values. + /// + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, DateTimeKind kind) + { + ulong ticks = Init(year, month, day, hour, minute, second, millisecond, kind); + if ((uint)microsecond >= MicrosecondsPerMillisecond) ThrowMicrosecondOutOfRange(); + + ulong newTicks = (ticks & TicksMask) + (ulong)(microsecond * TicksPerMicrosecond); + + Debug.Assert(newTicks <= MaxTicks); + _dateData = newTicks | (ticks & FlagsMask); + } + + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through the number of years in ). + /// The month (1 through the number of months in ). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The milliseconds (0 through 900). + /// The calendar that is used to interpret , , and . + /// + /// is + /// + /// + /// is not in the range supported by . + /// + /// -or- + /// + /// is less than 1 or greater than the number of months in . + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// -or- + /// + /// is less than 0 or greater than 900. + /// + /// + /// The allowable values for , , and parameters + /// depend on the parameter. An exception is thrown if the specified date and time cannot + /// be expressed using . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, Calendar calendar!!) + : this(year, month, day, hour, minute, second, millisecond, microsecond, calendar, DateTimeKind.Unspecified) + { + } + + /// + /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, + /// millisecond, and Coordinated Universal Time (UTC) or local time for the specified calendar. + /// + /// The year (1 through the number of years in ). + /// The month (1 through the number of months in ). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The milliseconds (0 through 900). + /// The calendar that is used to interpret , , and . + /// + /// One of the enumeration values that indicates whether , , , + /// , , , and + /// specify a local time, Coordinated Universal Time (UTC), or neither. + /// + /// is + /// + /// + /// is not in the range supported by . + /// + /// -or- + /// + /// is less than 1 or greater than the number of months in . + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// -or- + /// + /// is less than 0 or greater than 900. + /// + /// + /// is not one of the values. + /// + /// + /// The allowable values for , , and parameters + /// depend on the parameter. An exception is thrown if the specified date and time cannot + /// be expressed using . + /// + /// For applications in which portability of date and time data or a limited degree of time zone awareness is important, + /// you can use the corresponding constructor. + /// + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, Calendar calendar!!, DateTimeKind kind) + : this(year, month, day, hour, minute, second, millisecond, calendar, kind) + { + if ((uint)microsecond >= MicrosecondsPerMillisecond) + { + ThrowMicrosecondOutOfRange(); + } + _dateData = new DateTime(_dateData).AddMicroseconds(microsecond)._dateData; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Init(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind = DateTimeKind.Unspecified) { if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange(); if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind(); @@ -281,49 +770,18 @@ public DateTime(int year, int month, int day, int hour, int minute, int second, ulong ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += (uint)millisecond * (uint)TicksPerMillisecond; Debug.Assert(ticks <= MaxTicks, "Input parameters validated already"); - _dateData = ticks | ((ulong)kind << KindShift); - } - else - { - // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. - this = new DateTime(year, month, day, hour, minute, 59, millisecond, kind); - ValidateLeapSecond(); - } - } - - // Constructs a DateTime from a given year, month, day, hour, - // minute, and second for the specified calendar. - // - public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar!!) - { - if (second != 60 || !s_systemSupportsLeapSeconds) - { - _dateData = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks; - } - else - { - // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. - this = new DateTime(year, month, day, hour, minute, 59, millisecond, calendar); - ValidateLeapSecond(); + return ticks | ((ulong)kind << KindShift); } - } - public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar!!, DateTimeKind kind) - { - if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange(); - if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind(); + // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. + DateTime dt = new(year, month, day, hour, minute, 59, millisecond, kind); - if (second != 60 || !s_systemSupportsLeapSeconds) - { - ulong ticks = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks; - _dateData = ticks | ((ulong)kind << KindShift); - } - else + if (!IsValidTimeWithLeapSeconds(year, month, day, hour, 59, kind)) { - // if we have a leap second, then we adjust it to 59 so that DateTime will consider it the last in the specified minute. - this = new DateTime(year, month, day, hour, minute, 59, millisecond, calendar, kind); - ValidateLeapSecond(); + ThrowHelper.ThrowArgumentOutOfRange_BadHourMinuteSecond(); } + + return dt._dateData; } private void ValidateLeapSecond() @@ -422,6 +880,42 @@ public DateTime AddMilliseconds(double value) return Add(value, 1); } + /// + /// Returns a new that adds the specified number of microseconds to the value of this instance. + /// + /// + /// A number of whole and fractional microseconds. + /// The parameter can be negative or positive. + /// Note that this value is rounded to the nearest integer. + /// + /// + /// An object whose value is the sum of the date and time represented + /// by this instance and the number of microseconds represented by . + /// + /// + /// This method does not change the value of this . Instead, it returns a new + /// whose value is the result of this operation. + /// + /// The fractional part of value is the fractional part of a microsecond. + /// For example, 4.5 is equivalent to 4 microseconds and 50 ticks, where one microseconds = 10 ticks. + /// + /// The value parameter is rounded to the nearest integer. + /// + /// + /// The resulting is less than or greater than . + /// + public DateTime AddMicroseconds(double value) + { + if (value < -MaxMicroseconds || value > MaxMicroseconds) + { + ThrowOutOfRange(); + } + + return AddTicks((long)(value * 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 @@ -625,6 +1119,19 @@ internal static ulong TimeToTicks(int hour, int minute, int second, int millisec return ticks; } + internal static ulong TimeToTicks(int hour, int minute, int second, int millisecond, int microsecond) + { + ulong ticks = TimeToTicks(hour, minute, second, millisecond); + + if ((uint)microsecond >= MicrosecondsPerMillisecond) ThrowMicrosecondOutOfRange(); + + ticks += (uint)microsecond * (uint)TicksPerMicrosecond; + + Debug.Assert(ticks <= MaxTicks, "Input parameters validated already"); + + return ticks; + } + // Returns the number of days in the month given by the year and // month arguments. // @@ -1018,6 +1525,16 @@ public DateTimeKind Kind // public int Millisecond => (int)((UTicks / TicksPerMillisecond) % 1000); + /// + /// The microseconds component, expressed as a value between 0 and 999. + /// + public int Microsecond => (int)((UTicks / TicksPerMicrosecond) % 1000); + + /// + /// The nanoseconds component, expressed as a value between 0 and 900. + /// + public int Nanosecond => (int)(UTicks % TicksPerMicrosecond) * 100; + // Returns the minute part of this DateTime. The returned value is // an integer between 0 and 59. // diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs index 61c57ed07c9a1..a3f6843d8ec21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs @@ -188,6 +188,145 @@ public DateTimeOffset(int year, int month, int day, int hour, int minute, int se } } + /// + /// Initializes a new instance of the structure using the + /// specified , , , , , + /// , , and . + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The microseconds (0 through 999). + /// The time's offset from Coordinated Universal Time (UTC). + /// + /// does not represent whole minutes. + /// + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// + /// is less than 1 or greater than 9999. + /// + /// -or- + /// + /// is less than 1 or greater than 12. + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// -or- + /// + /// is less than 0 or greater than 900. + /// + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, TimeSpan offset) + : this(year, month, day, hour, minute, second, millisecond, offset) + { + if ((uint)microsecond >= DateTime.MicrosecondsPerMillisecond) + { + throw new ArgumentOutOfRangeException(nameof(microsecond), SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + _dateTime = _dateTime.AddMicroseconds(microsecond); + } + + /// + /// Initializes a new instance of the structure using the + /// specified , , , , , + /// , , and . + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in ). + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The milliseconds (0 through 999). + /// The microseconds (0 through 999). + /// The calendar that is used to interpret , , and . + /// The time's offset from Coordinated Universal Time (UTC). + /// + /// This constructor interprets , and as a year, month and day + /// in the Gregorian calendar. To instantiate a value by using the year, month and day in another calendar, call + /// the constructor. + /// + /// + /// does not represent whole minutes. + /// + /// + /// is not in the range supported by . + /// + /// -or- + /// + /// is less than 1 or greater than the number of months in . + /// + /// -or- + /// + /// is less than 1 or greater than the number of days in . + /// + /// -or- + /// + /// is less than 0 or greater than 23. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 59. + /// + /// -or- + /// + /// is less than 0 or greater than 999. + /// + /// -or- + /// + /// is less than 0 or greater than 900. + /// + /// -or- + /// + /// is less than -14 hours or greater than 14 hours. + /// + /// -or- + /// + /// The , , and parameters + /// cannot be represented as a date and time value. + /// + /// -or- + /// + /// The property is earlier than or later than . + /// + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, Calendar calendar, TimeSpan offset) + : this(year, month, day, hour, minute, second, millisecond, calendar, offset) + { + if ((uint)microsecond >= DateTime.MicrosecondsPerMillisecond) + { + throw new ArgumentOutOfRangeException(nameof(microsecond), SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + _dateTime = _dateTime.AddMicroseconds(microsecond); + } + // Returns a DateTimeOffset representing the current date and time. The // resolution of the returned value depends on the system timer. public static DateTimeOffset Now => ToLocalTime(DateTime.UtcNow, true); @@ -256,6 +395,26 @@ public DateTimeOffset ToOffset(TimeSpan offset) => // public int Millisecond => ClockDateTime.Millisecond; + /// + /// Gets the microsecond component of the time represented by the current object. + /// + /// + /// If you rely on properties such as or to accurately track the number of elapsed microseconds, + /// the precision of the time's microseconds component depends on the resolution of the system clock. + /// On Windows NT 3.5 and later, and Windows Vista operating systems, the clock's resolution is approximately 10000-15000 microseconds. + /// + public int Microsecond => ClockDateTime.Microsecond; + + /// + /// Gets the nanosecond component of the time represented by the current object. + /// + /// + /// If you rely on properties such as or to accurately track the number of elapsed nanosecond, + /// the precision of the time's nanosecond component depends on the resolution of the system clock. + /// On Windows NT 3.5 and later, and Windows Vista operating systems, the clock's resolution is approximately 10000000-15000000 nanoseconds. + /// + public int Nanosecond => ClockDateTime.Nanosecond; + // Returns the minute part of this DateTimeOffset. The returned value is // an integer between 0 and 59. // @@ -324,6 +483,33 @@ public DateTimeOffset AddHours(double hours) => public DateTimeOffset AddMilliseconds(double milliseconds) => new DateTimeOffset(ClockDateTime.AddMilliseconds(milliseconds), Offset); + /// + /// Returns a new object that adds a specified number of microseconds to the value of this instance. + /// + /// A number of whole and fractional microseconds. The number can be negative or positive. + /// + /// An object whose value is the sum of the date and time represented by the current object and the number + /// of whole microseconds represented by . + /// + /// + /// The fractional part of value is the fractional part of a microsecond. + /// For example, 4.5 is equivalent to 4 microseconds and 50 ticks, where one microseconds = 10 ticks. + /// However, is rounded to the nearest microsecond; all values of .5 or greater are rounded up. + /// + /// Because a object does not represent the date and time in a specific time zone, + /// the method does not consider a particular time zone's adjustment rules + /// when it performs date and time arithmetic. + /// + /// + /// The resulting value is less than + /// + /// -or- + /// + /// The resulting value is greater than + /// + public DateTimeOffset AddMicroseconds(double microseconds) => + new DateTimeOffset(ClockDateTime.AddMicroseconds(microseconds), Offset); + // Returns the DateTimeOffset resulting from adding a fractional number of // minutes to this DateTimeOffset. The result is computed by rounding the // fractional number of minutes given by value to the nearest diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs index 9169cdcae0bb7..251005ad2c724 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs @@ -65,7 +65,17 @@ public TimeOnly(int hour, int minute, int second) : this(DateTime.TimeToTicks(ho /// The minutes (0 through 59). /// The seconds (0 through 59). /// The millisecond (0 through 999). - public TimeOnly(int hour, int minute, int second, int millisecond) : this(DateTime.TimeToTicks(hour, minute, second, millisecond)) {} + public TimeOnly(int hour, int minute, int second, int millisecond) : this(DateTime.TimeToTicks(hour, minute, second, millisecond)) { } + + /// + /// Initializes a new instance of the structure to the specified hour, minute, second, and millisecond. + /// + /// The hours (0 through 23). + /// The minutes (0 through 59). + /// The seconds (0 through 59). + /// The millisecond (0 through 999). + /// The microsecond (0 through 999). + public TimeOnly(int hour, int minute, int second, int millisecond, int microsecond) : this(DateTime.TimeToTicks(hour, minute, second, millisecond, microsecond)) { } /// /// Initializes a new instance of the TimeOnly structure using a specified number of ticks. @@ -104,6 +114,16 @@ public TimeOnly(long ticks) /// public int Millisecond => new TimeSpan(_ticks).Milliseconds; + /// + /// Gets the microsecond component of the time represented by this instance. + /// + public int Microsecond => new TimeSpan(_ticks).Microseconds; + + /// + /// Gets the nanosecond component of the time represented by this instance. + /// + public int Nanosecond => new TimeSpan(_ticks).Nanoseconds; + /// /// Gets the number of ticks that represent the time of this instance. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs index 346f7c36670bd..4a7ae81d011bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs @@ -45,7 +45,29 @@ public readonly struct TimeSpan IUnaryNegationOperators, IUnaryPlusOperators { - public const long TicksPerMillisecond = 10000; + /// + /// Represents the number of nanoseconds per tick. This field is constant. + /// + /// + /// The value of this constant is 100. + /// + public const long NanosecondsPerTick = 100; + + /// + /// Represents the number of ticks in 1 microsecond. This field is constant. + /// + /// + /// The value of this constant is 10. + /// + public const long TicksPerMicrosecond = 10; + + /// + /// Represents the number of ticks in 1 millisecond. This field is constant. + /// + /// + /// The value of this constant is 10 thousand; that is, 10,000. + /// + public const long TicksPerMillisecond = TicksPerMicrosecond * 1000; public const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000 @@ -61,6 +83,9 @@ public readonly struct TimeSpan internal const long MaxMilliSeconds = long.MaxValue / TicksPerMillisecond; internal const long MinMilliSeconds = long.MinValue / TicksPerMillisecond; + internal const long MaxMicroSeconds = long.MaxValue / TicksPerMicrosecond; + internal const long MinMicroSeconds = long.MinValue / TicksPerMicrosecond; + internal const long TicksPerTenthSecond = TicksPerMillisecond * 100; public static readonly TimeSpan Zero = new TimeSpan(0); @@ -87,12 +112,50 @@ public TimeSpan(int days, int hours, int minutes, int seconds) { } - public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) - { - long totalMilliSeconds = ((long)days * 3600 * 24 + (long)hours * 3600 + (long)minutes * 60 + seconds) * 1000 + milliseconds; - if (totalMilliSeconds > MaxMilliSeconds || totalMilliSeconds < MinMilliSeconds) + /// + /// Initializes a new instance of the structure to a specified number of + /// days, hours, minutes, seconds, and milliseconds. + /// + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + /// Number of milliseconds. + /// + /// The specified , , , + /// and are converted to ticks, and that value initializes this instance. + /// + /// + /// The parameters specify a value less than or greater than + /// + public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) : + this(days, hours, minutes, seconds, milliseconds, 0) + { + } + + /// + /// Initializes a new instance of the structure to a specified number of + /// days, hours, minutes, seconds, and milliseconds. + /// + /// Number of days. + /// Number of hours. + /// Number of minutes. + /// Number of seconds. + /// Number of milliseconds. + /// Number of microseconds. + /// + /// The specified , , , + /// and are converted to ticks, and that value initializes this instance. + /// + /// + /// The parameters specify a value less than or greater than + /// + public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds) + { + long totalMicroseconds = (((long)days * 3600 * 24 + (long)hours * 3600 + (long)minutes * 60 + seconds) * 1000 + milliseconds) * 1000 + microseconds; + if (totalMicroseconds > MaxMicroSeconds || totalMicroseconds < MinMicroSeconds) ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong(); - _ticks = (long)totalMilliSeconds * TicksPerMillisecond; + _ticks = totalMicroseconds * TicksPerMicrosecond; } public long Ticks => _ticks; @@ -103,6 +166,24 @@ public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) public int Milliseconds => (int)((_ticks / TicksPerMillisecond) % 1000); + /// + /// Gets the microseconds component of the time interval represented by the current structure. + /// + /// + /// The property represents whole microseconds, whereas the + /// property represents whole and fractional microseconds. + /// + public int Microseconds => (int)((_ticks / TicksPerMicrosecond) % 1000); + + /// + /// Gets the nanoseconds component of the time interval represented by the current structure. + /// + /// + /// The property represents whole nanoseconds, whereas the + /// property represents whole and fractional nanoseconds. + /// + public int Nanoseconds => (int)((_ticks % TicksPerMicrosecond) * 100); + public int Minutes => (int)((_ticks / TicksPerMinute) % 60); public int Seconds => (int)((_ticks / TicksPerSecond) % 60); @@ -126,6 +207,30 @@ public double TotalMilliseconds } } + /// + /// Gets the value of the current structure expressed in whole and fractional microseconds. + /// + /// + /// This property converts the value of this instance from ticks to microseconds. + /// This number might include whole and fractional microseconds. + /// + /// The property represents whole and fractional microseconds, + /// whereas the property represents whole microseconds. + /// + public double TotalMicroseconds => (double)_ticks / TicksPerMicrosecond; + + /// + /// Gets the value of the current structure expressed in whole and fractional nanoseconds. + /// + /// + /// This property converts the value of this instance from ticks to nanoseconds. + /// This number might include whole and fractional nanoseconds. + /// + /// The property represents whole and fractional nanoseconds, + /// whereas the property represents whole nanoseconds. + /// + public double TotalNanoseconds => (double)_ticks * NanosecondsPerTick; + public double TotalMinutes => (double)_ticks / TicksPerMinute; public double TotalSeconds => (double)_ticks / TicksPerSecond; @@ -234,6 +339,31 @@ public static TimeSpan FromMilliseconds(double value) return Interval(value, TicksPerMillisecond); } + /// + /// Returns a that represents a specified number of microseconds. + /// + /// A number of microseconds. + /// An object that represents . + /// + /// is less than or greater than . + /// + /// -or- + /// + /// is + /// + /// -or- + /// + /// is + /// + /// + /// is equal to . + /// + public static TimeSpan FromMicroseconds(double value) + { + // ISSUE: https://github.com/dotnet/runtime/issues/66815 + return Interval(value, TicksPerMicrosecond); + } + public static TimeSpan FromMinutes(double value) { return Interval(value, TicksPerMinute); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 2db4a634b1838..1b831964c2bfe 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1513,6 +1513,10 @@ public static partial class Convert public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.DateTimeKind kind) { throw null; } public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar) { throw null; } public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.DateTimeKind kind) { throw null; } + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) { throw null; } + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.DateTimeKind kind) { throw null; } + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar) { throw null; } + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar, System.DateTimeKind kind) { throw null; } public DateTime(long ticks) { throw null; } public DateTime(long ticks, System.DateTimeKind kind) { throw null; } public System.DateTime Date { get { throw null; } } @@ -1521,9 +1525,11 @@ public static partial class Convert public int DayOfYear { get { throw null; } } public int Hour { get { throw null; } } public System.DateTimeKind Kind { get { throw null; } } + public int Microsecond { get { throw null; } } public int Millisecond { get { throw null; } } public int Minute { get { throw null; } } public int Month { get { throw null; } } + public int Nanosecond { get { throw null; } } public static System.DateTime Now { get { throw null; } } public int Second { get { throw null; } } static System.TimeSpan System.Numerics.IAdditiveIdentity.AdditiveIdentity { get { throw null; } } @@ -1537,6 +1543,7 @@ public static partial class Convert public System.DateTime Add(System.TimeSpan value) { throw null; } public System.DateTime AddDays(double value) { throw null; } public System.DateTime AddHours(double value) { throw null; } + public System.DateTime AddMicroseconds(double value) { throw null; } public System.DateTime AddMilliseconds(double value) { throw null; } public System.DateTime AddMinutes(double value) { throw null; } public System.DateTime AddMonths(int months) { throw null; } @@ -1641,6 +1648,8 @@ public enum DateTimeKind public DateTimeOffset(System.DateTime dateTime) { throw null; } public DateTimeOffset(System.DateTime dateTime, System.TimeSpan offset) { throw null; } public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, System.Globalization.Calendar calendar, System.TimeSpan offset) { throw null; } + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar, System.TimeSpan offset) { throw null; } + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset) { throw null; } public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, System.TimeSpan offset) { throw null; } public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, System.TimeSpan offset) { throw null; } public DateTimeOffset(long ticks, System.TimeSpan offset) { throw null; } @@ -1651,9 +1660,11 @@ public enum DateTimeKind public int DayOfYear { get { throw null; } } public int Hour { get { throw null; } } public System.DateTime LocalDateTime { get { throw null; } } + public int Microsecond { get { throw null; } } public int Millisecond { get { throw null; } } public int Minute { get { throw null; } } public int Month { get { throw null; } } + public int Nanosecond { get { throw null; } } public static System.DateTimeOffset Now { get { throw null; } } public System.TimeSpan Offset { get { throw null; } } public int Second { get { throw null; } } @@ -1669,6 +1680,7 @@ public enum DateTimeKind public System.DateTimeOffset Add(System.TimeSpan timeSpan) { throw null; } public System.DateTimeOffset AddDays(double days) { throw null; } public System.DateTimeOffset AddHours(double hours) { throw null; } + public System.DateTimeOffset AddMicroseconds(double microseconds) { throw null; } public System.DateTimeOffset AddMilliseconds(double milliseconds) { throw null; } public System.DateTimeOffset AddMinutes(double minutes) { throw null; } public System.DateTimeOffset AddMonths(int months) { throw null; } @@ -4538,12 +4550,15 @@ public ThreadStaticAttribute() { } public TimeOnly(int hour, int minute) { throw null; } public TimeOnly(int hour, int minute, int second) { throw null; } public TimeOnly(int hour, int minute, int second, int millisecond) { throw null; } + public TimeOnly(int hour, int minute, int second, int millisecond, int microsecond) { throw null; } public TimeOnly(long ticks) { throw null; } public int Hour { get { throw null; } } public static System.TimeOnly MaxValue { get { throw null; } } + public int Microsecond { get { throw null; } } public int Millisecond { get { throw null; } } public int Minute { get { throw null; } } public static System.TimeOnly MinValue { get { throw null; } } + public int Nanosecond { get { throw null; } } public int Second { get { throw null; } } public long Ticks { get { throw null; } } public System.TimeOnly Add(System.TimeSpan value) { throw null; } @@ -4614,8 +4629,10 @@ public TimeoutException(string? message, System.Exception? innerException) { } private readonly int _dummyPrimitive; public static readonly System.TimeSpan MaxValue; public static readonly System.TimeSpan MinValue; + public const long NanosecondsPerTick = (long)100; public const long TicksPerDay = (long)864000000000; public const long TicksPerHour = (long)36000000000; + public const long TicksPerMicrosecond = (long)10; public const long TicksPerMillisecond = (long)10000; public const long TicksPerMinute = (long)600000000; public const long TicksPerSecond = (long)10000000; @@ -4623,11 +4640,14 @@ public TimeoutException(string? message, System.Exception? innerException) { } public TimeSpan(int hours, int minutes, int seconds) { throw null; } public TimeSpan(int days, int hours, int minutes, int seconds) { throw null; } public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) { throw null; } + public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds) { throw null; } public TimeSpan(long ticks) { throw null; } public int Days { get { throw null; } } public int Hours { get { throw null; } } + public int Microseconds { get { throw null; } } public int Milliseconds { get { throw null; } } public int Minutes { get { throw null; } } + public int Nanoseconds { get { throw null; } } public int Seconds { get { throw null; } } static System.TimeSpan System.Numerics.IAdditiveIdentity.AdditiveIdentity { get { throw null; } } static System.TimeSpan System.Numerics.IMinMaxValue.MaxValue { get { throw null; } } @@ -4636,8 +4656,10 @@ public TimeoutException(string? message, System.Exception? innerException) { } public long Ticks { get { throw null; } } public double TotalDays { get { throw null; } } public double TotalHours { get { throw null; } } + public double TotalMicroseconds { get { throw null; } } public double TotalMilliseconds { get { throw null; } } public double TotalMinutes { get { throw null; } } + public double TotalNanoseconds { get { throw null; } } public double TotalSeconds { get { throw null; } } public System.TimeSpan Add(System.TimeSpan ts) { throw null; } public static int Compare(System.TimeSpan t1, System.TimeSpan t2) { throw null; } @@ -4651,6 +4673,7 @@ public TimeoutException(string? message, System.Exception? innerException) { } public static bool Equals(System.TimeSpan t1, System.TimeSpan t2) { throw null; } public static System.TimeSpan FromDays(double value) { throw null; } public static System.TimeSpan FromHours(double value) { throw null; } + public static System.TimeSpan FromMicroseconds(double value) { throw null; } public static System.TimeSpan FromMilliseconds(double value) { throw null; } public static System.TimeSpan FromMinutes(double value) { throw null; } public static System.TimeSpan FromSeconds(double value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs index 043fa669ccdb7..149442d931c27 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs @@ -14,34 +14,34 @@ public static class DateTimeOffsetTests [Fact] public static void MaxValue() { - VerifyDateTimeOffset(DateTimeOffset.MaxValue, 9999, 12, 31, 23, 59, 59, 999, TimeSpan.Zero); + VerifyDateTimeOffset(DateTimeOffset.MaxValue, 9999, 12, 31, 23, 59, 59, 999, 999, TimeSpan.Zero, 900); } [Fact] public static void MinValue() { - VerifyDateTimeOffset(DateTimeOffset.MinValue, 1, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(DateTimeOffset.MinValue, 1, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); } [Fact] public static void Ctor_Empty() { - VerifyDateTimeOffset(new DateTimeOffset(), 1, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); - VerifyDateTimeOffset(default(DateTimeOffset), 1, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(new DateTimeOffset(), 1, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(default(DateTimeOffset), 1, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); } [Fact] public static void Ctor_DateTime() { var dateTimeOffset = new DateTimeOffset(new DateTime(2012, 6, 11, 0, 0, 0, 0, DateTimeKind.Utc)); - VerifyDateTimeOffset(dateTimeOffset, 2012, 6, 11, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 2012, 6, 11, 0, 0, 0, 0, 0, TimeSpan.Zero); - dateTimeOffset = new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 4, DateTimeKind.Local)); - VerifyDateTimeOffset(dateTimeOffset, 1986, 8, 15, 10, 20, 5, 4, null); + dateTimeOffset = new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 4, 3, DateTimeKind.Local)); + VerifyDateTimeOffset(dateTimeOffset, 1986, 8, 15, 10, 20, 5, 4, 3, null); DateTimeOffset today = new DateTimeOffset(DateTime.Today); DateTimeOffset now = DateTimeOffset.Now.Date; - VerifyDateTimeOffset(today, now.Year, now.Month, now.Day, 0, 0, 0, 0, now.Offset); + VerifyDateTimeOffset(today, now.Year, now.Month, now.Day, 0, 0, 0, 0, 0, now.Offset); today = new DateTimeOffset(new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, DateTimeKind.Utc)); Assert.Equal(TimeSpan.Zero, today.Offset); @@ -60,6 +60,7 @@ public static void Ctor_DateTime_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute - 1, min.Second, min.Millisecond, DateTimeKind.Utc))); AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second - 1, min.Millisecond, DateTimeKind.Utc))); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond - 1, DateTimeKind.Utc))); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond, min.Microsecond - 1, DateTimeKind.Utc))); // DateTime > DateTimeOffset.MaxValue DateTimeOffset max = DateTimeOffset.MaxValue; @@ -70,19 +71,20 @@ public static void Ctor_DateTime_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute + 1, max.Second, max.Millisecond, DateTimeKind.Utc))); AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second + 1, max.Millisecond, DateTimeKind.Utc))); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond + 1, DateTimeKind.Utc))); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond, max.Microsecond + 1, DateTimeKind.Utc))); } [Fact] public static void Ctor_DateTime_TimeSpan() { var dateTimeOffset = new DateTimeOffset(DateTime.MinValue, TimeSpan.FromHours(-14)); - VerifyDateTimeOffset(dateTimeOffset, 1, 1, 1, 0, 0, 0, 0, TimeSpan.FromHours(-14)); + VerifyDateTimeOffset(dateTimeOffset, 1, 1, 1, 0, 0, 0, 0, 0, TimeSpan.FromHours(-14)); dateTimeOffset = new DateTimeOffset(DateTime.MaxValue, TimeSpan.FromHours(14)); - VerifyDateTimeOffset(dateTimeOffset, 9999, 12, 31, 23, 59, 59, 999, TimeSpan.FromHours(14)); + VerifyDateTimeOffset(dateTimeOffset, 9999, 12, 31, 23, 59, 59, 999, 999, TimeSpan.FromHours(14), 900); dateTimeOffset = new DateTimeOffset(new DateTime(2012, 12, 31, 13, 50, 10), TimeSpan.Zero); - VerifyDateTimeOffset(dateTimeOffset, 2012, 12, 31, 13, 50, 10, 0, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 2012, 12, 31, 13, 50, 10, 0, 0, TimeSpan.Zero); } [Fact] @@ -97,6 +99,8 @@ public static void Ctor_DateTime_TimeSpan_Invalid() AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.UtcNow, new TimeSpan(0, 0, -3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.UtcNow, new TimeSpan(0, 0, 0, 0, 3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.UtcNow, new TimeSpan(0, 0, 0, 0, -3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.UtcNow, new TimeSpan(0, 0, 0, 0, 0, 3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.UtcNow, new TimeSpan(0, 0, 0, 0, -3))); // TimeSpan is not whole minutes // DateTime < DateTimeOffset.MinValue DateTimeOffset min = DateTimeOffset.MinValue; @@ -107,6 +111,7 @@ public static void Ctor_DateTime_TimeSpan_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute - 1, min.Second, min.Millisecond, DateTimeKind.Utc), TimeSpan.Zero)); AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second - 1, min.Millisecond, DateTimeKind.Utc), TimeSpan.Zero)); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond - 1, DateTimeKind.Utc), TimeSpan.Zero)); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(new DateTime(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond, min.Microsecond - 1, DateTimeKind.Utc), TimeSpan.Zero)); // DateTime > DateTimeOffset.MaxValue DateTimeOffset max = DateTimeOffset.MaxValue; @@ -117,6 +122,7 @@ public static void Ctor_DateTime_TimeSpan_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute + 1, max.Second, max.Millisecond, DateTimeKind.Utc), TimeSpan.Zero)); AssertExtensions.Throws(null, () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second + 1, max.Millisecond, DateTimeKind.Utc), TimeSpan.Zero)); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond + 1, DateTimeKind.Utc), TimeSpan.Zero)); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(new DateTime(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond, max.Microsecond + 1, DateTimeKind.Utc), TimeSpan.Zero)); // Invalid offset AssertExtensions.Throws("offset", () => new DateTimeOffset(DateTime.Now, TimeSpan.FromTicks(1))); @@ -128,7 +134,7 @@ public static void Ctor_Long_TimeSpan() { var expected = new DateTime(1, 2, 3, 4, 5, 6, 7); var dateTimeOffset = new DateTimeOffset(expected.Ticks, TimeSpan.Zero); - VerifyDateTimeOffset(dateTimeOffset, dateTimeOffset.Year, dateTimeOffset.Month, dateTimeOffset.Day, dateTimeOffset.Hour, dateTimeOffset.Minute, dateTimeOffset.Second, dateTimeOffset.Millisecond, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, dateTimeOffset.Year, dateTimeOffset.Month, dateTimeOffset.Day, dateTimeOffset.Hour, dateTimeOffset.Minute, dateTimeOffset.Second, dateTimeOffset.Millisecond, dateTimeOffset.Microsecond, TimeSpan.Zero); } [Fact] @@ -138,6 +144,8 @@ public static void Ctor_Long_TimeSpan_Invalid() AssertExtensions.Throws("offset", () => new DateTimeOffset(0, new TimeSpan(0, 0, -3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(0, new TimeSpan(0, 0, 0, 0, 3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(0, new TimeSpan(0, 0, 0, 0, -3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(0, new TimeSpan(0, 0, 0, 0, 0, 3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(0, new TimeSpan(0, 0, 0, 0, 0, -3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(0, TimeSpan.FromHours(-15))); // TimeZone.Offset > 14 AssertExtensions.Throws("offset", () => new DateTimeOffset(0, TimeSpan.FromHours(15))); // TimeZone.Offset < -14 @@ -146,11 +154,18 @@ public static void Ctor_Long_TimeSpan_Invalid() AssertExtensions.Throws("ticks", () => new DateTimeOffset(DateTimeOffset.MaxValue.Ticks + 1, TimeSpan.Zero)); // Ticks > DateTimeOffset.MaxValue.Ticks } + [Fact] + public static void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_TimeSpan() + { + var dateTimeOffset = new DateTimeOffset(1973, 10, 6, 14, 30, 0, 500, 400, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 1973, 10, 6, 14, 30, 0, 500, 400, TimeSpan.Zero); + } + [Fact] public static void Ctor_Int_Int_Int_Int_Int_Int_Int_TimeSpan() { var dateTimeOffset = new DateTimeOffset(1973, 10, 6, 14, 30, 0, 500, TimeSpan.Zero); - VerifyDateTimeOffset(dateTimeOffset, 1973, 10, 6, 14, 30, 0, 500, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 1973, 10, 6, 14, 30, 0, 500, 0, TimeSpan.Zero); } [Fact] @@ -186,6 +201,9 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_Int_TimeSpan_Invalid() AssertExtensions.Throws("millisecond", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, -1, TimeSpan.Zero)); // Millisecond < 0 AssertExtensions.Throws("millisecond", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, 1000, TimeSpan.Zero)); // Millisecond > 999 + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, 0, -1, TimeSpan.Zero)); // Microsecond < 0 + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, 0, 1000, TimeSpan.Zero)); // Microsecond > 999 + // DateTime < DateTimeOffset.MinValue DateTimeOffset min = DateTimeOffset.MinValue; AssertExtensions.Throws(null, () => new DateTimeOffset(min.Year - 1, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond, TimeSpan.Zero)); @@ -195,6 +213,7 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_Int_TimeSpan_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(min.Year, min.Month, min.Day, min.Hour, min.Minute - 1, min.Second, min.Millisecond, TimeSpan.Zero)); AssertExtensions.Throws(null, () => new DateTimeOffset(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second - 1, min.Millisecond, TimeSpan.Zero)); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond - 1, TimeSpan.Zero)); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(min.Year, min.Month, min.Day, min.Hour, min.Minute, min.Second, min.Millisecond, min.Microsecond - 1, TimeSpan.Zero)); // DateTime > DateTimeOffset.MaxValue DateTimeOffset max = DateTimeOffset.MaxValue; @@ -205,13 +224,14 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_Int_TimeSpan_Invalid() AssertExtensions.Throws(null, () => new DateTimeOffset(max.Year, max.Month, max.Day, max.Hour, max.Minute + 1, max.Second, max.Millisecond, TimeSpan.Zero)); AssertExtensions.Throws(null, () => new DateTimeOffset(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second + 1, max.Millisecond, TimeSpan.Zero)); AssertExtensions.Throws("millisecond", () => new DateTimeOffset(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond + 1, TimeSpan.Zero)); + AssertExtensions.Throws("microsecond", () => new DateTimeOffset(max.Year, max.Month, max.Day, max.Hour, max.Minute, max.Second, max.Millisecond, max.Microsecond + 1, TimeSpan.Zero)); } [Fact] public static void Ctor_Int_Int_Int_Int_Int_Int_TimeSpan() { var dateTimeOffset = new DateTimeOffset(1973, 10, 6, 14, 30, 0, TimeSpan.Zero); - VerifyDateTimeOffset(dateTimeOffset, 1973, 10, 6, 14, 30, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 1973, 10, 6, 14, 30, 0, 0, 0, TimeSpan.Zero); } [Fact] @@ -221,6 +241,8 @@ public static void Ctor_Int_Int_Int_Int_Int_Int_TimeSpan_Invalid() AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, new TimeSpan(0, 0, -3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, new TimeSpan(0, 0, 0, 0, 3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, new TimeSpan(0, 0, 0, 0, -3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, new TimeSpan(0, 0, 0, 0, 0, 3))); // TimeSpan is not whole minutes + AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, new TimeSpan(0, 0, 0, 0, 0, -3))); // TimeSpan is not whole minutes AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, TimeSpan.FromHours(-15))); // TimeZone.Offset > 14 AssertExtensions.Throws("offset", () => new DateTimeOffset(1, 1, 1, 1, 1, 1, TimeSpan.FromHours(15))); // TimeZone.Offset < -14 @@ -268,7 +290,7 @@ public static void ImplicitCast_DateTime() { DateTime dateTime = new DateTime(2012, 6, 11, 0, 0, 0, 0, DateTimeKind.Utc); DateTimeOffset dateTimeOffset = dateTime; - VerifyDateTimeOffset(dateTimeOffset, 2012, 6, 11, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 2012, 6, 11, 0, 0, 0, 0, 0, TimeSpan.Zero); } [Fact] @@ -492,6 +514,27 @@ public static void AddMilliseconds_NewDateOutOfRange_ThrowsArgumentOutOfRangeExc AssertExtensions.Throws("value", () => DateTimeOffset.MinValue.AddMilliseconds(-1)); } + public static IEnumerable AddMicroseconds_TestData() + { + yield return new object[] { new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 70, DateTimeKind.Utc)), 10, new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 80, DateTimeKind.Utc)) }; + yield return new object[] { new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 70, DateTimeKind.Utc)), 0, new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 70, DateTimeKind.Utc)) }; + yield return new object[] { new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 70, DateTimeKind.Utc)), -10, new DateTimeOffset(new DateTime(1986, 8, 15, 10, 20, 5, 70, 60, DateTimeKind.Utc)) }; + } + + [Theory] + [MemberData(nameof(AddMicroseconds_TestData))] + public static void AddMicroseconds(DateTimeOffset dateTimeOffset, double microseconds, DateTimeOffset expected) + { + Assert.Equal(expected, dateTimeOffset.AddMicroseconds(microseconds)); + } + + [Fact] + public static void AddMicroseconds_NewDateOutOfRange_ThrowsArgumentOutOfRangeException() + { + AssertExtensions.Throws("value", () => DateTimeOffset.MaxValue.AddMicroseconds(1)); + AssertExtensions.Throws("value", () => DateTimeOffset.MinValue.AddMicroseconds(-1)); + } + public static IEnumerable AddTicks_TestData() { yield return new object[] { new DateTimeOffset(new DateTime(1000, DateTimeKind.Utc)), 10, new DateTimeOffset(new DateTime(1010, DateTimeKind.Utc)) }; @@ -1095,7 +1138,7 @@ public static void Parse_InvalidDateTimeStyle_ThrowsArgumentException(DateTimeSt Assert.Equal(default(DateTimeOffset), dateTimeOffset); } - private static void VerifyDateTimeOffset(DateTimeOffset dateTimeOffset, int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan? offset) + private static void VerifyDateTimeOffset(DateTimeOffset dateTimeOffset, int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, TimeSpan? offset, int nanosecond = 0) { Assert.Equal(year, dateTimeOffset.Year); Assert.Equal(month, dateTimeOffset.Month); @@ -1104,6 +1147,8 @@ private static void VerifyDateTimeOffset(DateTimeOffset dateTimeOffset, int year Assert.Equal(minute, dateTimeOffset.Minute); Assert.Equal(second, dateTimeOffset.Second); Assert.Equal(millisecond, dateTimeOffset.Millisecond); + Assert.Equal(microsecond, dateTimeOffset.Microsecond); + Assert.Equal(nanosecond, dateTimeOffset.Nanosecond); if (offset.HasValue) { @@ -1157,7 +1202,15 @@ public static TestTime FromSeconds(DateTimeOffset dateTimeOffset, long unixTimeS public static void Ctor_Calendar_TimeSpan() { var dateTimeOffset = new DateTimeOffset(1, 1, 1, 0, 0, 0, 0, new GregorianCalendar(),TimeSpan.Zero); - VerifyDateTimeOffset(dateTimeOffset, 1, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 1, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); + } + + + [Fact] + public static void Ctor_Calendar_TimeSpan_Microseconds() + { + var dateTimeOffset = new DateTimeOffset(1, 1, 1, 0, 0, 0, 0, 123, new GregorianCalendar(), TimeSpan.Zero); + VerifyDateTimeOffset(dateTimeOffset, 1, 1, 1, 0, 0, 0, 0, 123, TimeSpan.Zero); } public static IEnumerable ToString_MatchesExpected_MemberData() @@ -1370,7 +1423,7 @@ public static void TryFormat_MatchesExpected(DateTimeOffset dateTimeOffset, stri [Fact] public static void UnixEpoch() { - VerifyDateTimeOffset(DateTimeOffset.UnixEpoch, 1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + VerifyDateTimeOffset(DateTimeOffset.UnixEpoch, 1970, 1, 1, 0, 0, 0, 0, 0, TimeSpan.Zero); } } } diff --git a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs index a004a43ae8dab..236ba3c8e094c 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs @@ -130,6 +130,71 @@ public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Calendar_DateTimeKind(int year, VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, DateTimeKind.Local); } + public static IEnumerable Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_TestData() + { + yield return new object[] { 1986, 8, 15, 10, 20, 5, 600, 300 }; + yield return new object[] { 1986, 2, 28, 10, 20, 5, 600, 300 }; + yield return new object[] { 1986, 12, 31, 10, 20, 5, 600, 300 }; + yield return new object[] { 2000, 2, 28, 10, 20, 5, 600, 300 }; + yield return new object[] { 2000, 2, 29, 10, 20, 5, 600, 300 }; + yield return new object[] { 2000, 12, 31, 10, 20, 5, 600, 300 }; + yield return new object[] { 1900, 2, 28, 10, 20, 5, 600, 300 }; + yield return new object[] { 1900, 12, 31, 10, 20, 5, 600, 300 }; + } + + [Theory] + [MemberData(nameof(Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_TestData))] + public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) + { + var dateTime = new DateTime(year, month, day, hour, minute, second, millisecond, microsecond); + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Unspecified); + } + + [Theory] + [MemberData(nameof(Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_TestData))] + public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Calendar(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) + { + var dateTime = new DateTime(year, month, day, hour, minute, second, millisecond, microsecond, new GregorianCalendar()); + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Unspecified); + } + + [Theory] + [MemberData(nameof(Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_TestData))] + public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_DateTimeKind(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) + { + var dateTime = new DateTime(year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Local); + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Local); + } + + [Theory] + [MemberData(nameof(Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_TestData))] + public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_Calendar_DateTimeKind(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) + { + var dateTime = new DateTime(year, month, day, hour, minute, second, millisecond, microsecond, new GregorianCalendar(), DateTimeKind.Local); + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, DateTimeKind.Local); + } + + public static IEnumerable Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_WithNanoseconds_TestData() + { + yield return new object[] { 1986, 8, 15, 10, 20, 5, 600, 300, 0 }; + yield return new object[] { 1986, 2, 28, 10, 20, 5, 600, 300, 100 }; + yield return new object[] { 1986, 12, 31, 10, 20, 5, 600, 300, 200 }; + yield return new object[] { 2000, 2, 28, 10, 20, 5, 600, 300, 300 }; + yield return new object[] { 2000, 2, 29, 10, 20, 5, 600, 300, 400 }; + yield return new object[] { 2000, 12, 31, 10, 20, 5, 600, 300, 500 }; + yield return new object[] { 1900, 2, 28, 10, 20, 5, 600, 300, 600 }; + yield return new object[] { 1900, 12, 31, 10, 20, 5, 600, 300, 900 }; + } + + [Theory] + [MemberData(nameof(Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_WithNanoseconds_TestData))] + public void Ctor_Int_Int_Int_Int_Int_Int_Int_Int_Int_WithNanoseconds(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, int nanosecond) + { + var dateTime = new DateTime(year, month, day, hour, minute, second, millisecond, microsecond); + dateTime = dateTime.AddTicks(nanosecond / 100); + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, DateTimeKind.Unspecified); + } + [Theory] [InlineData(0)] [InlineData(10000)] @@ -144,6 +209,10 @@ public void Ctor_InvalidYear_ThrowsArgumentOutOfRangeException(int year) AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, new GregorianCalendar())); AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, 1, new GregorianCalendar())); + AssertExtensions.Throws(null, () => new DateTime(year, 1, 1, 1, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -160,6 +229,10 @@ public void Ctor_InvalidMonth_ThrowsArgumentOutOfRangeException(int month) AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, new GregorianCalendar())); AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, 1, new GregorianCalendar())); + AssertExtensions.Throws(null, () => new DateTime(1, month, 1, 1, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -176,6 +249,10 @@ public void Ctor_InvalidDay_ThrowsArgumentOutOfRangeException(int day) AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, new GregorianCalendar())); AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, 1, new GregorianCalendar())); + AssertExtensions.Throws(null, () => new DateTime(1, 1, day, 1, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -190,6 +267,10 @@ public void Ctor_InvalidHour_ThrowsArgumentOutOfRangeException(int hour) AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, new GregorianCalendar())); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, 1, new GregorianCalendar())); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, hour, 1, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -204,6 +285,10 @@ public void Ctor_InvalidMinute_ThrowsArgumentOutOfRangeException(int minute) AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, new GregorianCalendar())); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, 1, new GregorianCalendar())); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, minute, 1, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -217,6 +302,9 @@ public void Ctor_InvalidSecond_ThrowsArgumentOutOfRangeException(int second) AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1)); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1, DateTimeKind.Utc)); AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1, 1)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1, 1, DateTimeKind.Utc)); + AssertExtensions.Throws(null, () => new DateTime(1, 1, 1, 1, 1, second, 1, 1, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -228,6 +316,21 @@ public void Ctor_InvalidMillisecond_ThrowsArgumentOutOfRangeException(int millis AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, DateTimeKind.Utc)); AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, new GregorianCalendar())); AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, new GregorianCalendar(), DateTimeKind.Utc)); + AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, 1)); + AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, 1, DateTimeKind.Utc)); + AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, 1, new GregorianCalendar())); + AssertExtensions.Throws("millisecond", () => new DateTime(1, 1, 1, 1, 1, 1, millisecond, 1, new GregorianCalendar(), DateTimeKind.Utc)); + } + + [Theory] + [InlineData(-1)] + [InlineData(1000)] + public void Ctor_InvalidMicrosecond_ThrowsArgumentOutOfRangeException(int microsecond) + { + AssertExtensions.Throws("microsecond", () => new DateTime(1, 1, 1, 1, 1, 1, 1, microsecond)); + AssertExtensions.Throws("microsecond", () => new DateTime(1, 1, 1, 1, 1, 1, 1, microsecond, DateTimeKind.Utc)); + AssertExtensions.Throws("microsecond", () => new DateTime(1, 1, 1, 1, 1, 1, 1, microsecond, new GregorianCalendar())); + AssertExtensions.Throws("microsecond", () => new DateTime(1, 1, 1, 1, 1, 1, 1, microsecond, new GregorianCalendar(), DateTimeKind.Utc)); } [Theory] @@ -239,6 +342,8 @@ public void Ctor_InvalidDateTimeKind_ThrowsArgumentException(DateTimeKind kind) AssertExtensions.Throws("kind", () => new DateTime(1, 1, 1, 1, 1, 1, kind)); AssertExtensions.Throws("kind", () => new DateTime(1, 1, 1, 1, 1, 1, 1, kind)); AssertExtensions.Throws("kind", () => new DateTime(1, 1, 1, 1, 1, 1, 1, new GregorianCalendar(), kind)); + AssertExtensions.Throws("kind", () => new DateTime(1, 1, 1, 1, 1, 1, 1, 1, kind)); + AssertExtensions.Throws("kind", () => new DateTime(1, 1, 1, 1, 1, 1, 1, 1, new GregorianCalendar(), kind)); } [Fact] @@ -248,6 +353,8 @@ public void Ctor_NullCalendar_ThrowsArgumentNullException() AssertExtensions.Throws("calendar", () => new DateTime(1, 1, 1, 1, 1, 1, null)); AssertExtensions.Throws("calendar", () => new DateTime(1, 1, 1, 1, 1, 1, 1, null)); AssertExtensions.Throws("calendar", () => new DateTime(1, 1, 1, 1, 1, 1, 1, null, DateTimeKind.Local)); + AssertExtensions.Throws("calendar", () => new DateTime(1, 1, 1, 1, 1, 1, 1, 1, null)); + AssertExtensions.Throws("calendar", () => new DateTime(1, 1, 1, 1, 1, 1, 1, 1, null, DateTimeKind.Local)); } [Theory] @@ -573,6 +680,35 @@ public void AddMilliseconds_NewDateOutOfRange_ThrowsArgumentOutOfRangeException( AssertExtensions.Throws("value", () => date.AddMilliseconds(milliseconds)); } + public static IEnumerable AddMicroseconds_TestData() + { + yield return new object[] { new DateTime(1986, 8, 15, 10, 20, 5, 70, 70), 10, new DateTime(1986, 8, 15, 10, 20, 5, 70, 80) }; + yield return new object[] { new DateTime(1986, 8, 15, 10, 20, 5, 70, 70), 0, new DateTime(1986, 8, 15, 10, 20, 5, 70, 70) }; + yield return new object[] { new DateTime(1986, 8, 15, 10, 20, 5, 70, 70), -10, new DateTime(1986, 8, 15, 10, 20, 5, 70, 60) }; + } + + [Theory] + [MemberData(nameof(AddMicroseconds_TestData))] + public void AddMicroseconds_Invoke_ReturnsExpected(DateTime dateTime, double microseconds, DateTime expected) + { + Assert.Equal(expected, dateTime.AddMicroseconds(microseconds)); + } + + public static IEnumerable AddMicroseconds_OutOfRange_TestData() + { + yield return new object[] { DateTime.MaxValue, 1 }; + yield return new object[] { DateTime.MinValue, -1 }; + yield return new object[] { DateTime.Now, double.MaxValue }; + yield return new object[] { DateTime.Now, double.MinValue }; + } + + [Theory] + [MemberData(nameof(AddMicroseconds_OutOfRange_TestData))] + public void AddMicroseconds_NewDateOutOfRange_ThrowsArgumentOutOfRangeException(DateTime date, double microseconds) + { + AssertExtensions.Throws("value", () => date.AddMicroseconds(microseconds)); + } + public static IEnumerable AddTicks_TestData() { yield return new object[] { new DateTime(1000), 10, new DateTime(1010) }; @@ -1721,6 +1857,20 @@ private static void VerifyDateTime(DateTime dateTime, int year, int month, int d Assert.Equal(kind, dateTime.Kind); } + private static void VerifyDateTime(DateTime dateTime, int year, int month, int day, int hour, int minute, + int second, int millisecond, int microsecond, DateTimeKind kind) + { + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, kind); + Assert.Equal(microsecond, dateTime.Microsecond); + } + + private static void VerifyDateTime(DateTime dateTime, int year, int month, int day, int hour, int minute, + int second, int millisecond, int microsecond, int nanosecond, DateTimeKind kind) + { + VerifyDateTime(dateTime, year, month, day, hour, minute, second, millisecond, microsecond, kind); + Assert.Equal(nanosecond, dateTime.Nanosecond); + } + private class MyFormatter : IFormatProvider { public object GetFormat(Type formatType) diff --git a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs index f74a401274748..c3bb010d7d7d6 100644 --- a/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeOnlyTests.cs @@ -33,6 +33,8 @@ public static void ConstructorsTest() Assert.Equal(35, to.Minute); Assert.Equal(0, to.Second); Assert.Equal(0, to.Millisecond); + Assert.Equal(0, to.Microsecond); + Assert.Equal(0, to.Nanosecond); Assert.Equal(new DateTime(1, 1, 1, to.Hour, to.Minute, to.Second, to.Millisecond).Ticks, to.Ticks); to = new TimeOnly(10, 20, 30); @@ -40,6 +42,8 @@ public static void ConstructorsTest() Assert.Equal(20, to.Minute); Assert.Equal(30, to.Second); Assert.Equal(0, to.Millisecond); + Assert.Equal(0, to.Microsecond); + Assert.Equal(0, to.Nanosecond); Assert.Equal(new DateTime(1, 1, 1, to.Hour, to.Minute, to.Second, to.Millisecond).Ticks, to.Ticks); to = new TimeOnly(23, 59, 59, 999); @@ -47,14 +51,27 @@ public static void ConstructorsTest() Assert.Equal(59, to.Minute); Assert.Equal(59, to.Second); Assert.Equal(999, to.Millisecond); + Assert.Equal(0, to.Microsecond); + Assert.Equal(0, to.Nanosecond); Assert.Equal(new DateTime(1, 1, 1, to.Hour, to.Minute, to.Second, to.Millisecond).Ticks, to.Ticks); + to = new TimeOnly(23, 59, 59, 999, 999); + Assert.Equal(23, to.Hour); + Assert.Equal(59, to.Minute); + Assert.Equal(59, to.Second); + Assert.Equal(999, to.Millisecond); + Assert.Equal(999, to.Microsecond); + Assert.Equal(0, to.Nanosecond); + Assert.Equal(new DateTime(1, 1, 1, to.Hour, to.Minute, to.Second, to.Millisecond, to.Microsecond).Ticks, to.Ticks); + DateTime dt = DateTime.Now; to = new TimeOnly(dt.TimeOfDay.Ticks); Assert.Equal(dt.Hour, to.Hour); Assert.Equal(dt.Minute, to.Minute); Assert.Equal(dt.Second, to.Second); Assert.Equal(dt.Millisecond, to.Millisecond); + Assert.Equal(dt.Microsecond, to.Microsecond); + Assert.Equal(dt.Nanosecond, to.Nanosecond); Assert.Throws(() => new TimeOnly(24, 10)); Assert.Throws(() => new TimeOnly(-1, 10)); @@ -64,6 +81,8 @@ public static void ConstructorsTest() Assert.Throws(() => new TimeOnly(10, 10, -3)); AssertExtensions.Throws("millisecond", () => new TimeOnly(10, 10, 10, 1000)); AssertExtensions.Throws("millisecond", () => new TimeOnly(10, 10, 10, -4)); + AssertExtensions.Throws("microsecond", () => new TimeOnly(10, 10, 10, 10, 1000)); + AssertExtensions.Throws("microsecond", () => new TimeOnly(10, 10, 10, 10, -4)); AssertExtensions.Throws("ticks", () => new TimeOnly(TimeOnly.MaxValue.Ticks + 1)); AssertExtensions.Throws("ticks", () => new TimeOnly(-1)); } @@ -72,6 +91,10 @@ public static void ConstructorsTest() public static void AddTest() { TimeOnly to = new TimeOnly(1, 10, 20, 900); + to = to.Add(new TimeSpan(1)); + Assert.Equal(TimeSpan.NanosecondsPerTick, to.Nanosecond); + to = to.Add(new TimeSpan(TimeSpan.TicksPerMicrosecond)); + Assert.Equal(1, to.Microsecond); to = to.Add(new TimeSpan(TimeSpan.TicksPerMillisecond)); Assert.Equal(901, to.Millisecond); to = to.Add(new TimeSpan(TimeSpan.TicksPerSecond)); @@ -102,11 +125,14 @@ public static void AddTest() Assert.Equal(30, to.Minute); Assert.Equal(0, to.Second); Assert.Equal(0, to.Millisecond); + Assert.Equal(0, to.Microsecond); + Assert.Equal(0, to.Nanosecond); to = to.AddHours(1.5, out wrappedDays); Assert.Equal(3, to.Hour); Assert.Equal(0, to.Minute); Assert.Equal(0, to.Second); - Assert.Equal(0, to.Millisecond); + Assert.Equal(0, to.Microsecond); + Assert.Equal(0, to.Nanosecond); Assert.Equal(0, wrappedDays); to = to.AddHours(-28, out wrappedDays); Assert.Equal(23, to.Hour); @@ -216,6 +242,8 @@ public static void FromDateTimeTest() Assert.Equal(dt.Minute, timeOnly.Minute); Assert.Equal(dt.Second, timeOnly.Second); Assert.Equal(dt.Millisecond, timeOnly.Millisecond); + Assert.Equal(dt.Microsecond, timeOnly.Microsecond); + Assert.Equal(dt.Nanosecond, timeOnly.Nanosecond); Assert.Equal(dt.TimeOfDay.Ticks, timeOnly.Ticks); } diff --git a/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs b/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs index 094f9cda87ecc..494eddaadd747 100644 --- a/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs @@ -82,6 +82,45 @@ public static void Ctor_Int_Int_Int_Int_Int_Invalid() AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes, max.Seconds, max.Milliseconds + 1)); } + [Fact] + public static void Ctor_Int_Int_Int_Int_Int_Int() + { + var timeSpan = new TimeSpan(10, 9, 8, 7, 6, 5); + VerifyTimeSpan(timeSpan, 10, 9, 8, 7, 6, 5); + } + + [Fact] + public static void Ctor_Int_Int_Int_Int_Int_Int_Invalid() + { + // TimeSpan > TimeSpan.MinValue + TimeSpan min = TimeSpan.MinValue; + AssertExtensions.Throws(null, () => new TimeSpan(min.Days - 1, min.Hours, min.Minutes, min.Seconds, min.Milliseconds, min.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(min.Days, min.Hours - 1, min.Minutes, min.Seconds, min.Milliseconds, min.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(min.Days, min.Hours, min.Minutes - 1, min.Seconds, min.Milliseconds, min.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(min.Days, min.Hours, min.Minutes, min.Seconds - 1, min.Milliseconds, min.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(min.Days, min.Hours, min.Minutes, min.Seconds, min.Milliseconds, min.Microseconds - 1)); + + // TimeSpan > TimeSpan.MaxValue + TimeSpan max = TimeSpan.MaxValue; + AssertExtensions.Throws(null, () => new TimeSpan(max.Days + 1, max.Hours, max.Minutes, max.Seconds, max.Milliseconds, max.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours + 1, max.Minutes, max.Seconds, max.Milliseconds, max.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes + 1, max.Seconds, max.Milliseconds, max.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes, max.Seconds + 1, max.Milliseconds, max.Microseconds)); + AssertExtensions.Throws(null, () => new TimeSpan(max.Days, max.Hours, max.Minutes, max.Seconds, max.Milliseconds, max.Microseconds + 1)); + } + + [Theory] + [InlineData(100)] + [InlineData(300)] + [InlineData(900)] + public static void Ctor_Int_Int_Int_Int_Int_Int_WithNanosecond(int nanoseconds) + { + var timeSpan = new TimeSpan(10, 9, 8, 7, 6, 5); + timeSpan = new TimeSpan(timeSpan.Ticks + nanoseconds / 100); + + VerifyTimeSpan(timeSpan, 10, 9, 8, 7, 6, 5, nanoseconds); + } + public static IEnumerable Total_Days_Hours_Minutes_Seconds_Milliseconds_TestData() { yield return new object[] { new TimeSpan(0, 0, 0, 0, 0), 0.0, 0.0, 0.0, 0.0, 0.0 }; @@ -1233,6 +1272,20 @@ private static void VerifyTimeSpan(TimeSpan timeSpan, int days, int hours, int m Assert.Equal(timeSpan, +timeSpan); } + private static void VerifyTimeSpan(TimeSpan timeSpan, int days, int hours, int minutes, int seconds, + int milliseconds, int microseconds) + { + VerifyTimeSpan(timeSpan, days, hours, minutes, seconds, milliseconds); + Assert.Equal(microseconds, timeSpan.Microseconds); + } + + private static void VerifyTimeSpan(TimeSpan timeSpan, int days, int hours, int minutes, int seconds, + int milliseconds, int microseconds, int nanoseconds) + { + VerifyTimeSpan(timeSpan, days, hours, minutes, seconds, milliseconds, microseconds); + Assert.Equal(nanoseconds, timeSpan.Nanoseconds); + } + [Theory] [MemberData(nameof(CompareTo_TestData))] public static void CompareTo_Object(TimeSpan timeSpan1, object obj, int expected) @@ -1406,5 +1459,25 @@ public static void ConvertToTimeSpanPrecisionTest() Assert.Equal(1.23456 * 60 * 10_000_000, TimeSpan.FromMinutes(1.23456).Ticks); } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 10)] + [InlineData(1_000, 100)] + public static void TestTotalMicroseconds(long ticks, double totalMicroseconds) + { + var timeSpan = new TimeSpan(ticks); + Assert.Equal(totalMicroseconds, timeSpan.TotalMicroseconds); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 10_000)] + [InlineData(1_000, 100_000)] + public static void TestTotalNanoseconds(long ticks, double totalNanoseconds) + { + var timeSpan = new TimeSpan(ticks); + Assert.Equal(totalNanoseconds, timeSpan.TotalNanoseconds); + } } }