5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
8 // Marek Safar (marek.safar@gmail.com)
10 // (C) 2001 Marcel Narings
11 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections.Generic;
35 using System.Globalization;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
44 /// The DateTime structure represents dates and time ranging from
45 /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
49 [StructLayout (LayoutKind.Auto)]
50 public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
53 // Encodes the DateTime in 64 bits, top two bits contain the DateTimeKind,
54 // the rest contains the 62 bit value for the ticks. This reduces the
55 // memory usage from 16 to 8 bytes, see bug: 592221. This also fixes the
56 // 622127 issue and simplifies the code in reflection.c to encode DateTimes
59 const long TicksMask = 0x3fffffffffffffff;
60 const long KindMask = unchecked ((long) 0xc000000000000000);
61 const int KindShift = 62;
63 private const int dp400 = 146097;
64 private const int dp100 = 36524;
65 private const int dp4 = 1461;
67 // w32 file time starts counting from 1/1/1601 00:00 GMT
68 // which is the constant ticks from the .NET epoch
69 private const long w32file_epoch = 504911232000000000L;
71 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
72 // -- Microsoft .NET has this value.
73 private const long MAX_VALUE_TICKS = 3155378975999999999L;
76 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
79 internal const long UnixEpoch = 621355968000000000L;
81 // for OLE Automation dates
82 private const long ticks18991230 = 599264352000000000L;
83 private const double OAMinValue = -657435.0d;
84 private const double OAMaxValue = 2958466.0d;
86 public static readonly DateTime MaxValue = new DateTime (3155378975999999999);
87 public static readonly DateTime MinValue = new DateTime (0);
89 // DateTime.Parse patterns
90 // Patterns are divided to date and time patterns. The algorithm will
91 // try combinations of these patterns. The algorithm also looks for
92 // day of the week, AM/PM GMT and Z independently of the patterns.
93 private static readonly string[] ParseTimeFormats = new string [] {
109 "H tt", // Specifies AM to disallow '8'.
110 "H'\u6642'm'\u5206's'\u79D2'",
113 // DateTime.Parse date patterns extend ParseExact patterns as follows:
114 // MMM - month short name or month full name
115 // MMMM - month number or short name or month full name
117 // Parse behaves differently according to the ShorDatePattern of the
118 // DateTimeFormatInfo. The following define the date patterns for
119 // different orders of day, month and year in ShorDatePattern.
120 // Note that the year cannot go between the day and the month.
121 private static readonly string[] ParseYearDayMonthFormats = new string [] {
124 "yyyy'\u5E74'M'\u6708'd'\u65E5",
137 private static readonly string[] ParseYearMonthDayFormats = new string [] {
140 "yyyy'\u5E74'M'\u6708'd'\u65E5",
154 private static readonly string[] ParseDayMonthYearFormats = new string [] {
157 "yyyy'\u5E74'M'\u6708'd'\u65E5",
174 private static readonly string[] ParseMonthDayYearFormats = new string [] {
177 "yyyy'\u5E74'M'\u6708'd'\u65E5",
194 private static readonly string[] ParseGenericYearMonthDayFormats = new string [] {
199 "yyyy'\u5E74'M'\u6708'd'\u65E5",
204 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
205 // Note that these patterns cannot be followed by the time.
206 private static readonly string[] MonthDayShortFormats = new string [] {
211 private static readonly string[] DayMonthShortFormats = new string [] {
225 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
226 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
228 private static int AbsoluteDays (int year, int month, int day)
233 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
237 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
240 private int FromTicks(Which what)
242 int num400, num100, num4, numyears;
245 int[] days = daysmonth;
246 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
248 num400 = (totaldays / dp400);
249 totaldays -= num400 * dp400;
251 num100 = (totaldays / dp100);
252 if (num100 == 4) // leap
254 totaldays -= (num100 * dp100);
256 num4 = totaldays / dp4;
257 totaldays -= (num4 * dp4);
259 numyears = totaldays / 365 ;
261 if (numyears == 4) //leap
263 if (what == Which.Year )
264 return num400*400 + num100*100 + num4*4 + numyears + 1;
266 totaldays -= (numyears * 365) ;
267 if (what == Which.DayYear )
268 return totaldays + 1;
270 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
271 days = daysmonthleap;
273 while (totaldays >= days[M])
274 totaldays -= days[M++];
276 if (what == Which.Month )
282 static void InvalidTickValue (long ticks)
284 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
285 throw new ArgumentOutOfRangeException ("ticks", msg);
291 /// Constructs a DateTime for specified ticks
294 public DateTime (long ticks)
296 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
297 InvalidTickValue (ticks);
301 public DateTime (int year, int month, int day)
302 : this (year, month, day,0,0,0,0) {}
304 public DateTime (int year, int month, int day, int hour, int minute, int second)
305 : this (year, month, day, hour, minute, second, 0) {}
307 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
309 if (year < 1 || year > 9999 ||
310 month < 1 || month >12 ||
311 day < 1 || day > DaysInMonth(year, month) ||
312 hour < 0 || hour > 23 ||
313 minute < 0 || minute > 59 ||
314 second < 0 || second > 59 ||
315 millisecond < 0 || millisecond > 999)
316 throw new ArgumentOutOfRangeException ("Parameters describe an " +
317 "unrepresentable DateTime.");
319 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
322 public DateTime (int year, int month, int day, Calendar calendar)
323 : this (year, month, day, 0, 0, 0, 0, calendar)
327 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
328 : this (year, month, day, hour, minute, second, 0, calendar)
332 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
334 if (calendar == null)
335 throw new ArgumentNullException ("calendar");
336 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
339 public DateTime (long ticks, DateTimeKind kind)
341 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
342 InvalidTickValue (ticks);
343 if (kind < 0 || kind > DateTimeKind.Local)
344 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
346 encoded = ((long)kind << KindShift) | ticks;
349 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
350 : this (year, month, day, hour, minute, second)
352 if (kind < 0 || kind > DateTimeKind.Local)
353 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
354 encoded |= ((long)kind << KindShift);
357 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
358 : this (year, month, day, hour, minute, second, millisecond)
360 if (kind < 0 || kind > DateTimeKind.Local)
361 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
362 encoded |= ((long)kind << KindShift);
365 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
366 : this (year, month, day, hour, minute, second, millisecond, calendar)
368 if (kind < 0 || kind > DateTimeKind.Local)
369 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
370 encoded |= ((long)kind << KindShift);
374 // Not visible, but can be invoked during deserialization
376 DateTime (SerializationInfo info, StreamingContext context)
378 if (info.HasKey ("dateData")){
379 encoded = (Int64)info.GetUInt64 ("dateData");
380 } else if (info.HasKey ("ticks")){
381 encoded = info.GetInt64 ("ticks") & TicksMask;
390 public DateTime Date {
392 DateTime ret = new DateTime (Year, Month, Day);
393 ret.encoded |= encoded & KindMask;
400 return FromTicks (Which.Month);
406 return FromTicks (Which.Day);
410 public DayOfWeek DayOfWeek {
412 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
416 public int DayOfYear {
418 return FromTicks (Which.DayYear);
422 public TimeSpan TimeOfDay {
424 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
431 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
437 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
443 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
447 public int Millisecond {
449 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
453 [MethodImplAttribute(MethodImplOptions.InternalCall)]
454 internal static extern long GetTimeMonotonic ();
456 [MethodImplAttribute(MethodImplOptions.InternalCall)]
457 internal static extern long GetNow ();
460 // To reduce the time consumed by DateTime.Now, we keep
461 // the difference to map the system time into a local
462 // time into `to_local_time_span', we record the timestamp
463 // for this in `last_now'
465 static object to_local_time_span_object;
466 static long last_now;
468 public static DateTime Now {
470 long now = GetNow ();
471 DateTime dt = new DateTime (now);
473 if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
474 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
479 // This is boxed, so we avoid locking.
480 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
481 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
488 return encoded & TicksMask;
492 public static DateTime Today {
495 DateTime today = new DateTime (now.Year, now.Month, now.Day);
496 today.encoded |= ((long)DateTimeKind.Local << KindShift);
501 public static DateTime UtcNow {
503 return new DateTime (GetNow (), DateTimeKind.Utc);
509 return FromTicks (Which.Year);
513 public DateTimeKind Kind {
515 return (DateTimeKind) ((ulong)encoded >> KindShift);
521 public DateTime Add (TimeSpan value)
523 DateTime ret = AddTicks (value.Ticks);
527 public DateTime AddDays (double value)
529 return AddMilliseconds (Math.Round (value * 86400000));
532 public DateTime AddTicks (long value)
534 long res = value + (encoded & TicksMask);
535 if (res < 0 || res > MAX_VALUE_TICKS)
536 throw new ArgumentOutOfRangeException();
538 DateTime ret = new DateTime (res);
539 ret.encoded |= (encoded & KindMask);
543 public DateTime AddHours (double value)
545 return AddMilliseconds (value * 3600000);
548 public DateTime AddMilliseconds (double value)
550 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
551 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
552 throw new ArgumentOutOfRangeException();
554 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
556 return AddTicks (msticks);
559 // required to match MS implementation for OADate (OLE Automation)
560 private DateTime AddRoundedMilliseconds (double ms)
562 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
563 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
564 throw new ArgumentOutOfRangeException ();
566 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
568 return AddTicks (msticks);
571 public DateTime AddMinutes (double value)
573 return AddMilliseconds (value * 60000);
576 public DateTime AddMonths (int months)
578 int day, month, year, maxday ;
582 month = this.Month + (months % 12);
583 year = this.Year + months/12 ;
595 maxday = DaysInMonth(year, month);
599 temp = new DateTime (year, month, day);
600 temp.encoded |= encoded & KindMask;
601 return temp.Add (this.TimeOfDay);
604 public DateTime AddSeconds (double value)
606 return AddMilliseconds (value * 1000);
609 public DateTime AddYears (int value)
611 return AddMonths (value * 12);
614 public static int Compare (DateTime t1, DateTime t2)
616 long t1t = t1.encoded & TicksMask;
617 long t2t = t2.encoded & TicksMask;
627 public int CompareTo (object value)
632 if (!(value is System.DateTime))
633 throw new ArgumentException (Locale.GetText (
634 "Value is not a System.DateTime"));
636 return Compare (this, (DateTime) value);
639 public bool IsDaylightSavingTime ()
641 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
643 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
646 public int CompareTo (DateTime value)
648 return Compare (this, value);
651 public bool Equals (DateTime value)
653 return (value.encoded & TicksMask) == (encoded & TicksMask);
656 public long ToBinary ()
658 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
659 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
664 public static DateTime FromBinary (long dateData)
666 switch ((ulong)dateData >> KindShift) {
668 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
669 case 0: // Unspecified
670 return new DateTime (dateData, DateTimeKind.Unspecified);
672 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
676 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
678 return new DateTime (value.Ticks, kind);
681 public static int DaysInMonth (int year, int month)
685 if (month < 1 || month >12)
686 throw new ArgumentOutOfRangeException ();
688 if (year < 1 || year > 9999)
689 throw new ArgumentOutOfRangeException ();
691 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
695 public override bool Equals (object value)
697 if (!(value is System.DateTime))
700 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
703 public static bool Equals (DateTime t1, DateTime t2 )
705 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
708 public static DateTime FromFileTime (long fileTime)
711 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
713 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
716 public static DateTime FromFileTimeUtc (long fileTime)
719 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
721 return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
724 public static DateTime FromOADate (double d)
726 // An OLE Automation date is implemented as a floating-point number
727 // whose value is the number of days from midnight, 30 December 1899.
729 // d must be negative 657435.0 through positive 2958466.0.
730 if ((d <= OAMinValue) || (d >= OAMaxValue))
731 throw new ArgumentException ("d", "[-657435,2958466]");
733 DateTime dt = new DateTime (ticks18991230);
735 Double days = Math.Ceiling (d);
736 // integer part is the number of days (negative)
737 dt = dt.AddRoundedMilliseconds (days * 86400000);
738 // but decimals are the number of hours (in days fractions) and positive
739 Double hours = (days - d);
740 dt = dt.AddRoundedMilliseconds (hours * 86400000);
743 dt = dt.AddRoundedMilliseconds (d * 86400000);
749 public string[] GetDateTimeFormats()
751 return GetDateTimeFormats (CultureInfo.CurrentCulture);
754 public string[] GetDateTimeFormats(char format)
756 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
757 throw new FormatException ("Invalid format character.");
758 string[] result = new string[1];
759 result[0] = this.ToString(format.ToString());
763 public string[] GetDateTimeFormats(IFormatProvider provider)
765 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
766 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
767 var l = new List<string> ();
768 foreach (char c in "dDgGfFmMrRstTuUyY")
769 l.AddRange (GetDateTimeFormats (c, info));
773 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
775 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
776 throw new FormatException ("Invalid format character.");
778 // LAMESPEC: There is NO assurance that 'U' ALWAYS
779 // euqals to 'F', but since we have to iterate all
780 // the pattern strings, we cannot just use
781 // ToString("U", provider) here. I believe that the
782 // method's behavior cannot be formalized.
783 bool adjustutc = false;
792 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
793 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
796 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
798 string [] results = new string [patterns.Length];
799 DateTime val = adjustutc ? ToUniversalTime () : this;
800 for (int i = 0; i < results.Length; i++)
801 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
805 public override int GetHashCode ()
807 return (int) encoded;
810 public TypeCode GetTypeCode ()
812 return TypeCode.DateTime;
815 public static bool IsLeapYear (int year)
817 if (year < 1 || year > 9999)
818 throw new ArgumentOutOfRangeException ();
819 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
822 public static DateTime Parse (string s)
824 return Parse (s, null);
827 public static DateTime Parse (string s, IFormatProvider provider)
829 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
832 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
835 throw new ArgumentNullException ("s");
839 Exception exception = null;
840 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
846 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
848 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
849 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
851 dto = new DateTimeOffset (0, TimeSpan.Zero);
852 if (s == null || s.Length == 0) {
853 if (setExceptionOnError)
854 exception = new FormatException (formatExceptionMessage);
859 if (provider == null)
860 provider = CultureInfo.CurrentCulture;
861 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
863 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
864 string[] allDateFormats = YearMonthDayFormats (dfi);
865 if (allDateFormats == null){
870 bool longYear = false;
871 for (int i = 0; i < allDateFormats.Length; i++) {
872 string firstPart = allDateFormats [i];
873 bool incompleteFormat = false;
874 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
877 if (!incompleteFormat)
880 for (int j = 0; j < ParseTimeFormats.Length; j++) {
881 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
889 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
890 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
891 if (dayIndex == -1 || monthIndex == -1){
893 if (setExceptionOnError)
894 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
897 bool is_day_before_month = dayIndex < monthIndex;
898 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
899 for (int i = 0; i < monthDayFormats.Length; i++) {
900 bool incompleteFormat = false;
901 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
905 for (int j = 0; j < ParseTimeFormats.Length; j++) {
906 string firstPart = ParseTimeFormats [j];
907 bool incompleteFormat = false;
908 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
910 if (!incompleteFormat)
913 for (int i = 0; i < monthDayFormats.Length; i++) {
914 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
917 for (int i = 0; i < allDateFormats.Length; i++) {
918 string dateFormat = allDateFormats [i];
919 if (dateFormat[dateFormat.Length - 1] == 'T')
920 continue; // T formats must be before the time part
921 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
926 // Try as a last resort all the patterns
927 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
930 if (!setExceptionOnError)
933 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
934 exception = new FormatException (formatExceptionMessage);
938 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
940 return ParseExact (s, format, provider, DateTimeStyles.None);
943 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
945 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
946 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
947 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
948 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
949 return ParseGenericYearMonthDayFormats;
951 if (yearIndex < monthIndex)
952 if (monthIndex < dayIndex)
953 return ParseYearMonthDayFormats;
954 else if (yearIndex < dayIndex)
955 return ParseYearDayMonthFormats;
957 // The year cannot be between the date and the month
958 return ParseGenericYearMonthDayFormats;
960 else if (dayIndex < monthIndex)
961 return ParseDayMonthYearFormats;
962 else if (dayIndex < yearIndex)
963 return ParseMonthDayYearFormats;
965 // The year cannot be between the month and the date
966 return ParseGenericYearMonthDayFormats;
970 private static int _ParseNumber (string s, int valuePos,
984 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
985 if (!Char.IsDigit (s[i]))
991 digits = real_digits;
993 if (digits < min_digits) {
998 if (s.Length - valuePos < digits) {
1003 for (i = valuePos; i < digits + valuePos; i++) {
1005 if (!Char.IsDigit (c)) {
1010 number = number * 10 + (byte) (c - '0');
1013 num_parsed = digits;
1017 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1019 // FIXME: I know this is somehow lame code. Probably
1020 // it should iterate all the enum value and return
1021 // the longest match. However right now I don't see
1022 // anything but "1" and "10" - "12" that might match
1023 // two or more values. (They are only abbrev month
1024 // names, so do reverse order search). See bug #80094.
1025 for (int i = values.Length - 1; i >= 0; i--) {
1026 if (!exact && invValues [i].Length > values[i].Length) {
1027 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1029 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1033 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1035 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1044 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1047 maxlength = value.Length;
1049 if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
1050 num_parsed = maxlength;
1058 // Note that in case of Parse (exact == false) we check both for AM/PM
1059 // and the culture spcific AM/PM strings.
1060 private static bool _ParseAmPm(string s,
1063 DateTimeFormatInfo dfi,
1072 if (!IsLetter (s, valuePos)) {
1073 if (dfi.AMDesignator != "")
1080 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1081 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1082 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1084 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1085 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1086 if (exact || num_parsed != 0)
1094 // Note that in case of Parse (exact == false) we check both for ':'
1095 // and the culture spcific TimeSperator
1096 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1098 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1099 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1102 // Accept any character for DateSeparator, except TimeSeparator,
1103 // a digit or a letter.
1104 // Not documented, but seems to be MS behaviour here. See bug 54047.
1105 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1108 if (exact && s [sPos] != '/')
1111 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1112 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1119 private static bool IsLetter (string s, int pos)
1121 return pos < s.Length && Char.IsLetter (s [pos]);
1124 // To implement better DateTime.Parse we use two format strings one
1125 // for Date and one for Time. This allows us to define two different
1126 // arrays of formats for Time and Dates and to combine them more or less
1127 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1128 private static bool _DoParse (string s,
1132 out DateTime result,
1133 out DateTimeOffset dto,
1134 DateTimeFormatInfo dfi,
1135 DateTimeStyles style,
1136 bool firstPartIsDate,
1137 ref bool incompleteFormat,
1140 bool useutc = false;
1141 bool use_invariant = false;
1142 bool sloppy_parsing = false;
1143 dto = new DateTimeOffset (0, TimeSpan.Zero);
1144 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1145 incompleteFormat = false;
1147 string format = firstPart;
1148 bool afterTFormat = false;
1149 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1150 if (format.Length == 1)
1151 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1153 result = new DateTime (0);
1160 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1161 format = format.TrimStart (null);
1163 s = s.TrimStart (null); // it could be optimized, but will make little good.
1166 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1167 format = format.TrimEnd (null);
1168 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1174 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1175 sloppy_parsing = true;
1177 string chars = format;
1178 int len = format.Length, pos = 0, num = 0;
1182 int day = -1, dayofweek = -1, month = -1, year = -1;
1183 int hour = -1, minute = -1, second = -1;
1184 double fractionalSeconds = -1;
1186 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1187 bool isFirstPart = true;
1188 bool format_with_24_hours = false;
1192 if (valuePos == s.Length)
1196 if (flexibleTwoPartsParsing && pos + num == 0)
1198 bool isLetter = IsLetter(s, valuePos);
1200 if (s [valuePos] == 'Z')
1203 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1204 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1205 valuePos += num_parsed;
1210 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1211 if (IsLetter (s, valuePos + num_parsed))
1213 else if (num_parsed > 0) {
1214 valuePos += num_parsed;
1219 if (!afterTFormat && dayofweek == -1 && isLetter) {
1220 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1221 if (dayofweek == -1)
1222 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1223 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1224 valuePos += num_parsed;
1231 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1238 if (pos + num >= len)
1240 if (flexibleTwoPartsParsing && num == 0) {
1241 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1242 if (!isFirstPart && format == "")
1247 format = secondPart;
1252 isFirstPart = false;
1258 bool leading_zeros = true;
1260 if (chars[pos] == '\'') {
1262 while (pos+num < len) {
1263 if (chars[pos+num] == '\'')
1266 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1276 } else if (chars[pos] == '"') {
1278 while (pos+num < len) {
1279 if (chars[pos+num] == '"')
1282 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1292 } else if (chars[pos] == '\\') {
1297 if (s [valuePos] != chars [pos])
1303 } else if (chars[pos] == '%') {
1306 } else if (char.IsWhiteSpace (s [valuePos]) ||
1307 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1310 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1311 if (!Char.IsWhiteSpace (chars[pos]))
1318 while (ws < s.Length) {
1319 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1326 while (ws < chars.Length) {
1327 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1333 // A whitespace may match a '/' in the pattern.
1334 if (!exact && pos < chars.Length && chars[pos] == '/')
1335 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1340 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1348 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1351 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1353 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1355 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1357 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1363 if (flexibleTwoPartsParsing) {
1365 if (num == 0 || num == 3)
1366 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1367 if (num > 1 && num_parsed == -1)
1368 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1369 if (num > 1 && num_parsed == -1)
1370 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1375 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1377 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1379 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1381 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1388 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1389 } else if (num < 3) {
1390 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1392 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1393 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1395 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1396 longYear = (ly > 9999);
1401 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1402 if (num_parsed <= 2)
1403 year += (year < 30) ? 2000 : 1900;
1409 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1411 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1420 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1423 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1425 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1430 format_with_24_hours = true;
1436 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1438 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1448 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1450 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1457 leading_zeros = false;
1460 if (num > 6 || fractionalSeconds != -1)
1462 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1463 if (num_parsed == -1)
1465 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1468 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1475 if (s [valuePos] == '+')
1477 else if (s [valuePos] == '-')
1484 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1486 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1488 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1489 valuePos += num_parsed;
1494 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1495 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1496 valuePos += num_parsed;
1497 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1501 else if (!flexibleTwoPartsParsing)
1508 if (s [valuePos] == 'Z') {
1512 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1515 if (s [valuePos] == '+')
1517 else if (s [valuePos] == '-')
1522 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1523 valuePos += num_parsed;
1527 if (Char.IsDigit (s [valuePos]))
1529 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1531 valuePos += num_parsed;
1533 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1540 // LAMESPEC: This should be part of UTCpattern
1541 // string and thus should not be considered here.
1543 // Note that 'Z' is not defined as a pattern
1544 // character. Keep it for X509 certificate
1545 // verification. Also, "Z" != "'Z'" under MS.NET
1546 // ("'Z'" is just literal; handled above)
1548 if (s [valuePos] != 'Z')
1555 if (s [valuePos] != 'G')
1558 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1559 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1560 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1572 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1576 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1582 if (s[valuePos] == '.') {
1588 // '.FFF....' can be mapped to nothing
1589 if (pos + 1 < len && chars[pos + 1] == 'F') {
1591 while (pos + 1 < len && chars[pos + 1] == 'F') {
1603 if (s [valuePos] != chars [pos])
1614 valuePos += num_parsed;
1616 if (!exact && !flexibleTwoPartsParsing) {
1617 switch (chars [pos]) {
1623 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1624 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1632 pos = pos + num + 1;
1636 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1638 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1641 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1647 if (s.Length > valuePos) // extraneous tail.
1652 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1654 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1656 incompleteFormat = true;
1667 if (fractionalSeconds == -1)
1668 fractionalSeconds = 0;
1670 // If no date was given
1671 if ((day == -1) && (month == -1) && (year == -1)) {
1672 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1677 day = DateTime.Today.Day;
1678 month = DateTime.Today.Month;
1679 year = DateTime.Today.Year;
1688 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1691 year = DateTime.Today.Year;
1694 if (ampm == 0) { // AM designator
1695 if (hour >= 12 && format_with_24_hours && exact)
1700 } else if (ampm == 1) { // PM designator
1702 if (format_with_24_hours && exact)
1710 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1715 result = result.AddSeconds(fractionalSeconds);
1717 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1721 if (result != DateTime.MinValue) {
1723 dto = new DateTimeOffset (result);
1724 } catch { } // We handle this error in DateTimeOffset.Parse
1732 tzoffset = -tzoffset;
1733 tzoffmin = -tzoffmin;
1736 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1737 } catch {} // We handle this error in DateTimeOffset.Parse
1739 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1742 long newticks = (result - dto.Offset).Ticks;
1744 newticks += TimeSpan.TicksPerDay;
1745 result = new DateTime (newticks, DateTimeKind.Utc);
1746 if ((style & DateTimeStyles.RoundtripKind) != 0)
1747 result = result.ToLocalTime ();
1748 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1749 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1750 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1751 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1753 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1754 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1755 if (adjustToUniversal)
1756 result = result.ToUniversalTime ();
1757 else if (adjustToLocal)
1758 result = result.ToLocalTime ();
1764 public static DateTime ParseExact (string s, string format,
1765 IFormatProvider provider, DateTimeStyles style)
1768 throw new ArgumentNullException ("format");
1770 string [] formats = new string [1];
1771 formats[0] = format;
1773 return ParseExact (s, formats, provider, style);
1776 public static DateTime ParseExact (string s, string[] formats,
1777 IFormatProvider provider,
1778 DateTimeStyles style)
1780 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1783 throw new ArgumentNullException ("s");
1784 if (formats == null)
1785 throw new ArgumentNullException ("formats");
1786 if (formats.Length == 0)
1787 throw new FormatException ("Format specifier was invalid.");
1790 bool longYear = false;
1792 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1797 private static void CheckStyle (DateTimeStyles style)
1799 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1801 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1802 (style & DateTimeStyles.AssumeUniversal) != 0)
1803 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1805 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1806 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1809 public static bool TryParse (string s, out DateTime result)
1813 Exception exception = null;
1816 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1823 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1827 Exception exception = null;
1830 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1837 public static bool TryParseExact (string s, string format,
1838 IFormatProvider provider,
1839 DateTimeStyles style,
1840 out DateTime result)
1843 formats = new string [1];
1844 formats[0] = format;
1846 return TryParseExact (s, formats, provider, style, out result);
1849 public static bool TryParseExact (string s, string[] formats,
1850 IFormatProvider provider,
1851 DateTimeStyles style,
1852 out DateTime result)
1855 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1857 bool longYear = false;
1859 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1866 private static bool ParseExact (string s, string [] formats,
1867 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1868 bool exact, ref bool longYear,
1869 bool setExceptionOnError, ref Exception exception)
1872 bool incompleteFormat = false;
1873 for (i = 0; i < formats.Length; i++)
1876 string format = formats[i];
1877 if (format == null || format == String.Empty)
1881 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1887 if (setExceptionOnError)
1888 exception = new FormatException ("Invalid format string");
1889 ret = DateTime.MinValue;
1893 public TimeSpan Subtract (DateTime value)
1895 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1898 public DateTime Subtract(TimeSpan value)
1902 newticks = Ticks - value.Ticks;
1903 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1904 throw new ArgumentOutOfRangeException ();
1905 DateTime ret = new DateTime (newticks);
1906 ret.encoded |= (encoded & KindMask);
1910 public long ToFileTime()
1912 DateTime universalTime = ToUniversalTime();
1914 if (universalTime.Ticks < w32file_epoch) {
1915 throw new ArgumentOutOfRangeException("file time is not valid");
1918 return(universalTime.Ticks - w32file_epoch);
1921 public long ToFileTimeUtc()
1923 if (Kind == DateTimeKind.Local)
1924 return ToFileTime ();
1926 if (Ticks < w32file_epoch) {
1927 throw new ArgumentOutOfRangeException("file time is not valid");
1930 return (Ticks - w32file_epoch);
1933 public string ToLongDateString()
1935 return ToString ("D");
1938 public string ToLongTimeString()
1940 return ToString ("T");
1943 public double ToOADate ()
1945 long t = this.Ticks;
1946 // uninitialized DateTime case
1949 // we can't reach minimum value
1950 if (t < 31242239136000000)
1951 return OAMinValue + 0.001;
1953 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1954 double result = ts.TotalDays;
1955 // t < 0 (where 599264352000000000 == 0.0d for OA)
1956 if (t < 599264352000000000) {
1957 // negative days (int) but decimals are positive
1958 double d = Math.Ceiling (result);
1959 result = d - 2 - (result - d);
1962 // we can't reach maximum value
1963 if (result >= OAMaxValue)
1964 result = OAMaxValue - 0.00000001d;
1969 public string ToShortDateString()
1971 return ToString ("d");
1974 public string ToShortTimeString()
1976 return ToString ("t");
1979 public override string ToString ()
1981 return ToString ("G", null);
1984 public string ToString (IFormatProvider provider)
1986 return ToString (null, provider);
1989 public string ToString (string format)
1991 return ToString (format, null);
1994 public string ToString (string format, IFormatProvider provider)
1996 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1998 if (format == null || format == String.Empty)
2001 if (format.Length == 1) {
2002 char fchar = format [0];
2003 bool use_invariant, useutc;
2004 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2006 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2007 // return ToUniversalTime()._ToString (format, dfi);
2010 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2013 dfi = DateTimeFormatInfo.InvariantInfo;
2016 // Don't convert UTC value. It just adds 'Z' for
2017 // 'u' format, for the same ticks.
2018 return DateTimeUtils.ToString (this, format, dfi);
2021 public DateTime ToLocalTime ()
2023 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2026 public DateTime ToUniversalTime()
2028 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2033 public static DateTime operator +(DateTime d, TimeSpan t)
2036 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2037 if (res < 0 || res > MAX_VALUE_TICKS){
2038 throw new ArgumentOutOfRangeException ();
2041 return new DateTime (res, d.Kind);
2042 } catch (OverflowException){
2043 throw new ArgumentOutOfRangeException ();
2047 public static bool operator ==(DateTime d1, DateTime d2)
2049 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2052 public static bool operator >(DateTime t1,DateTime t2)
2054 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2057 public static bool operator >=(DateTime t1,DateTime t2)
2059 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2062 public static bool operator !=(DateTime d1, DateTime d2)
2064 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2067 public static bool operator <(DateTime t1, DateTime t2)
2069 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2072 public static bool operator <=(DateTime t1, DateTime t2)
2074 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2077 public static TimeSpan operator -(DateTime d1, DateTime d2)
2079 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2082 public static DateTime operator -(DateTime d, TimeSpan t)
2085 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2086 if (res < 0 || res > MAX_VALUE_TICKS)
2087 throw new ArgumentOutOfRangeException ();
2088 return new DateTime (res, d.Kind);
2089 } catch (OverflowException){
2090 throw new ArgumentOutOfRangeException ();
2094 bool IConvertible.ToBoolean (IFormatProvider provider)
2096 throw new InvalidCastException();
2099 byte IConvertible.ToByte (IFormatProvider provider)
2101 throw new InvalidCastException();
2105 char IConvertible.ToChar (IFormatProvider provider)
2107 throw new InvalidCastException();
2110 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2115 decimal IConvertible.ToDecimal (IFormatProvider provider)
2117 throw new InvalidCastException();
2120 double IConvertible.ToDouble (IFormatProvider provider)
2122 throw new InvalidCastException();
2125 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2127 throw new InvalidCastException();
2130 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2132 throw new InvalidCastException();
2135 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2137 throw new InvalidCastException();
2140 SByte IConvertible.ToSByte (IFormatProvider provider)
2142 throw new InvalidCastException();
2145 Single IConvertible.ToSingle (IFormatProvider provider)
2147 throw new InvalidCastException();
2150 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2152 if (targetType == null)
2153 throw new ArgumentNullException ("targetType");
2155 if (targetType == typeof (DateTime))
2157 else if (targetType == typeof (String))
2158 return this.ToString (provider);
2159 else if (targetType == typeof (Object))
2162 throw new InvalidCastException();
2165 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2167 throw new InvalidCastException();
2170 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2172 throw new InvalidCastException();
2175 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2177 throw new InvalidCastException();
2180 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2183 info.AddValue ("ticks", t);
2185 // This is the new .NET format, encodes the kind on the top bits
2186 info.AddValue ("dateData", (UInt64)encoded);
2190 static DateTime () {
2191 if (MonoTouchAOTHelper.FalseFlag) {
2192 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2193 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();