diff --git a/source/nanoFramework.CoreLibrary/System/DateTime.cs b/source/nanoFramework.CoreLibrary/System/DateTime.cs index a7b80dd6..20586552 100644 --- a/source/nanoFramework.CoreLibrary/System/DateTime.cs +++ b/source/nanoFramework.CoreLibrary/System/DateTime.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2017 The nanoFramework project contributors // Portions Copyright (c) Microsoft Corporation. All rights reserved. // See LICENSE file in the project root for full license information. @@ -6,6 +6,7 @@ namespace System { using Runtime.CompilerServices; using Globalization; + using System.Diagnostics; // Summary: // Specifies whether a System.DateTime object represents a local time, a Coordinated @@ -76,11 +77,13 @@ public enum DateTimeKind /// Represents an instant in time, typically expressed as a date and time of day. /// [Serializable] + [DebuggerDisplay("{DateTimeDisplay,nq}")] public struct DateTime { /// Our origin is at 1601/01/01:00:00:00.000 /// While desktop CLR's origin is at 0001/01/01:00:00:00.000. /// There are 504911232000000000 ticks between them which we are subtracting. + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private const long _ticksAtOrigin = 504911232000000000; // Number of 100ns ticks per time unit @@ -97,12 +100,20 @@ public struct DateTime private const int MillisPerDay = MillisPerHour * 24; // Unix Epoch constants - private const long UnixEpochTicks = (TicksPerDay * 719162) - _ticksAtOrigin; // 621355968000000000 + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private const long UnixEpochTicks = (TicksPerDay * 719162); // 621355968000000000 + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private const long UnixEpochSeconds = UnixEpochTicks / TicksPerSecond; // 62135596800 - private const long MinTicks = 0; - // ticks value corresponding to 3000/12/31:23:59:59.999 - private const long MaxTicks = 946708127999999999 - _ticksAtOrigin; + // ticks value corresponding to 1601/01/01:00:00:00.000 (nanoFramework origin date time) + private const long MinTicks = _ticksAtOrigin; + // ticks value corresponding to 3000/12/31:23:59:59.999 (nanoFramework maximum date time) + private const long MaxTicks = 946708127999999999; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private const ulong _tickMask = 0x7FFFFFFFFFFFFFFFL; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private const ulong _UTCMask = 0x8000000000000000L; /// /// Represents the smallest possible value of . This field is read-only. @@ -110,7 +121,7 @@ public struct DateTime /// The value of this constant is equivalent to 00:00:00.0000000, January 1, 1601. /// This value is specific to nanoFramework. .NET equivalent is 00:00:00.0000000 UTC, January 1, 0001, in the Gregorian calendar. /// - public static readonly DateTime MinValue = new DateTime(MinTicks + _ticksAtOrigin); + public static readonly DateTime MinValue = new DateTime(MinTicks); /// /// Represents the largest possible value of . This field is read-only. @@ -118,7 +129,7 @@ public struct DateTime /// The value of this constant is equivalent to 23:59:59.9999999, December 31, 3000. /// This value is specific to nanoFramework. .NET equivalent is 23:59:59.9999999 UTC, December 31, 9999 in the Gregorian calendar. /// - public static readonly DateTime MaxValue = new DateTime(MaxTicks + _ticksAtOrigin); + public static readonly DateTime MaxValue = new DateTime(MaxTicks); /// /// Represents the Unix Epoch value. This field is read-only. @@ -126,13 +137,29 @@ public struct DateTime /// The value of this constant is equivalent to the corresponding to 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). /// This value is specific to nanoFramework. /// - public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks + _ticksAtOrigin, DateTimeKind.Utc); + public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks, DateTimeKind.Utc); // The data is stored as an unsigned 64-bit integer // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1601/01/01:00:00:00.000, up until the value // 3000/12/31:23:59:59.999 // Bits 63-64: Ignored in .NET nanoFramework implementation. - private UInt64 _ticks; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private ulong _ticks; + + // this enum is used to make the call to get date part + // keep in sync with native + private enum DateTimePart + { + Year, + Month, + Day, + DayOfWeek, + DayOfYear, + Hour, + Minute, + Second, + Millisecond + } /// /// Initializes a new instance of the structure to a specified number of ticks. @@ -150,7 +177,10 @@ public DateTime(long ticks) } #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one - _ticks = (UInt64)ticks; + _ticks = (ulong)ticks; + + // allways UTC + _ticks |= _UTCMask; } /// @@ -170,7 +200,7 @@ public DateTime(long ticks, DateTimeKind kind) if (kind != DateTimeKind.Utc) { #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one - throw new ArgumentOutOfRangeException(); + throw new ArgumentException(); #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one } } @@ -178,7 +208,7 @@ public DateTime(long ticks, DateTimeKind kind) /// /// Initializes a new instance of the structure to the specified year, month, and day. /// - /// The year (1 through 9999). + /// The year (1601 through 3000). /// The month (1 through 12). /// The day (1 through the number of days in month). public DateTime(int year, int month, int day) @@ -189,7 +219,7 @@ public DateTime(int year, int month, int day) /// /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, and second. /// - /// The year (1 through 9999). + /// The year (1601 through 3000). /// The month (1 through 12). /// The day (1 through the number of days in month). /// The hours (0 through 23). @@ -203,13 +233,14 @@ public DateTime(int year, int month, int day, int hour, int minute, int second) /// /// Initializes a new instance of the structure to the specified year, month, day, hour, minute, second, and millisecond. /// - /// The year (1 through 9999). + /// The year (1601 through 3000). /// The month (1 through 12). /// The day (1 through the number of days in month). /// The hours (0 through 23). /// The minutes (0 through 59). /// The seconds (0 through 59). /// The milliseconds (0 through 999). + /// Any parameter out of the accepted ranges [MethodImpl(MethodImplOptions.InternalCall)] public extern DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond); @@ -220,12 +251,12 @@ public DateTime(int year, int month, int day, int hour, int minute, int second) /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by . public DateTime Add(TimeSpan val) { - return new DateTime((long)_ticks + val.Ticks + _ticksAtOrigin); + return new DateTime(Ticks + val.Ticks); } private DateTime Add(double val, int scale) { - return new DateTime((long)_ticks + (long)(val * scale * TicksPerMillisecond + (val >= 0 ? 0.5 : -0.5)) + _ticksAtOrigin); + return new DateTime(Ticks + (long)(val * scale * TicksPerMillisecond + (val >= 0 ? 0.5 : -0.5))); } /// @@ -285,7 +316,7 @@ public DateTime AddSeconds(double val) /// An object whose value is the sum of the date and time represented by this instance and the time represented by . public DateTime AddTicks(long val) { - return new DateTime((long)_ticks + val + _ticksAtOrigin); + return new DateTime(Ticks + val); } /// @@ -297,8 +328,8 @@ public DateTime AddTicks(long val) public static int Compare(DateTime t1, DateTime t2) { // Get ticks - var t1Ticks = t1._ticks; - var t2Ticks = t2._ticks; + var t1Ticks = t1.Ticks; + var t2Ticks = t2.Ticks; // Compare ticks. if (t1Ticks > t2Ticks) return 1; @@ -373,7 +404,7 @@ public DateTime Date { get { - return new DateTime((long)(_ticks - (_ticks % TicksPerDay)) + _ticksAtOrigin); + return new DateTime(Ticks - (Ticks % TicksPerDay)); } } @@ -383,10 +414,9 @@ public DateTime Date /// /// The day component, expressed as a value between 1 and 31. /// - public extern int Day + public int Day { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Day); } /// @@ -395,10 +425,9 @@ public extern int Day /// /// An enumerated constant that indicates the day of the week of this value. /// - public extern DayOfWeek DayOfWeek + public DayOfWeek DayOfWeek { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => (DayOfWeek)GetDateTimePart(DateTimePart.DayOfWeek); } /// @@ -407,10 +436,9 @@ public extern DayOfWeek DayOfWeek /// /// The day of the year, expressed as a value between 1 and 366. /// - public extern int DayOfYear + public int DayOfYear { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.DayOfYear); } /// @@ -419,10 +447,9 @@ public extern int DayOfYear /// /// The hour component, expressed as a value between 0 and 23. /// - public extern int Hour + public int Hour { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Hour); } /// @@ -434,15 +461,7 @@ public extern int Hour /// /// Despite the default in the full .NET Framework is this won't never happen because nanoFramework only supports UTC time. /// - public DateTimeKind Kind - { - get - { - // always UTC - return DateTimeKind.Utc; - } - - } + public DateTimeKind Kind => DateTimeKind.Utc; // always UTC in nanoFramework /// /// Gets the milliseconds component of the date represented by this instance. @@ -450,10 +469,9 @@ public DateTimeKind Kind /// /// The milliseconds component, expressed as a value between 0 and 999. /// - public extern int Millisecond + public int Millisecond { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Millisecond); } /// @@ -462,10 +480,9 @@ public extern int Millisecond /// /// The minute component, expressed as a value between 0 and 59. /// - public extern int Minute + public int Minute { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Minute); } /// @@ -474,10 +491,9 @@ public extern int Minute /// /// The month component, expressed as a value between 1 and 12. /// - public extern int Month + public int Month { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Month); } /// @@ -486,10 +502,10 @@ public extern int Month /// /// An object whose value is the current UTC date and time. /// - public static extern DateTime UtcNow + public static DateTime UtcNow { [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => new DateTime(); } /// @@ -498,10 +514,9 @@ public static extern DateTime UtcNow /// /// The seconds component, expressed as a value between 0 and 59. /// - public extern int Second + public int Second { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Second); } /// @@ -514,7 +529,7 @@ public long Ticks { get { - return (long)_ticks; + return (long)(_ticks & _tickMask) + _ticksAtOrigin; } } @@ -528,7 +543,7 @@ public TimeSpan TimeOfDay { get { - return new TimeSpan((long)(_ticks % TicksPerDay)); + return new TimeSpan(Ticks % TicksPerDay); } } @@ -538,10 +553,10 @@ public TimeSpan TimeOfDay /// /// An object that is set to today's date, with the time component set to 00:00:00. /// - public static extern DateTime Today + public static DateTime Today { [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => new DateTime(); } /// @@ -550,10 +565,9 @@ public static extern DateTime Today /// /// The year, between 1 and 9999. /// - public extern int Year + public int Year { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + get => GetDateTimePart(DateTimePart.Year); } /// @@ -563,7 +577,7 @@ public extern int Year /// A time interval that is equal to the date and time represented by this instance minus the date and time represented by . public TimeSpan Subtract(DateTime val) { - return new TimeSpan((long)_ticks - (long)val._ticks); + return new TimeSpan(Ticks - val.Ticks); } /// @@ -573,27 +587,21 @@ public TimeSpan Subtract(DateTime val) /// An object that is equal to the date and time represented by this instance minus the time interval represented by . public DateTime Subtract(TimeSpan val) { - return new DateTime((long)(_ticks - (ulong)val._ticks) + _ticksAtOrigin); + return new DateTime(Ticks - val._ticks); } /// /// Converts the value of the current object to its equivalent string representation. /// /// A string representation of the value of the current object. - public override String ToString() - { - return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); - } + public override string ToString() => DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); /// /// Converts the value of the current object to its equivalent string representation using the specified format. /// /// A standard or custom date and time format string (see Remarks). /// A string representation of value of the current DateTime object as specified by format. - public String ToString(String format) - { - return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); - } + public string ToString(string format) => DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); /// /// Adds a specified time interval to a specified date and time, yielding a new date and time. @@ -605,7 +613,7 @@ public String ToString(String format) /// public static DateTime operator +(DateTime d, TimeSpan t) { - return new DateTime((long)(d._ticks + (ulong)t._ticks) + _ticksAtOrigin); + return new DateTime(d.Ticks + t.Ticks); } @@ -619,7 +627,7 @@ public String ToString(String format) /// public static DateTime operator -(DateTime d, TimeSpan t) { - return new DateTime((long)(d._ticks - (ulong)t._ticks) + _ticksAtOrigin); + return new DateTime(d.Ticks - t.Ticks); } /// @@ -723,10 +731,9 @@ public String ToString(String format) /// public static DateTime FromUnixTimeSeconds(long seconds) { - const long MinSeconds = (MinTicks / TicksPerSecond) - UnixEpochSeconds; const long MaxSeconds = (MaxTicks / TicksPerSecond) - UnixEpochSeconds; - if (seconds < MinSeconds || seconds > MaxSeconds) + if (seconds < 0 || seconds > MaxSeconds) { #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one throw new ArgumentOutOfRangeException(); @@ -734,7 +741,7 @@ public static DateTime FromUnixTimeSeconds(long seconds) } long ticks = (seconds * TicksPerSecond) + UnixEpochTicks; - return new DateTime(ticks + _ticksAtOrigin); + return new DateTime(ticks); } /// @@ -764,7 +771,7 @@ public long ToUnixTimeSeconds() // // In other words, we want to consistently round toward the time 1/1/0001 00:00:00, // rather than toward the Unix Epoch (1/1/1970 00:00:00). - long seconds = (long)(_ticks / TicksPerSecond); + long seconds = Ticks / TicksPerSecond; return seconds - UnixEpochSeconds; } @@ -776,6 +783,13 @@ public override int GetHashCode() { ulong internalTicks = _ticks; return ((int)internalTicks) ^ ((int)(internalTicks >> 0x20)); + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DateTimeDisplay => $"{{{new DateTime(Ticks).ToString()}}}"; + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern int GetDateTimePart(DateTimePart part); } }