From 9e71e2ae8d520eb484a0e74dc8a9a74ef43b561d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Wed, 3 Jan 2018 16:24:37 +0000 Subject: [PATCH] Revert "Extract System.DateTime class and related (#7)" This reverts commit b399f3b32b19f62f64be5328a3ca7389d39d08f2. --- GitVersion.yml | 2 +- source/CoreLibrary.nfproj | 4 + source/System/DateTime.cs | 656 ++++++++++++++++++ source/System/DayOfWeek.cs | 44 ++ source/System/Globalization/CultureInfo.cs | 15 + source/System/Globalization/DateTimeFormat.cs | 481 +++++++++++++ .../Globalization/DateTimeFormatInfo.cs | 277 ++++++++ 7 files changed, 1478 insertions(+), 1 deletion(-) create mode 100644 source/System/DateTime.cs create mode 100644 source/System/DayOfWeek.cs create mode 100644 source/System/Globalization/DateTimeFormat.cs create mode 100644 source/System/Globalization/DateTimeFormatInfo.cs diff --git a/GitVersion.yml b/GitVersion.yml index 84882722..00dcb15a 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -9,7 +9,7 @@ patch-version-bump-message: '\+semver:\s?(fix|patch)' commit-message-incrementing: MergeMessageOnly branches: master: - mode: ContinuousDelivery + mode: ContinuousDelivery tag: increment: Patch prevent-increment-of-merged-branch-version: true diff --git a/source/CoreLibrary.nfproj b/source/CoreLibrary.nfproj index c07ef973..f304ae6c 100644 --- a/source/CoreLibrary.nfproj +++ b/source/CoreLibrary.nfproj @@ -83,6 +83,8 @@ + + @@ -96,6 +98,8 @@ + + diff --git a/source/System/DateTime.cs b/source/System/DateTime.cs new file mode 100644 index 00000000..c97640ee --- /dev/null +++ b/source/System/DateTime.cs @@ -0,0 +1,656 @@ +// +// 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. +namespace System +{ + using Runtime.CompilerServices; + using Globalization; + + // Summary: + // Specifies whether a System.DateTime object represents a local time, a Coordinated + // Universal Time (UTC), or is not specified as either local time or UTC. + /// + /// Specifies whether a DateTime object represents a local time, a Coordinated Universal Time (UTC), or is not specified as either local time or UTC. + /// + /// nanoFramework doesn't suport local time, only UTC, so it's not possible to specify DateTimeKind.Local. + [Serializable] + public enum DateTimeKind + { + /// + /// The time represented is UTC. + /// + Utc = 1, + /// + /// The time represented is local time. + /// + [Obsolete("nanoFrameowrk doesn't support local time, so DateTimeKind.Local can't be used for consistency", true)] + Local = 2, + } + + // This value type represents a date and time. Every DateTime + // object has a private field (Ticks) of type Int64 that stores the + // date and time as the number of 100 nanosecond intervals since + // 12:00 AM January 1, year 1601 A.D. in the proleptic Gregorian Calendar. + // + //

For a description of various calendar issues, look at + // + // Calendar Studies web site, at + // http://serendipity.nofadz.com/hermetic/cal_stud.htm. + // + //

+ //

Warning about 2 digit years

+ //

As a temporary hack until we get new DateTime <-> String code, + // some systems won't be able to round trip dates less than 1930. This + // is because we're using OleAut's string parsing routines, which look + // at your computer's default short date string format, which uses 2 digit + // years on most computers. To fix this, go to Control Panel -> Regional + // Settings -> Date and change the short date style to something like + // "M/d/yyyy" (specifying four digits for the year). + // + ///

