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 [] {
217 private static readonly string[] ExoticAndNonStandardFormats = new string[] {
229 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
230 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
232 private static int AbsoluteDays (int year, int month, int day)
237 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
241 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
244 private int FromTicks(Which what)
246 int num400, num100, num4, numyears;
249 int[] days = daysmonth;
250 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
252 num400 = (totaldays / dp400);
253 totaldays -= num400 * dp400;
255 num100 = (totaldays / dp100);
256 if (num100 == 4) // leap
258 totaldays -= (num100 * dp100);
260 num4 = totaldays / dp4;
261 totaldays -= (num4 * dp4);
263 numyears = totaldays / 365 ;
265 if (numyears == 4) //leap
267 if (what == Which.Year )
268 return num400*400 + num100*100 + num4*4 + numyears + 1;
270 totaldays -= (numyears * 365) ;
271 if (what == Which.DayYear )
272 return totaldays + 1;
274 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
275 days = daysmonthleap;
277 while (totaldays >= days[M])
278 totaldays -= days[M++];
280 if (what == Which.Month )
286 static void InvalidTickValue (long ticks)
288 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
289 throw new ArgumentOutOfRangeException ("ticks", msg);
295 /// Constructs a DateTime for specified ticks
298 public DateTime (long ticks)
300 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
301 InvalidTickValue (ticks);
305 public DateTime (int year, int month, int day)
306 : this (year, month, day,0,0,0,0) {}
308 public DateTime (int year, int month, int day, int hour, int minute, int second)
309 : this (year, month, day, hour, minute, second, 0) {}
311 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
313 if (year < 1 || year > 9999 ||
314 month < 1 || month >12 ||
315 day < 1 || day > DaysInMonth(year, month) ||
316 hour < 0 || hour > 23 ||
317 minute < 0 || minute > 59 ||
318 second < 0 || second > 59 ||
319 millisecond < 0 || millisecond > 999)
320 throw new ArgumentOutOfRangeException ("Parameters describe an " +
321 "unrepresentable DateTime.");
323 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
326 public DateTime (int year, int month, int day, Calendar calendar)
327 : this (year, month, day, 0, 0, 0, 0, calendar)
331 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
332 : this (year, month, day, hour, minute, second, 0, calendar)
336 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
338 if (calendar == null)
339 throw new ArgumentNullException ("calendar");
340 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
343 public DateTime (long ticks, DateTimeKind kind)
345 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
346 InvalidTickValue (ticks);
347 if (kind < 0 || kind > DateTimeKind.Local)
348 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
350 encoded = ((long)kind << KindShift) | ticks;
353 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
354 : this (year, month, day, hour, minute, second)
356 if (kind < 0 || kind > DateTimeKind.Local)
357 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
358 encoded |= ((long)kind << KindShift);
361 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
362 : this (year, month, day, hour, minute, second, millisecond)
364 if (kind < 0 || kind > DateTimeKind.Local)
365 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
366 encoded |= ((long)kind << KindShift);
369 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
370 : this (year, month, day, hour, minute, second, millisecond, calendar)
372 if (kind < 0 || kind > DateTimeKind.Local)
373 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
374 encoded |= ((long)kind << KindShift);
378 // Not visible, but can be invoked during deserialization
380 DateTime (SerializationInfo info, StreamingContext context)
382 if (info.HasKey ("dateData")){
383 encoded = (Int64)info.GetUInt64 ("dateData");
384 } else if (info.HasKey ("ticks")){
385 encoded = info.GetInt64 ("ticks") & TicksMask;
394 public DateTime Date {
396 DateTime ret = new DateTime (Year, Month, Day);
397 ret.encoded |= encoded & KindMask;
404 return FromTicks (Which.Month);
410 return FromTicks (Which.Day);
414 public DayOfWeek DayOfWeek {
416 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
420 public int DayOfYear {
422 return FromTicks (Which.DayYear);
426 public TimeSpan TimeOfDay {
428 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
435 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
441 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
447 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
451 public int Millisecond {
453 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
457 [MethodImplAttribute(MethodImplOptions.InternalCall)]
458 internal static extern long GetTimeMonotonic ();
460 [MethodImplAttribute(MethodImplOptions.InternalCall)]
461 internal static extern long GetNow ();
464 // To reduce the time consumed by DateTime.Now, we keep
465 // the difference to map the system time into a local
466 // time into `to_local_time_span', we record the timestamp
467 // for this in `last_now'
469 static object to_local_time_span_object;
470 static long last_now;
472 public static DateTime Now {
474 long now = GetNow ();
475 DateTime dt = new DateTime (now);
477 if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
478 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
483 // This is boxed, so we avoid locking.
484 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
485 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
492 return encoded & TicksMask;
496 public static DateTime Today {
499 DateTime today = new DateTime (now.Year, now.Month, now.Day);
500 today.encoded |= ((long)DateTimeKind.Local << KindShift);
505 public static DateTime UtcNow {
507 return new DateTime (GetNow (), DateTimeKind.Utc);
513 return FromTicks (Which.Year);
517 public DateTimeKind Kind {
519 return (DateTimeKind) ((ulong)encoded >> KindShift);
525 public DateTime Add (TimeSpan value)
527 DateTime ret = AddTicks (value.Ticks);
531 public DateTime AddDays (double value)
533 return AddMilliseconds (Math.Round (value * 86400000));
536 public DateTime AddTicks (long value)
538 long res = value + (encoded & TicksMask);
539 if (res < 0 || res > MAX_VALUE_TICKS)
540 throw new ArgumentOutOfRangeException();
542 DateTime ret = new DateTime (res);
543 ret.encoded |= (encoded & KindMask);
547 public DateTime AddHours (double value)
549 return AddMilliseconds (value * 3600000);
552 public DateTime AddMilliseconds (double value)
554 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
555 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
556 throw new ArgumentOutOfRangeException();
558 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
560 return AddTicks (msticks);
563 // required to match MS implementation for OADate (OLE Automation)
564 private DateTime AddRoundedMilliseconds (double ms)
566 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
567 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
568 throw new ArgumentOutOfRangeException ();
570 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
572 return AddTicks (msticks);
575 public DateTime AddMinutes (double value)
577 return AddMilliseconds (value * 60000);
580 public DateTime AddMonths (int months)
582 int day, month, year, maxday ;
586 month = this.Month + (months % 12);
587 year = this.Year + months/12 ;
599 maxday = DaysInMonth(year, month);
603 temp = new DateTime (year, month, day);
604 temp.encoded |= encoded & KindMask;
605 return temp.Add (this.TimeOfDay);
608 public DateTime AddSeconds (double value)
610 return AddMilliseconds (value * 1000);
613 public DateTime AddYears (int value)
615 return AddMonths (value * 12);
618 public static int Compare (DateTime t1, DateTime t2)
620 long t1t = t1.encoded & TicksMask;
621 long t2t = t2.encoded & TicksMask;
631 public int CompareTo (object value)
636 if (!(value is System.DateTime))
637 throw new ArgumentException (Locale.GetText (
638 "Value is not a System.DateTime"));
640 return Compare (this, (DateTime) value);
643 public bool IsDaylightSavingTime ()
645 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
647 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
650 public int CompareTo (DateTime value)
652 return Compare (this, value);
655 public bool Equals (DateTime value)
657 return (value.encoded & TicksMask) == (encoded & TicksMask);
660 public long ToBinary ()
662 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
663 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
668 public static DateTime FromBinary (long dateData)
670 switch ((ulong)dateData >> KindShift) {
672 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
673 case 0: // Unspecified
674 return new DateTime (dateData, DateTimeKind.Unspecified);
676 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
680 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
682 return new DateTime (value.Ticks, kind);
685 public static int DaysInMonth (int year, int month)
689 if (month < 1 || month >12)
690 throw new ArgumentOutOfRangeException ();
692 if (year < 1 || year > 9999)
693 throw new ArgumentOutOfRangeException ();
695 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
699 public override bool Equals (object value)
701 if (!(value is System.DateTime))
704 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
707 public static bool Equals (DateTime t1, DateTime t2 )
709 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
712 public static DateTime FromFileTime (long fileTime)
715 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
717 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
720 public static DateTime FromFileTimeUtc (long fileTime)
723 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
725 return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
728 public static DateTime FromOADate (double d)
730 // An OLE Automation date is implemented as a floating-point number
731 // whose value is the number of days from midnight, 30 December 1899.
733 // d must be negative 657435.0 through positive 2958466.0.
734 if ((d <= OAMinValue) || (d >= OAMaxValue))
735 throw new ArgumentException ("d", "[-657435,2958466]");
737 DateTime dt = new DateTime (ticks18991230);
739 Double days = Math.Ceiling (d);
740 // integer part is the number of days (negative)
741 dt = dt.AddRoundedMilliseconds (days * 86400000);
742 // but decimals are the number of hours (in days fractions) and positive
743 Double hours = (days - d);
744 dt = dt.AddRoundedMilliseconds (hours * 86400000);
747 dt = dt.AddRoundedMilliseconds (d * 86400000);
753 public string[] GetDateTimeFormats()
755 return GetDateTimeFormats (CultureInfo.CurrentCulture);
758 public string[] GetDateTimeFormats(char format)
760 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
761 throw new FormatException ("Invalid format character.");
762 string[] result = new string[1];
763 result[0] = this.ToString(format.ToString());
767 public string[] GetDateTimeFormats(IFormatProvider provider)
769 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
770 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
771 var l = new List<string> ();
772 foreach (char c in "dDgGfFmMrRstTuUyY")
773 l.AddRange (GetDateTimeFormats (c, info));
777 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
779 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
780 throw new FormatException ("Invalid format character.");
782 // LAMESPEC: There is NO assurance that 'U' ALWAYS
783 // euqals to 'F', but since we have to iterate all
784 // the pattern strings, we cannot just use
785 // ToString("U", provider) here. I believe that the
786 // method's behavior cannot be formalized.
787 bool adjustutc = false;
796 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
797 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
800 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
802 string [] results = new string [patterns.Length];
803 DateTime val = adjustutc ? ToUniversalTime () : this;
804 for (int i = 0; i < results.Length; i++)
805 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
809 public override int GetHashCode ()
811 return (int) encoded;
814 public TypeCode GetTypeCode ()
816 return TypeCode.DateTime;
819 public static bool IsLeapYear (int year)
821 if (year < 1 || year > 9999)
822 throw new ArgumentOutOfRangeException ();
823 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
826 public static DateTime Parse (string s)
828 return Parse (s, null);
831 public static DateTime Parse (string s, IFormatProvider provider)
833 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
836 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
839 throw new ArgumentNullException ("s");
843 Exception exception = null;
844 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
850 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
852 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
853 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
855 dto = new DateTimeOffset (0, TimeSpan.Zero);
856 if (s == null || s.Length == 0) {
857 if (setExceptionOnError)
858 exception = new FormatException (formatExceptionMessage);
863 if (provider == null)
864 provider = CultureInfo.CurrentCulture;
865 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
867 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
868 string[] allDateFormats = YearMonthDayFormats (dfi);
869 if (allDateFormats == null){
874 bool longYear = false;
875 for (int i = 0; i < allDateFormats.Length; i++) {
876 string firstPart = allDateFormats [i];
877 bool incompleteFormat = false;
878 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
881 if (!incompleteFormat)
884 for (int j = 0; j < ParseTimeFormats.Length; j++) {
885 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
889 if (_DoParse (s, firstPart, "zzz", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
896 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
897 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
898 if (dayIndex == -1 || monthIndex == -1){
900 if (setExceptionOnError)
901 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
904 bool is_day_before_month = dayIndex < monthIndex;
905 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
906 for (int i = 0; i < monthDayFormats.Length; i++) {
907 bool incompleteFormat = false;
908 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
912 for (int j = 0; j < ParseTimeFormats.Length; j++) {
913 string firstPart = ParseTimeFormats [j];
914 bool incompleteFormat = false;
915 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
917 if (!incompleteFormat)
920 for (int i = 0; i < monthDayFormats.Length; i++) {
921 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
924 for (int i = 0; i < allDateFormats.Length; i++) {
925 string dateFormat = allDateFormats [i];
926 if (dateFormat[dateFormat.Length - 1] == 'T')
927 continue; // T formats must be before the time part
928 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
933 // Try as a last resort all the patterns
934 if (CoreParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
937 if (CoreParseExact (s, ExoticAndNonStandardFormats, dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
940 if (!setExceptionOnError)
943 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
944 exception = new FormatException (formatExceptionMessage);
948 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
950 return ParseExact (s, format, provider, DateTimeStyles.None);
953 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
955 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
956 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
957 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
958 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
959 return ParseGenericYearMonthDayFormats;
961 if (yearIndex < monthIndex)
962 if (monthIndex < dayIndex)
963 return ParseYearMonthDayFormats;
964 else if (yearIndex < dayIndex)
965 return ParseYearDayMonthFormats;
967 // The year cannot be between the date and the month
968 return ParseGenericYearMonthDayFormats;
970 else if (dayIndex < monthIndex)
971 return ParseDayMonthYearFormats;
972 else if (dayIndex < yearIndex)
973 return ParseMonthDayYearFormats;
975 // The year cannot be between the month and the date
976 return ParseGenericYearMonthDayFormats;
980 private static int _ParseNumber (string s, int valuePos,
994 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
995 if (!Char.IsDigit (s[i]))
1001 digits = real_digits;
1003 if (digits < min_digits) {
1008 if (s.Length - valuePos < digits) {
1013 for (i = valuePos; i < digits + valuePos; i++) {
1015 if (!Char.IsDigit (c)) {
1020 number = number * 10 + (byte) (c - '0');
1023 num_parsed = digits;
1027 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1029 // FIXME: I know this is somehow lame code. Probably
1030 // it should iterate all the enum value and return
1031 // the longest match. However right now I don't see
1032 // anything but "1" and "10" - "12" that might match
1033 // two or more values. (They are only abbrev month
1034 // names, so do reverse order search). See bug #80094.
1035 for (int i = values.Length - 1; i >= 0; i--) {
1036 if (!exact && invValues [i].Length > values[i].Length) {
1037 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1039 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1043 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1045 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1054 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1057 maxlength = value.Length;
1059 if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
1060 num_parsed = maxlength;
1068 // Note that in case of Parse (exact == false) we check both for AM/PM
1069 // and the culture spcific AM/PM strings.
1070 private static bool _ParseAmPm(string s,
1073 DateTimeFormatInfo dfi,
1082 if (!IsLetter (s, valuePos)) {
1083 if (dfi.AMDesignator != "")
1090 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1091 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1092 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1094 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1095 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1096 if (exact || num_parsed != 0)
1104 // Note that in case of Parse (exact == false) we check both for ':'
1105 // and the culture spcific TimeSperator
1106 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1108 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1109 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1112 // Accept any character for DateSeparator, except TimeSeparator,
1113 // a digit or a letter.
1114 // Not documented, but seems to be MS behaviour here. See bug 54047.
1115 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1118 if (exact && s [sPos] != '/')
1121 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1122 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1129 private static bool IsLetter (string s, int pos)
1131 return pos < s.Length && Char.IsLetter (s [pos]);
1134 // To implement better DateTime.Parse we use two format strings one
1135 // for Date and one for Time. This allows us to define two different
1136 // arrays of formats for Time and Dates and to combine them more or less
1137 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1138 private static bool _DoParse (string s,
1142 out DateTime result,
1143 out DateTimeOffset dto,
1144 DateTimeFormatInfo dfi,
1145 DateTimeStyles style,
1146 bool firstPartIsDate,
1147 ref bool incompleteFormat,
1149 bool dateTimeOffset = false)
1151 bool useutc = false;
1152 bool use_invariant = false;
1153 bool sloppy_parsing = false;
1154 dto = new DateTimeOffset (0, TimeSpan.Zero);
1155 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1156 incompleteFormat = false;
1158 string format = firstPart;
1159 bool afterTFormat = false;
1160 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1161 if (format.Length == 1)
1162 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant, dateTimeOffset);
1164 result = new DateTime (0);
1171 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1172 format = format.TrimStart (null);
1174 s = s.TrimStart (null); // it could be optimized, but will make little good.
1177 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1178 format = format.TrimEnd (null);
1179 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1185 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1186 sloppy_parsing = true;
1188 string chars = format;
1189 int len = format.Length, pos = 0, num = 0;
1193 int day = -1, dayofweek = -1, month = -1, year = -1;
1194 int hour = -1, minute = -1, second = -1;
1195 double fractionalSeconds = -1;
1197 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1198 bool isFirstPart = true;
1199 bool format_with_24_hours = false;
1203 if (valuePos == s.Length)
1207 if (flexibleTwoPartsParsing && pos + num == 0)
1209 bool isLetter = IsLetter(s, valuePos);
1211 if (s [valuePos] == 'Z')
1214 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1215 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1216 valuePos += num_parsed;
1221 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1222 if (IsLetter (s, valuePos + num_parsed))
1224 else if (num_parsed > 0) {
1225 valuePos += num_parsed;
1230 if (!afterTFormat && dayofweek == -1 && isLetter) {
1231 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1232 if (dayofweek == -1)
1233 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1234 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1235 valuePos += num_parsed;
1242 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1249 if (pos + num >= len)
1251 if (flexibleTwoPartsParsing && num == 0) {
1252 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1253 if (!isFirstPart && format == "")
1258 format = secondPart;
1263 isFirstPart = false;
1269 bool leading_zeros = true;
1271 if (chars[pos] == '\'') {
1273 while (pos+num < len) {
1274 if (chars[pos+num] == '\'')
1277 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1287 } else if (chars[pos] == '"') {
1289 while (pos+num < len) {
1290 if (chars[pos+num] == '"')
1293 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1303 } else if (chars[pos] == '\\') {
1308 if (s [valuePos] != chars [pos])
1314 } else if (chars[pos] == '%') {
1317 } else if (char.IsWhiteSpace (s [valuePos]) ||
1318 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1321 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1322 if (!Char.IsWhiteSpace (chars[pos]))
1329 while (ws < s.Length) {
1330 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1337 while (ws < chars.Length) {
1338 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1344 // A whitespace may match a '/' in the pattern.
1345 if (!exact && pos < chars.Length && chars[pos] == '/')
1346 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1351 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1359 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1362 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1364 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1366 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1368 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1374 if (flexibleTwoPartsParsing) {
1376 if (num == 0 || num == 3)
1377 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1378 if (num > 1 && num_parsed == -1)
1379 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1380 if (num > 1 && num_parsed == -1)
1381 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1386 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1388 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1390 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1392 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1399 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1400 } else if (num < 3) {
1401 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1403 year = _ParseNumber (s, valuePos, exact ? num + 1 : 3, num + 1, false, sloppy_parsing, out num_parsed);
1404 longYear = (year > 9999);
1407 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1408 if (num_parsed <= 2)
1409 year += (year < 30) ? 2000 : 1900;
1415 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1417 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1426 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1429 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1431 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1436 format_with_24_hours = true;
1442 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1444 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1454 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1456 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1463 leading_zeros = false;
1466 if (num > 6 || fractionalSeconds != -1)
1468 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1469 if (num_parsed == -1)
1471 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1473 //Parse ISO8601 with an unlimited number of fractional digits.
1474 if (!exact && num == 6 && hour != -1 && minute != -1 && second != -1) {
1475 var total_num_parsed = num_parsed;
1477 valuePos += num_parsed;
1478 decimalNumber = (double) _ParseNumber (s, valuePos, 0, 1, leading_zeros, sloppy_parsing, out num_parsed);
1479 if (num_parsed < 1) {
1484 total_num_parsed += num_parsed;
1485 if (total_num_parsed > 15)
1486 continue; //not enough precision, ignore additional digits.
1488 fractionalSeconds += decimalNumber / Math.Pow (10.0, total_num_parsed);
1493 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1500 if (s [valuePos] == '+')
1502 else if (s [valuePos] == '-')
1509 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1511 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1513 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1514 valuePos += num_parsed;
1519 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1520 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1521 valuePos += num_parsed;
1522 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1526 else if (!flexibleTwoPartsParsing)
1533 if (s [valuePos] == 'Z') {
1537 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1540 if (s [valuePos] == '+')
1542 else if (s [valuePos] == '-')
1547 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1548 valuePos += num_parsed;
1552 if (Char.IsDigit (s [valuePos]))
1554 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1556 valuePos += num_parsed;
1558 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1564 // LAMESPEC: This should be part of UTCpattern
1565 // string and thus should not be considered here.
1567 // Note that 'Z' is not defined as a pattern
1568 // character. Keep it for X509 certificate
1569 // verification. Also, "Z" != "'Z'" under MS.NET
1570 // ("'Z'" is just literal; handled above)
1572 if (s [valuePos] != 'Z')
1579 if (s [valuePos] != 'G')
1582 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1583 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1584 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1596 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1600 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1606 if (s[valuePos] == '.') {
1612 // '.FFF....' can be mapped to nothing
1613 if (pos + 1 < len && chars[pos + 1] == 'F') {
1615 while (pos + 1 < len && chars[pos + 1] == 'F') {
1627 if (s [valuePos] != chars [pos])
1638 valuePos += num_parsed;
1640 if (!exact && !flexibleTwoPartsParsing) {
1641 switch (chars [pos]) {
1647 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1648 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1656 pos = pos + num + 1;
1660 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1662 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1665 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1671 if (s.Length > valuePos) // extraneous tail.
1676 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1678 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1680 incompleteFormat = true;
1691 if (fractionalSeconds == -1)
1692 fractionalSeconds = 0;
1694 // If no date was given
1695 if ((day == -1) && (month == -1) && (year == -1)) {
1696 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1701 day = DateTime.Today.Day;
1702 month = DateTime.Today.Month;
1703 year = DateTime.Today.Year;
1712 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1715 year = DateTime.Today.Year;
1718 if (ampm == 0) { // AM designator
1719 if (hour >= 12 && format_with_24_hours && exact)
1724 } else if (ampm == 1) { // PM designator
1726 if (format_with_24_hours && exact)
1734 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1739 result = result.AddSeconds(fractionalSeconds);
1741 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1745 if (result != DateTime.MinValue) {
1747 if (((style & DateTimeStyles.AssumeUniversal) != 0) || useutc) {
1748 dto = new DateTimeOffset (result, TimeSpan.Zero);
1749 } else if ((style & DateTimeStyles.AssumeLocal) != 0) {
1750 var offset = use_invariant ?
1752 TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
1753 dto = new DateTimeOffset (result, offset);
1755 dto = new DateTimeOffset (result);
1757 } catch { } // We handle this error in DateTimeOffset.Parse
1765 tzoffset = -tzoffset;
1766 tzoffmin = -tzoffmin;
1769 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1770 } catch {} // We handle this error in DateTimeOffset.Parse
1772 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1775 long newticks = (result - dto.Offset).Ticks;
1777 newticks += TimeSpan.TicksPerDay;
1778 result = new DateTime (newticks, DateTimeKind.Utc);
1779 if ((style & DateTimeStyles.RoundtripKind) != 0)
1780 result = result.ToLocalTime ();
1781 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1782 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1783 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1784 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1786 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1787 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1788 if (adjustToUniversal)
1789 result = result.ToUniversalTime ();
1790 else if (adjustToLocal)
1791 result = result.ToLocalTime ();
1797 public static DateTime ParseExact (string s, string format,
1798 IFormatProvider provider, DateTimeStyles style)
1801 throw new ArgumentNullException ("format");
1803 string [] formats = new string [1];
1804 formats[0] = format;
1806 return ParseExact (s, formats, provider, style);
1809 public static DateTime ParseExact (string s, string[] formats,
1810 IFormatProvider provider,
1811 DateTimeStyles style)
1813 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1816 throw new ArgumentNullException ("s");
1817 if (formats == null)
1818 throw new ArgumentNullException ("formats");
1819 if (formats.Length == 0)
1820 throw new FormatException ("Format specifier was invalid.");
1824 bool longYear = false;
1826 if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1831 private static void CheckStyle (DateTimeStyles style)
1833 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1835 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1836 (style & DateTimeStyles.AssumeUniversal) != 0)
1837 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1839 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1840 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1843 public static bool TryParse (string s, out DateTime result)
1847 Exception exception = null;
1850 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1857 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1861 Exception exception = null;
1864 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1871 public static bool TryParseExact (string s, string format,
1872 IFormatProvider provider,
1873 DateTimeStyles style,
1874 out DateTime result)
1877 formats = new string [1];
1878 formats[0] = format;
1880 return TryParseExact (s, formats, provider, style, out result);
1883 public static bool TryParseExact (string s, string[] formats,
1884 IFormatProvider provider,
1885 DateTimeStyles style,
1886 out DateTime result)
1889 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1892 bool longYear = false;
1894 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1901 internal static bool CoreParseExact (string s, string [] formats,
1902 DateTimeFormatInfo dfi, DateTimeStyles style,
1903 out DateTime ret, out DateTimeOffset dto,
1904 bool exact, ref bool longYear,
1905 bool setExceptionOnError, ref Exception exception,
1906 bool dateTimeOffset = false)
1908 dto = new DateTimeOffset (0, TimeSpan.Zero);
1910 bool incompleteFormat = false;
1911 for (i = 0; i < formats.Length; i++)
1914 string format = formats[i];
1915 if (format == null || format == String.Empty)
1918 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1924 if (setExceptionOnError)
1925 exception = new FormatException ("Invalid format string");
1926 ret = DateTime.MinValue;
1930 public TimeSpan Subtract (DateTime value)
1932 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1935 public DateTime Subtract(TimeSpan value)
1939 newticks = Ticks - value.Ticks;
1940 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1941 throw new ArgumentOutOfRangeException ();
1942 DateTime ret = new DateTime (newticks);
1943 ret.encoded |= (encoded & KindMask);
1947 public long ToFileTime()
1949 DateTime universalTime = ToUniversalTime();
1951 if (universalTime.Ticks < w32file_epoch) {
1952 throw new ArgumentOutOfRangeException("file time is not valid");
1955 return(universalTime.Ticks - w32file_epoch);
1958 public long ToFileTimeUtc()
1960 if (Kind == DateTimeKind.Local)
1961 return ToFileTime ();
1963 if (Ticks < w32file_epoch) {
1964 throw new ArgumentOutOfRangeException("file time is not valid");
1967 return (Ticks - w32file_epoch);
1970 public string ToLongDateString()
1972 return ToString ("D");
1975 public string ToLongTimeString()
1977 return ToString ("T");
1980 public double ToOADate ()
1982 long t = this.Ticks;
1983 // uninitialized DateTime case
1986 // we can't reach minimum value
1987 if (t < 31242239136000000)
1988 return OAMinValue + 0.001;
1990 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1991 double result = ts.TotalDays;
1992 // t < 0 (where 599264352000000000 == 0.0d for OA)
1993 if (t < 599264352000000000) {
1994 // negative days (int) but decimals are positive
1995 double d = Math.Ceiling (result);
1996 result = d - 2 - (result - d);
1999 // we can't reach maximum value
2000 if (result >= OAMaxValue)
2001 result = OAMaxValue - 0.00000001d;
2006 public string ToShortDateString()
2008 return ToString ("d");
2011 public string ToShortTimeString()
2013 return ToString ("t");
2016 public override string ToString ()
2018 return ToString ("G", null);
2021 public string ToString (IFormatProvider provider)
2023 return ToString (null, provider);
2026 public string ToString (string format)
2028 return ToString (format, null);
2031 public string ToString (string format, IFormatProvider provider)
2033 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2035 if (format == null || format == String.Empty)
2038 if (format.Length == 1) {
2039 char fchar = format [0];
2040 bool use_invariant, useutc;
2041 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2043 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2044 // return ToUniversalTime()._ToString (format, dfi);
2047 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2050 dfi = DateTimeFormatInfo.InvariantInfo;
2053 // Don't convert UTC value. It just adds 'Z' for
2054 // 'u' format, for the same ticks.
2055 return DateTimeUtils.ToString (this, format, dfi);
2058 public DateTime ToLocalTime ()
2060 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2063 public DateTime ToUniversalTime()
2065 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2070 public static DateTime operator +(DateTime d, TimeSpan t)
2073 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2074 if (res < 0 || res > MAX_VALUE_TICKS){
2075 throw new ArgumentOutOfRangeException ();
2078 return new DateTime (res, d.Kind);
2079 } catch (OverflowException){
2080 throw new ArgumentOutOfRangeException ();
2084 public static bool operator ==(DateTime d1, DateTime d2)
2086 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2089 public static bool operator >(DateTime t1,DateTime t2)
2091 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2094 public static bool operator >=(DateTime t1,DateTime t2)
2096 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2099 public static bool operator !=(DateTime d1, DateTime d2)
2101 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2104 public static bool operator <(DateTime t1, DateTime t2)
2106 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2109 public static bool operator <=(DateTime t1, DateTime t2)
2111 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2114 public static TimeSpan operator -(DateTime d1, DateTime d2)
2116 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2119 public static DateTime operator -(DateTime d, TimeSpan t)
2122 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2123 if (res < 0 || res > MAX_VALUE_TICKS)
2124 throw new ArgumentOutOfRangeException ();
2125 return new DateTime (res, d.Kind);
2126 } catch (OverflowException){
2127 throw new ArgumentOutOfRangeException ();
2131 bool IConvertible.ToBoolean (IFormatProvider provider)
2133 throw new InvalidCastException();
2136 byte IConvertible.ToByte (IFormatProvider provider)
2138 throw new InvalidCastException();
2142 char IConvertible.ToChar (IFormatProvider provider)
2144 throw new InvalidCastException();
2147 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2152 decimal IConvertible.ToDecimal (IFormatProvider provider)
2154 throw new InvalidCastException();
2157 double IConvertible.ToDouble (IFormatProvider provider)
2159 throw new InvalidCastException();
2162 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2164 throw new InvalidCastException();
2167 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2169 throw new InvalidCastException();
2172 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2174 throw new InvalidCastException();
2177 SByte IConvertible.ToSByte (IFormatProvider provider)
2179 throw new InvalidCastException();
2182 Single IConvertible.ToSingle (IFormatProvider provider)
2184 throw new InvalidCastException();
2187 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2189 if (targetType == null)
2190 throw new ArgumentNullException ("targetType");
2192 if (targetType == typeof (DateTime))
2194 else if (targetType == typeof (String))
2195 return this.ToString (provider);
2196 else if (targetType == typeof (Object))
2199 throw new InvalidCastException();
2202 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2204 throw new InvalidCastException();
2207 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2209 throw new InvalidCastException();
2212 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2214 throw new InvalidCastException();
2217 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2220 info.AddValue ("ticks", t);
2222 // This is the new .NET format, encodes the kind on the top bits
2223 info.AddValue ("dateData", (UInt64)encoded);
2227 static DateTime () {
2228 if (MonoTouchAOTHelper.FalseFlag) {
2229 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2230 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();