+ /// Represents an instant in time, typically expressed as a date and time of day. + /// + [Serializable] + public struct DateTime + { + // Number of 100ns ticks per time unit + private const long TicksPerMillisecond = 10000; + private const long TicksPerSecond = TicksPerMillisecond * 1000; + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * 24; + + // Number of milliseconds per time unit + private const int MillisPerSecond = 1000; + private const int MillisPerMinute = MillisPerSecond * 60; + private const int MillisPerHour = MillisPerMinute * 60; + private const int MillisPerDay = MillisPerHour * 24; + + private const long MinTicks = 0; + private const long MaxTicks = 441796895990000000; + + // This is mask to extract ticks from m_ticks + private const ulong TickMask = 0x7FFFFFFFFFFFFFFFL; + private const ulong UtcMask = 0x8000000000000000L; + + /// + /// Represents the smallest possible value of DateTime. This field is read-only. + /// + /// The value of this constant is equivalent to 00:00:00.0000000, January 1, 1601. + public static readonly DateTime MinValue = new DateTime(MinTicks); + /// + /// Represents the largest possible value of DateTime. This field is read-only. + /// + /// The value of this constant is equivalent to 23:59:59.9999999, December 31, 9999, exactly one tick (100 nanoseconds) before 00:00:00, January 1, 10000. + public static readonly DateTime MaxValue = new DateTime(MaxTicks); + + private ulong _ticks; + + /// + /// Initializes a new instance of the DateTime structure to a specified number of ticks. + /// + /// A date and time expressed in the number of 100-nanosecond intervals. + /// ticks - Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. + public DateTime(long ticks) + { + if ((ticks & (long)TickMask) < MinTicks || (ticks & (long)TickMask) > MaxTicks) throw new ArgumentOutOfRangeException(); + + _ticks = (ulong)ticks; + } + + /// + /// Initializes a new instance of the DateTime structure to a specified number of ticks and to Coordinated Universal Time (UTC). + /// + /// A date and time expressed in the number of 100-nanosecond intervals. + /// One of the enumeration values that indicates whether ticks specifies a local time, Coordinated Universal Time (UTC), or neither. + /// nanoFramework doesn't suport local time, only UTC, so it's not possible to specify DateTimeKind.Local. + public DateTime(long ticks, DateTimeKind kind) + : this(ticks) + { + _ticks |= UtcMask; + } + + /// + /// Initializes a new instance of the DateTime structure to the specified year, month, and day. + /// + /// The year (1 through 9999). + /// The month (1 through 12). + /// The day (1 through the number of days in month). + public DateTime(int year, int month, int day) + : this(year, month, day, 0, 0, 0) + { + } + + /// + /// Initializes a new instance of the DateTime structure to the specified year, month, day, hour, minute, and second. + /// + /// The year (1 through 9999). + /// 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). + public DateTime(int year, int month, int day, int hour, int minute, int second) + : this(year, month, day, hour, minute, second, 0) + { + } + + /// + /// Initializes a new instance of the DateTime structure to the specified year, month, day, hour, minute, second, and millisecond. + /// + /// The year (1 through 9999). + /// 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). + [MethodImpl(MethodImplOptions.InternalCall)] + public extern DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond); + + /// + /// Returns a new DateTime that adds the value of the specified TimeSpan to the value of this instance. + /// + /// A positive or negative time interval. + /// An object whose value is the sum of the date and time represented by this instance and the time interval represented by val. + public DateTime Add(TimeSpan val) + { + return new DateTime((long)_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))); + } + + /// + /// Returns a new DateTime that adds the specified number of days to the value of this instance. + /// + /// A number of whole and fractional days. The val parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of days represented by val. + public DateTime AddDays(double val) + { + return Add(val, MillisPerDay); + } + + /// + /// Returns a new DateTime that adds the specified number of hours to the value of this instance. + /// + /// A number of whole and fractional hours. The val parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of hours represented by val. + public DateTime AddHours(double val) + { + return Add(val, MillisPerHour); + } + + /// + /// Returns a new DateTime that adds the specified number of milliseconds to the value of this instance. + /// + /// A number of whole and fractional milliseconds. The val 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 milliseconds represented by val. + public DateTime AddMilliseconds(double val) + { + return Add(val, 1); + } + + /// + /// Returns a new DateTime that adds the specified number of minutes to the value of this instance. + /// + /// A number of whole and fractional minutes. The val parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of minutes represented by val. + public DateTime AddMinutes(double val) + { + return Add(val, MillisPerMinute); + } + + /// + /// Returns a new DateTime that adds the specified number of seconds to the value of this instance. + /// + /// A number of whole and fractional seconds. The val parameter can be negative or positive. + /// An object whose value is the sum of the date and time represented by this instance and the number of seconds represented by val. + public DateTime AddSeconds(double val) + { + return Add(val, MillisPerSecond); + } + + /// + /// Returns a new DateTime that adds the specified number of ticks to the value of this instance. + /// + /// A number of 100-nanosecond ticks. The val parameter can be positive or negative. + /// An object whose value is the sum of the date and time represented by this instance and the time represented by val. + public DateTime AddTicks(long val) + { + return new DateTime((long)_ticks + val); + } + + /// + /// Compares two instances of DateTime and returns an integer that indicates whether the first instance is earlier than, the same as, or later than the second instance. + /// + /// The first object to compare. + /// The second object to compare. + /// A signed number indicating the relative values of t1 and t2. + public static int Compare(DateTime t1, DateTime t2) + { + // Get ticks, clear UTC mask + var t1Ticks = t1._ticks & TickMask; + var t2Ticks = t2._ticks & TickMask; + + // Compare ticks, ignore the Kind property. + if (t1Ticks > t2Ticks) return 1; + if (t1Ticks < t2Ticks) return -1; + + // Values are equal + return 0; + } + + /// + /// Compares the value of this instance to a specified object that contains a specified DateTime value, and returns an integer that indicates whether this instance is earlier than, the same as, or later than the specified DateTime value. + /// + /// A boxed object to compare, or nullNothingnullptrunita null reference (Nothing in Visual Basic). + /// A signed number indicating the relative values of this instance and value. + public int CompareTo(Object val) + { + return val == null ? 1 : Compare(this, (DateTime)val); + } + + /// + /// Returns the number of days in the specified month and year. + /// + /// The year. + /// The month (a number ranging from 1 to 12). + /// The number of days in month for the specified year. + /// For example, if month equals 2 for February, the return value is 28 or 29 depending upon whether year is a leap year. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int DaysInMonth(int year, int month); + + /// + /// Returns a value indicating whether this instance is equal to a specified object. + /// + /// The object to compare to this instance. + /// true if val is an instance of DateTime and equals the value of this instance; otherwise, false. + public override bool Equals(Object val) + { + if (val is DateTime) + { + // Call compare for proper comparison of 2 DateTime objects + // Since DateTime is optimized value and internally represented by int64 + // "this" may still have type int64. + // Convertion to object and back is a workaround. + object o = this; + var thisTime = (DateTime)o; + + return Compare(thisTime, (DateTime)val) == 0; + } + + return false; + } + + /// + /// Returns a value indicating whether two DateTime instances have the same date and time value. + /// + /// The first object to compare. + /// The second object to compare. + /// true if the two values are equal; otherwise, false. + public static bool Equals(DateTime t1, DateTime t2) + { + return Compare(t1, t2) == 0; + } + + /// + /// Gets the date component of this instance. + /// + /// + /// A new object with the same date as this instance, and the time value set to 12:00:00 midnight (00:00:00). + /// + public DateTime Date + { + get + { + // Need to remove UTC mask before arithmetic operations. Then set it back. + return (_ticks & UtcMask) != 0 ? new DateTime((long)(((_ticks & TickMask) - (_ticks & TickMask) % TicksPerDay) | UtcMask)) : new DateTime((long)(_ticks - _ticks % TicksPerDay)); + } + } + + /// + /// Gets the day of the month represented by this instance. + /// + /// + /// The day component, expressed as a value between 1 and 31. + /// + public extern int Day + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the day of the week represented by this instance. + /// + /// + /// An enumerated constant that indicates the day of the week of this DateTime value. + /// + public extern DayOfWeek DayOfWeek + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the day of the year represented by this instance. + /// + /// + /// The day of the year, expressed as a value between 1 and 366. + /// + public extern int DayOfYear + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the hour component of the date represented by this instance. + /// + /// + /// The hour component, expressed as a value between 0 and 23. + /// + public extern int Hour + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets a value that indicates whether the time represented by this instance is based on local time, Coordinated Universal Time (UTC), or neither. + /// + /// + /// One of the enumeration values that indicates what the current time represents. Despite the default in the full .NET Framework is DateTimeKind.Local this won't never happen becuase nanoFramework only suports UTC time. + /// + public DateTimeKind Kind + { + get + { + // If mask for UTC time is set - return UTC. If no maskk - return local. + return DateTimeKind.Utc; + } + + } + + /// + /// Gets the milliseconds component of the date represented by this instance. + /// + /// + /// The milliseconds component, expressed as a value between 0 and 999. + /// + public extern int Millisecond + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the minute component of the date represented by this instance. + /// + /// + /// The minute component, expressed as a value between 0 and 59. + /// + public extern int Minute + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the month component of the date represented by this instance. + /// + /// + /// The month component, expressed as a value between 1 and 12. + /// + public extern int Month + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets a DateTime object that is set to the current date and time on this computer, expressed as the Coordinated Universal Time (UTC). + /// + /// + /// An object whose value is the current UTC date and time. + /// + public static extern DateTime UtcNow + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the seconds component of the date represented by this instance. + /// + /// + /// The seconds component, expressed as a value between 0 and 59. + /// + public extern int Second + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// 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. + /// See DeviceCode\PAL\time_decl.h for explanation of why we are taking + /// year 1601 as origin for our HAL, PAL, and CLR. + // static Int64 ticksAtOrigin = 504911232000000000; + private const Int64 TicksAtOrigin = 0; + + /// + /// Gets the number of ticks that represent the date and time of this instance. + /// + /// + /// The number of ticks that represent the date and time of this instance. The value is between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks + /// + public long Ticks + { + get + { + return (long)(_ticks & TickMask) + TicksAtOrigin; + } + } + + /// + /// Gets the time of day for this instance. + /// + /// + /// A time interval that represents the fraction of the day that has elapsed since midnight. + /// + public TimeSpan TimeOfDay + { + get + { + return new TimeSpan((long)((_ticks & TickMask) % TicksPerDay)); + } + } + + /// + /// Gets the current date. + /// + /// + /// An object that is set to today's date, with the time component set to 00:00:00. + /// + public static extern DateTime Today + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Gets the year component of the date represented by this instance. + /// + /// + /// The year, between 1 and 9999. + /// + public extern int Year + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Subtracts the specified date and time from this instance. + /// + /// The date and time value to subtract. + /// A time interval that is equal to the date and time represented by this instance minus the date and time represented by val. + public TimeSpan Subtract(DateTime val) + { + return new TimeSpan((long)(_ticks & TickMask) - (long)(val._ticks & TickMask)); + } + + /// + /// Subtracts the specified duration from this instance. + /// + /// The time interval to subtract. + /// An object that is equal to the date and time represented by this instance minus the time interval represented by val. + public DateTime Subtract(TimeSpan val) + { + return new DateTime((long)(_ticks - (ulong)val._numberOfTicks)); + } + + /// + /// Converts the value of the current DateTime object to its equivalent string representation. + /// + /// A string representation of the value of the current DateTime object. + public override String ToString() + { + return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); + } + + /// + /// Converts the value of the current DateTime 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); + } + + /// + /// Adds a specified time interval to a specified date and time, yielding a new date and time. + /// + /// The date and time value to add. + /// The time interval to add. + /// + /// An object that is the sum of the values of d and t. + /// + public static DateTime operator +(DateTime d, TimeSpan t) + { + return new DateTime((long)(d._ticks + (ulong)t._numberOfTicks)); + } + + + /// + /// Subtracts a specified time interval from a specified date and time and returns a new date and time. + /// + /// The date and time value to subtract from. + /// The time interval to subtract. + /// + /// An object whose value is the value of d minus the value of t. + /// + public static DateTime operator -(DateTime d, TimeSpan t) + { + return new DateTime((long)(d._ticks - (ulong)t._numberOfTicks)); + } + + /// + /// Subtracts a specified date and time from another specified date and time and returns a time interval. + /// + /// The date and time value to subtract from (the minuend). + /// The date and time value to subtract (the subtrahend). + /// + /// The time interval between d1 and d2; that is, d1 minus d2. + /// + public static TimeSpan operator -(DateTime d1, DateTime d2) + { + return d1.Subtract(d2); + } + + /// + /// Determines whether two specified instances of DateTime are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if d1 and d2 represent the same date and time; otherwise, false. + /// + public static bool operator ==(DateTime d1, DateTime d2) + { + return Compare(d1, d2) == 0; + } + + /// + /// Determines whether two specified instances of DateTime are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if t1 and t2 do not represent the same date and time; otherwise, false. + /// + public static bool operator !=(DateTime t1, DateTime t2) + { + return Compare(t1, t2) != 0; + } + + /// + /// Determines whether one specified DateTime is less than another specified DateTime. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if t1 is less than t2; otherwise, false. + /// + public static bool operator <(DateTime t1, DateTime t2) + { + return Compare(t1, t2) < 0; + } + + /// + /// Determines whether one specified DateTime is less than or equal to another specified DateTime. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if t1 is less than or equal to t2; otherwise, false. + /// + public static bool operator <=(DateTime t1, DateTime t2) + { + return Compare(t1, t2) <= 0; + } + + /// + /// Determines whether one specified DateTime is greater than another specified DateTime. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if t1 is greater than t2; otherwise, false. + /// + public static bool operator >(DateTime t1, DateTime t2) + { + return Compare(t1, t2) > 0; + } + + /// + /// Determines whether one specified DateTime is greater than or equal to another specified DateTime. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if t1 is greater than or equal to t2; otherwise, false. + /// + public static bool operator >=(DateTime t1, DateTime t2) + { + return Compare(t1, t2) >= 0; + } + } +} diff --git a/source/System/DayOfWeek.cs b/source/System/DayOfWeek.cs new file mode 100644 index 00000000..2144bbda --- /dev/null +++ b/source/System/DayOfWeek.cs @@ -0,0 +1,44 @@ +// +// 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. +// + +namespace System +{ + /// + /// Specifies the day of the week. + /// + [Serializable] + public enum DayOfWeek + { + /// + /// Indicates Sunday + /// + Sunday = 0, + /// + /// Indicates Monday + /// + Monday = 1, + /// + /// Indicates Tuesday + /// + Tuesday = 2, + /// + /// Indicates Wednesday + /// + Wednesday = 3, + /// + /// Indicates Thursday + /// + Thursday = 4, + /// + /// Indicates Friday + /// + Friday = 5, + /// + /// Indicates Saturday + /// + Saturday = 6, + } +} diff --git a/source/System/Globalization/CultureInfo.cs b/source/System/Globalization/CultureInfo.cs index e7a36ef9..96f66e61 100644 --- a/source/System/Globalization/CultureInfo.cs +++ b/source/System/Globalization/CultureInfo.cs @@ -17,6 +17,7 @@ namespace System.Globalization public class CultureInfo /*: ICloneable , IFormatProvider*/ { internal NumberFormatInfo _numInfo = null; + internal DateTimeFormatInfo _dateTimeInfo = null; internal string _cultureInfoName = ""; internal string _name = null; [NonSerialized] @@ -138,5 +139,19 @@ public virtual NumberFormatInfo NumberFormat return _numInfo; } } + + /// + /// Gets a DateTimeFormatInfo that defines the culturally appropriate format of displaying dates and times. + /// + /// A DateTimeFormatInfo that defines the culturally appropriate format of displaying dates and times. + public virtual DateTimeFormatInfo DateTimeFormat + { + get + { + if (_dateTimeInfo == null) _dateTimeInfo = new DateTimeFormatInfo(this); + + return _dateTimeInfo; + } + } } } diff --git a/source/System/Globalization/DateTimeFormat.cs b/source/System/Globalization/DateTimeFormat.cs new file mode 100644 index 00000000..5f7d08b0 --- /dev/null +++ b/source/System/Globalization/DateTimeFormat.cs @@ -0,0 +1,481 @@ +// +// 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. +// + +namespace System.Globalization +{ + using System; + using Runtime.CompilerServices; + + // Customized format patterns: + // P.S. Format in the table below is the internal number format used to display the pattern. + + // Patterns Format Description Example + // ========= ========== ===================================== ======== + // "h" "0" hour (12-hour clock)w/o leading zero 3 + // "hh" "00" hour (12-hour clock)with leading zero 03 + // "hh*" "00" hour (12-hour clock)with leading zero 03 + + // "H" "0" hour (24-hour clock)w/o leading zero 8 + // "HH" "00" hour (24-hour clock)with leading zero 08 + // "HH*" "00" hour (24-hour clock) 08 + + // "m" "0" minute w/o leading zero + // "mm" "00" minute with leading zero + // "mm*" "00" minute with leading zero + + // "s" "0" second w/o leading zero + // "ss" "00" second with leading zero + // "ss*" "00" second with leading zero + + // "f" "0" second fraction (1 digit) + // "ff" "00" second fraction (2 digit) + // "fff" "000" second fraction (3 digit) + // "ffff" "0000" second fraction (4 digit) + // "fffff" "00000" second fraction (5 digit) + // "ffffff" "000000" second fraction (6 digit) + // "fffffff" "0000000" second fraction (7 digit) + + // "F" "0" second fraction (up to 1 digit) + // "FF" "00" second fraction (up to 2 digit) + // "FFF" "000" second fraction (up to 3 digit) + // "FFFF" "0000" second fraction (up to 4 digit) + // "FFFFF" "00000" second fraction (up to 5 digit) + // "FFFFFF" "000000" second fraction (up to 6 digit) + // "FFFFFFF" "0000000" second fraction (up to 7 digit) + + // "t" first character of AM/PM designator A + // "tt" AM/PM designator AM + // "tt*" AM/PM designator PM + + // "d" "0" day w/o leading zero 1 + // "dd" "00" day with leading zero 01 + // "ddd" short weekday name (abbreviation) Mon + // "dddd" full weekday name Monday + // "dddd*" full weekday name Monday + + // "M" "0" month w/o leading zero 2 + // "MM" "00" month with leading zero 02 + // "MMM" short month name (abbreviation) Feb + // "MMMM" full month name Febuary + // "MMMM*" full month name Febuary + + // "y" "0" two digit year (year % 100) w/o leading zero 0 + // "yy" "00" two digit year (year % 100) with leading zero 00 + // "yyy" "D3" year 2000 + // "yyyy" "D4" year 2000 + // "yyyyy" "D5" year 2000 + // ... + + // "z" "+0;-0" timezone offset w/o leading zero -8 + // "zz" "+00;-00" timezone offset with leading zero -08 + // "zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00 + // "zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00 + + // "K" -Local "zzz", e.g. -08:00 + // -Utc "'Z'", representing UTC + // -Unspecified "" + + // "g*" the current era name A.D. + + // ":" time separator : + // "/" date separator / + // "'" quoted string 'ABC' will insert ABC into the formatted string. + // '"' quoted string "ABC" will insert ABC into the formatted string. + // "%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year. + // "\" escaped character E.g. '\d' insert the character 'd' into the format string. + // other characters insert the character into the format string. + + // Pre-defined format characters: + // (U) to indicate Universal time is used. + // (G) to indicate Gregorian calendar is used. + + // Format Description Real format Example + // ========= ================================= ====================== ======================= + // "d" short date culture-specific 10/31/1999 + // "D" long data culture-specific Sunday, October 31, 1999 + // "f" full date (long date + short time) culture-specific Sunday, October 31, 1999 2:00 AM + // "F" full date (long date + long time) culture-specific Sunday, October 31, 1999 2:00:00 AM + // "g" general date (short date + short time) culture-specific 10/31/1999 2:00 AM + // "G" general date (short date + long time) culture-specific 10/31/1999 2:00:00 AM + // "m"/"M" Month/Day date culture-specific October 31 + //(G) "o"/"O" Round Trip XML "yyyy-MM-ddTHH:mm:ss.fffffffK" 1999-10-31 02:00:00.0000000Z + //(G) "r"/"R" RFC 1123 date, "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" Sun, 31 Oct 1999 10:00:00 GMT + //(G) "s" Sortable format, based on ISO 8601. "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00 + // ('T' for local time) + // "t" short time culture-specific 2:00 AM + // "T" long time culture-specific 2:00:00 AM + //(G) "u" Universal time with sortable format, "yyyy'-'MM'-'dd HH':'mm':'ss'Z'" 1999-10-31 10:00:00Z + // based on ISO 8601. + //(U) "U" Universal time with full culture-specific Sunday, October 31, 1999 10:00:00 AM + // (long date + long time) format + // "y"/"Y" Year/Month day culture-specific October, 1999 + + + + //This class contains only static members and does not require the serializable attribute. + internal static + class DateTimeFormat + { + internal const int _maxSecondsFractionDigits = 3; + //////////////////////////////////////////////////////////////////////////// + // + // Format the positive integer value to a string and perfix with assigned + // length of leading zero. + // + // Parameters: + // value: The value to format + // len: The maximum length for leading zero. + // If the digits of the value is greater than len, no leading zero is added. + // (If len > 2, we'll treat it as 2) + // + // Notes: + // The function can format to Int32.MaxValue. + // + //////////////////////////////////////////////////////////////////////////// + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern String FormatDigits(int value, int len); + + private static int ParseRepeatPattern(String format, int pos, char patternChar) + { + var len = format.Length; + var index = pos + 1; + while (index < len && format[index] == patternChar) + { + index++; + } + + return index - pos; + } + + // + // The pos should point to a quote character. This method will + // get the string encloed by the quote character. + // + internal static String ParseQuoteString(String format, int pos, out int count) + { + // + // NOTE : pos will be the index of the quote character in the 'format' string. + // + var result = String.Empty; + var formatLen = format.Length; + var beginPos = pos; + var quoteChar = format[pos++]; // Get the character used to quote the following string. + + var foundQuote = false; + while (pos < formatLen) + { + var ch = format[pos++]; + if (ch == quoteChar) + { + foundQuote = true; + break; + } + if (ch == '\\') + { + // The following are used to support escaped character. + // Escaped character is also supported in the quoted string. + // Therefore, someone can use a format like "'minute:' mm\"" to display: + // minute: 45" + // because the second double quote is escaped. + if (pos < formatLen) + { + result += format[pos++]; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new ArgumentException("Format_InvalidString"); + //throw new FormatException( Environment.GetResourceString( "Format_InvalidString" ) ); + } + } + else + { + result += ch; + } + } + + if (!foundQuote) + { + // Here we can't find the matching quote. + throw new ArgumentException("Format_BadQuote"); + } + + // + // Return the character count including the begin/end quote characters and enclosed string. + // + count = pos - beginPos; + + return result; + } + + // + // Get the next character at the index of 'pos' in the 'format' string. + // Return value of -1 means 'pos' is already at the end of the 'format' string. + // Otherwise, return value is the int value of the next character. + // + private static int ParseNextChar(String format, int pos) + { + if (pos >= format.Length - 1) return -1; + + return format[pos + 1]; + } + + // + // FormatCustomized + // + // Actions: Format the DateTime instance using the specified format. + // + private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi) + { + var result = String.Empty; + var i = 0; + int tokenLen = 1; + var formatLen = format.Length; + + while (i < formatLen) + { + var ch = format[i]; + int nextChar; + var doneParsingCh = true; + var tempResult = String.Empty; + + switch (ch) + { + case ':': + tempResult = dtfi.TimeSeparator; + tokenLen = 1; + break; + case '/': + tempResult = dtfi.DateSeparator; + tokenLen = 1; + break; + case '\'': + case '\"': + tempResult = ParseQuoteString(format, i, out tokenLen); + break; + case '%': + // Optional format character. + // For example, format string "%d" will print day of month + // without leading zero. Most of the cases, "%" can be ignored. + nextChar = ParseNextChar(format, i); + // nextChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextChar >= 0 && nextChar != '%') + { + tempResult = FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi); + tokenLen = 2; + } + else + { + // + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + // + throw new ArgumentException("Format_InvalidString"); + } + break; + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For exmple, "\d" will insert the character 'd' into the string. + // + // NOTENOTE : we can remove this format character if we enforce the enforced quote + // character rule. + // That is, we ask everyone to use single quote or double quote to insert characters, + // then we can remove this character. + // + nextChar = ParseNextChar(format, i); + if (nextChar >= 0) + { + tempResult = ((char)nextChar).ToString(); + tokenLen = 2; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new ArgumentException("Format_InvalidString"); + } + break; + default: + doneParsingCh = false; + break; + } + + if (!doneParsingCh) + { + tokenLen = ParseRepeatPattern(format, i, ch); + switch (ch) + { + case 'h': + var hour12 = dateTime.Hour % 12; + if (hour12 == 0) hour12 = 12; + tempResult = FormatDigits(hour12, tokenLen); + break; + case 'H': + tempResult = FormatDigits(dateTime.Hour, tokenLen); + break; + case 'm': + tempResult = FormatDigits(dateTime.Minute, tokenLen); + break; + case 's': + tempResult = FormatDigits(dateTime.Second, tokenLen); + break; + case 'f': + if (tokenLen <= _maxSecondsFractionDigits) + { + var precision = 3; + var fraction = dateTime.Millisecond; + + // Note: Need to add special case when tokenLen > precision to begin with + // if we're to change MaxSecondsFractionDigits to be more than 3 + + while (tokenLen < precision) + { + fraction /= 10; + precision--; + } + + tempResult = FormatDigits(fraction, tokenLen); + } + else throw new ArgumentException("Format_InvalidString"); + break; + case 't': + if (tokenLen == 1) + { + if (dateTime.Hour < 12) + { + if (dtfi.AMDesignator.Length >= 1) tempResult = dtfi.AMDesignator[0].ToString(); + } + else + { + if (dtfi.PMDesignator.Length >= 1) tempResult = dtfi.PMDesignator[0].ToString(); + } + + } + else tempResult = dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator; + break; + case 'd': + // + // tokenLen == 1 : Day of month as digits with no leading zero. + // tokenLen == 2 : Day of month as digits with leading zero for single-digit months. + // tokenLen == 3 : Day of week as a three-leter abbreviation. + // tokenLen >= 4 : Day of week as its full name. + // + if (tokenLen <= 2) tempResult = FormatDigits(dateTime.Day, tokenLen); + else + { + var dayOfWeek = (int)dateTime.DayOfWeek; + + tempResult = tokenLen == 3 ? dtfi.AbbreviatedDayNames[dayOfWeek] : dtfi.DayNames[dayOfWeek]; + } + break; + case 'M': + // + // tokenLen == 1 : Month as digits with no leading zero. + // tokenLen == 2 : Month as digits with leading zero for single-digit months. + // tokenLen == 3 : Month as a three-letter abbreviation. + // tokenLen >= 4 : Month as its full name. + // + var month = dateTime.Month; + if (tokenLen <= 2) tempResult = FormatDigits(month, tokenLen); + else tempResult = tokenLen == 3 ? dtfi.AbbreviatedMonthNames[month - 1] : dtfi.MonthNames[month - 1]; + break; + case 'y': + // Notes about OS behavior: + // y: Always print (year % 100). No leading zero. + // yy: Always print (year % 100) with leading zero. + // yyy/yyyy/yyyyy/... : Print year value. With leading zeros. + + var year = dateTime.Year; + + tempResult = tokenLen <= 2 ? FormatDigits(year % 100, tokenLen) : year.ToString(); + + if (tempResult.Length < tokenLen) tempResult = new string('0', tokenLen - tempResult.Length) + tempResult; + break; + + default: + tempResult = tokenLen == 1 ? ch.ToString() : new String(ch, tokenLen); + break; + } + } + + result += tempResult; + i += tokenLen; + } + + return result; + } + + internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) + { + String realFormat; + + switch (format[0]) + { + case 'd': // Short Date + realFormat = dtfi.ShortDatePattern; + break; + case 'D': // Long Date + realFormat = dtfi.LongDatePattern; + break; + case 'f': // Full (long date + short time) + realFormat = dtfi.LongDatePattern + " " + dtfi.ShortTimePattern; + break; + case 'F': // Full (long date + long time) + realFormat = dtfi.FullDateTimePattern; + break; + case 'g': // General (short date + short time) + realFormat = dtfi.GeneralShortTimePattern; + break; + case 'G': // General (short date + long time) + realFormat = dtfi.GeneralLongTimePattern; + break; + case 'm': + case 'M': // Month/Day Date + realFormat = dtfi.MonthDayPattern; + break; + case 'r': + case 'R': // RFC 1123 Standard + realFormat = dtfi.RFC1123Pattern; + break; + case 's': // Sortable without Time Zone Info + realFormat = dtfi.SortableDateTimePattern; + break; + case 't': // Short Time + realFormat = dtfi.ShortTimePattern; + break; + case 'T': // Long Time + realFormat = dtfi.LongTimePattern; + break; + case 'u': // Universal with Sortable format + realFormat = dtfi.UniversalSortableDateTimePattern; + break; + case 'U': // Universal with Full (long date + long time) format + realFormat = dtfi.FullDateTimePattern; + break; + case 'y': + case 'Y': // Year/Month Date + realFormat = dtfi.YearMonthPattern; + break; + default: + throw new ArgumentException("Format_InvalidString"); + } + + return realFormat; + } + + internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi) + { + if (format == null || format.Length == 0) format = "G"; + if (format.Length == 1) format = GetRealFormat(format, dtfi); + + return FormatCustomized(dateTime, format, dtfi); + } + } +} diff --git a/source/System/Globalization/DateTimeFormatInfo.cs b/source/System/Globalization/DateTimeFormatInfo.cs new file mode 100644 index 00000000..3a2d14e2 --- /dev/null +++ b/source/System/Globalization/DateTimeFormatInfo.cs @@ -0,0 +1,277 @@ +// +// 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. +// + +// ReSharper disable InconsistentNaming +#define ENABLE_CROSS_APPDOMAIN +namespace System.Globalization +{ + using System; + + /// + /// Provides culture-specific information about the format of date and time values. + /// + public sealed class DateTimeFormatInfo /*: ICloneable, IFormatProvider*/ + { + internal String _generalShortTimePattern = "MM/dd/yyyy HH:mm"; + internal String _generalLongTimePattern = "MM/dd/yyyy HH:mm:ss"; + internal const String _rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; + internal const String _sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss"; + internal const String _universalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'"; + internal String _fullDateTimePattern = "dddd, dd MMMM yyyy HH:mm:ss"; + private readonly CultureInfo CultureInfo; + + internal DateTimeFormatInfo(CultureInfo cultureInfo) + { + CultureInfo = cultureInfo; + } + + /// + /// Gets a read-only DateTimeFormatInfo object that formats values based on the current culture. + /// + /// A read-only DateTimeFormatInfo object based on the CultureInfo object for the current thread. + public static DateTimeFormatInfo CurrentInfo + { + get + { + return CultureInfo.CurrentUICulture.DateTimeFormat; + } + } + + /// + /// Gets the string designator for hours that are "ante meridiem" (before noon). + /// + /// The string designator for hours that are ante meridiem. The default for InvariantInfo is "AM". + public String AMDesignator + { + get + { + return "AM"; + } + } + + /// + /// Gets the string that separates the components of a date, that is, the year, month, and day. + /// + /// The string that separates the components of a date, that is, the year, month, and day. The default for InvariantInfo is "/". + public String DateSeparator + { + get + { + return "/"; + } + } + + /// + /// Gets the custom format string for a long date and long time value. + /// + /// The custom format string for a long date and long time value. + public String FullDateTimePattern + { + get + { + return _fullDateTimePattern; + } + } + + /// + /// Gets the custom format string for a long date value. + /// + /// The custom format string for a long date value. + public String LongDatePattern + { + get + { + return "dddd, dd MMMM yyyy"; + } + } + + /// + /// Gets the custom format string for a long time value. + /// + /// The format pattern for a long time value. + public String LongTimePattern + { + get + { + return "HH:mm:ss"; + } + } + + /// + /// Gets the custom format string for a month and day value. + /// + /// The custom format string for a month and day value. + public String MonthDayPattern + { + get + { + return "MMMM dd"; + } + } + + /// + /// Gets the string designator for hours that are "post meridiem" (after noon). + /// + /// The string designator for hours that are "post meridiem" (after noon). The default for InvariantInfo is "PM". + public String PMDesignator + { + get + { + return "PM"; + } + } + + /// + /// Gets the custom format string for a time value that is based on the Internet Engineering Task Force (IETF) Request for Comments (RFC) 1123 specification. + /// + /// The custom format string for a time value that is based on the IETF RFC 1123 specification. + public String RFC1123Pattern + { + get + { + return _rfc1123Pattern; + } + } + + /// + /// Gets the custom format string for a short date value. + /// + /// The custom format string for a short date value. + public String ShortDatePattern + { + get + { + return "MM/dd/yyyy"; + } + } + + /// + /// Gets the custom format string for a short time value. + /// + /// The custom format string for a short time value. + public String ShortTimePattern + { + get + { + return "HH:mm"; + } + } + + /// + /// Gets the custom format string for a sortable date and time value. + /// + /// The custom format string for a sortable date and time value. + public String SortableDateTimePattern + { + get + { + return SortableDateTimePattern; + } + } + + internal String GeneralShortTimePattern + { + get + { + return _generalShortTimePattern; + } + } + + internal String GeneralLongTimePattern + { + get + { + return _generalLongTimePattern; + } + } + + /// + /// Gets the string that separates the components of time, that is, the hour, minutes, and seconds. + /// + /// The string that separates the components of time. The default for InvariantInfo is ":". + public String TimeSeparator + { + get + { + return ":"; + } + } + + /// + /// Gets the custom format string for a universal, sortable date and time string. + /// + /// The custom format string for a universal, sortable date and time string. + public String UniversalSortableDateTimePattern + { + get + { + return _universalSortableDateTimePattern; + } + } + + /// + /// Gets the custom format string for a year and month value. + /// + /// The custom format string for a year and month value. + public String YearMonthPattern + { + get + { + return "yyyy MMMM"; + } + } + + /// + /// Gets a one-dimensional array of type String containing the culture-specific abbreviated names of the days of the week. + /// + /// A one-dimensional array of type String containing the culture-specific abbreviated names of the days of the week. The array for InvariantInfo contains "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", and "Sat". + public String[] AbbreviatedDayNames + { + get + { + return new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + } + } + + /// + /// Gets a one-dimensional string array that contains the culture-specific full names of the days of the week. + /// + /// A one-dimensional string array that contains the culture-specific full names of the days of the week. The array for InvariantInfo contains "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", and "Saturday". + public String[] DayNames + { + get + { + return new String[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + } + } + + /// + /// Gets or sets a one-dimensional string array that contains the culture-specific abbreviated names of the months. + /// + /// A one-dimensional string array with 13 elements that contains the culture-specific abbreviated names of the months. For 12-month calendars, the 13th element of the array is an empty string. + /// The array for InvariantInfo contains "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", and "". + public String[] AbbreviatedMonthNames + { + get + { + return new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" }; + } + } + + /// + /// Gets or sets a one-dimensional array of type String containing the culture-specific full names of the months. + /// + /// A one-dimensional array of type String containing the culture-specific full names of the months. In a 12-month calendar, the 13th element of the array is an empty string. + /// The array for InvariantInfo contains "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", and "". + public String[] MonthNames + { + get + { + return new String[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" }; + } + } + } +}