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))
893 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
894 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
895 if (dayIndex == -1 || monthIndex == -1){
897 if (setExceptionOnError)
898 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
901 bool is_day_before_month = dayIndex < monthIndex;
902 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
903 for (int i = 0; i < monthDayFormats.Length; i++) {
904 bool incompleteFormat = false;
905 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
909 for (int j = 0; j < ParseTimeFormats.Length; j++) {
910 string firstPart = ParseTimeFormats [j];
911 bool incompleteFormat = false;
912 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
914 if (!incompleteFormat)
917 for (int i = 0; i < monthDayFormats.Length; i++) {
918 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
921 for (int i = 0; i < allDateFormats.Length; i++) {
922 string dateFormat = allDateFormats [i];
923 if (dateFormat[dateFormat.Length - 1] == 'T')
924 continue; // T formats must be before the time part
925 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
930 // Try as a last resort all the patterns
931 if (CoreParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
934 if (CoreParseExact (s, ExoticAndNonStandardFormats, dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
937 if (!setExceptionOnError)
940 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
941 exception = new FormatException (formatExceptionMessage);
945 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
947 return ParseExact (s, format, provider, DateTimeStyles.None);
950 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
952 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
953 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
954 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
955 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
956 return ParseGenericYearMonthDayFormats;
958 if (yearIndex < monthIndex)
959 if (monthIndex < dayIndex)
960 return ParseYearMonthDayFormats;
961 else if (yearIndex < dayIndex)
962 return ParseYearDayMonthFormats;
964 // The year cannot be between the date and the month
965 return ParseGenericYearMonthDayFormats;
967 else if (dayIndex < monthIndex)
968 return ParseDayMonthYearFormats;
969 else if (dayIndex < yearIndex)
970 return ParseMonthDayYearFormats;
972 // The year cannot be between the month and the date
973 return ParseGenericYearMonthDayFormats;
977 private static int _ParseNumber (string s, int valuePos,
991 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
992 if (!Char.IsDigit (s[i]))
998 digits = real_digits;
1000 if (digits < min_digits) {
1005 if (s.Length - valuePos < digits) {
1010 for (i = valuePos; i < digits + valuePos; i++) {
1012 if (!Char.IsDigit (c)) {
1017 number = number * 10 + (byte) (c - '0');
1020 num_parsed = digits;
1024 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1026 // FIXME: I know this is somehow lame code. Probably
1027 // it should iterate all the enum value and return
1028 // the longest match. However right now I don't see
1029 // anything but "1" and "10" - "12" that might match
1030 // two or more values. (They are only abbrev month
1031 // names, so do reverse order search). See bug #80094.
1032 for (int i = values.Length - 1; i >= 0; i--) {
1033 if (!exact && invValues [i].Length > values[i].Length) {
1034 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1036 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1040 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1042 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1051 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1054 maxlength = value.Length;
1056 if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
1057 num_parsed = maxlength;
1065 // Note that in case of Parse (exact == false) we check both for AM/PM
1066 // and the culture spcific AM/PM strings.
1067 private static bool _ParseAmPm(string s,
1070 DateTimeFormatInfo dfi,
1079 if (!IsLetter (s, valuePos)) {
1080 if (dfi.AMDesignator != "")
1087 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1088 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1089 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1091 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1092 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1093 if (exact || num_parsed != 0)
1101 // Note that in case of Parse (exact == false) we check both for ':'
1102 // and the culture spcific TimeSperator
1103 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1105 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1106 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1109 // Accept any character for DateSeparator, except TimeSeparator,
1110 // a digit or a letter.
1111 // Not documented, but seems to be MS behaviour here. See bug 54047.
1112 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1115 if (exact && s [sPos] != '/')
1118 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1119 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1126 private static bool IsLetter (string s, int pos)
1128 return pos < s.Length && Char.IsLetter (s [pos]);
1131 // To implement better DateTime.Parse we use two format strings one
1132 // for Date and one for Time. This allows us to define two different
1133 // arrays of formats for Time and Dates and to combine them more or less
1134 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1135 private static bool _DoParse (string s,
1139 out DateTime result,
1140 out DateTimeOffset dto,
1141 DateTimeFormatInfo dfi,
1142 DateTimeStyles style,
1143 bool firstPartIsDate,
1144 ref bool incompleteFormat,
1146 bool dateTimeOffset = false)
1148 bool useutc = false;
1149 bool use_invariant = false;
1150 bool sloppy_parsing = false;
1151 dto = new DateTimeOffset (0, TimeSpan.Zero);
1152 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1153 incompleteFormat = false;
1155 string format = firstPart;
1156 bool afterTFormat = false;
1157 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1158 if (format.Length == 1)
1159 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant, dateTimeOffset);
1161 result = new DateTime (0);
1168 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1169 format = format.TrimStart (null);
1171 s = s.TrimStart (null); // it could be optimized, but will make little good.
1174 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1175 format = format.TrimEnd (null);
1176 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1182 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1183 sloppy_parsing = true;
1185 string chars = format;
1186 int len = format.Length, pos = 0, num = 0;
1190 int day = -1, dayofweek = -1, month = -1, year = -1;
1191 int hour = -1, minute = -1, second = -1;
1192 double fractionalSeconds = -1;
1194 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1195 bool isFirstPart = true;
1196 bool format_with_24_hours = false;
1200 if (valuePos == s.Length)
1204 if (flexibleTwoPartsParsing && pos + num == 0)
1206 bool isLetter = IsLetter(s, valuePos);
1208 if (s [valuePos] == 'Z')
1211 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1212 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1213 valuePos += num_parsed;
1218 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1219 if (IsLetter (s, valuePos + num_parsed))
1221 else if (num_parsed > 0) {
1222 valuePos += num_parsed;
1227 if (!afterTFormat && dayofweek == -1 && isLetter) {
1228 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1229 if (dayofweek == -1)
1230 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1231 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1232 valuePos += num_parsed;
1239 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1246 if (pos + num >= len)
1248 if (flexibleTwoPartsParsing && num == 0) {
1249 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1250 if (!isFirstPart && format == "")
1255 format = secondPart;
1260 isFirstPart = false;
1266 bool leading_zeros = true;
1268 if (chars[pos] == '\'') {
1270 while (pos+num < len) {
1271 if (chars[pos+num] == '\'')
1274 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1284 } else if (chars[pos] == '"') {
1286 while (pos+num < len) {
1287 if (chars[pos+num] == '"')
1290 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1300 } else if (chars[pos] == '\\') {
1305 if (s [valuePos] != chars [pos])
1311 } else if (chars[pos] == '%') {
1314 } else if (char.IsWhiteSpace (s [valuePos]) ||
1315 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1318 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1319 if (!Char.IsWhiteSpace (chars[pos]))
1326 while (ws < s.Length) {
1327 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1334 while (ws < chars.Length) {
1335 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1341 // A whitespace may match a '/' in the pattern.
1342 if (!exact && pos < chars.Length && chars[pos] == '/')
1343 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1348 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1356 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1359 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1361 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1363 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1365 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1371 if (flexibleTwoPartsParsing) {
1373 if (num == 0 || num == 3)
1374 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1375 if (num > 1 && num_parsed == -1)
1376 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1377 if (num > 1 && num_parsed == -1)
1378 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1383 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1385 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1387 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1389 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1396 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1397 } else if (num < 3) {
1398 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1400 year = _ParseNumber (s, valuePos, exact ? num + 1 : 3, num + 1, false, sloppy_parsing, out num_parsed);
1401 longYear = (year > 9999);
1404 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1405 if (num_parsed <= 2)
1406 year += (year < 30) ? 2000 : 1900;
1412 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1414 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1423 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1426 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1428 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1433 format_with_24_hours = true;
1439 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1441 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1451 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1453 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1460 leading_zeros = false;
1463 if (num > 6 || fractionalSeconds != -1)
1465 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1466 if (num_parsed == -1)
1468 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1471 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1478 if (s [valuePos] == '+')
1480 else if (s [valuePos] == '-')
1487 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1489 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1491 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1492 valuePos += num_parsed;
1497 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1498 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1499 valuePos += num_parsed;
1500 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1504 else if (!flexibleTwoPartsParsing)
1511 if (s [valuePos] == 'Z') {
1515 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1518 if (s [valuePos] == '+')
1520 else if (s [valuePos] == '-')
1525 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1526 valuePos += num_parsed;
1530 if (Char.IsDigit (s [valuePos]))
1532 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1534 valuePos += num_parsed;
1536 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1542 // LAMESPEC: This should be part of UTCpattern
1543 // string and thus should not be considered here.
1545 // Note that 'Z' is not defined as a pattern
1546 // character. Keep it for X509 certificate
1547 // verification. Also, "Z" != "'Z'" under MS.NET
1548 // ("'Z'" is just literal; handled above)
1550 if (s [valuePos] != 'Z')
1557 if (s [valuePos] != 'G')
1560 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1561 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1562 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1574 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1578 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1584 if (s[valuePos] == '.') {
1590 // '.FFF....' can be mapped to nothing
1591 if (pos + 1 < len && chars[pos + 1] == 'F') {
1593 while (pos + 1 < len && chars[pos + 1] == 'F') {
1605 if (s [valuePos] != chars [pos])
1616 valuePos += num_parsed;
1618 if (!exact && !flexibleTwoPartsParsing) {
1619 switch (chars [pos]) {
1625 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1626 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1634 pos = pos + num + 1;
1638 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1640 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1643 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1649 if (s.Length > valuePos) // extraneous tail.
1654 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1656 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1658 incompleteFormat = true;
1669 if (fractionalSeconds == -1)
1670 fractionalSeconds = 0;
1672 // If no date was given
1673 if ((day == -1) && (month == -1) && (year == -1)) {
1674 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1679 day = DateTime.Today.Day;
1680 month = DateTime.Today.Month;
1681 year = DateTime.Today.Year;
1690 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1693 year = DateTime.Today.Year;
1696 if (ampm == 0) { // AM designator
1697 if (hour >= 12 && format_with_24_hours && exact)
1702 } else if (ampm == 1) { // PM designator
1704 if (format_with_24_hours && exact)
1712 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1717 result = result.AddSeconds(fractionalSeconds);
1719 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1723 if (result != DateTime.MinValue) {
1725 if ((style & DateTimeStyles.AssumeUniversal) != 0) {
1726 dto = new DateTimeOffset (result, TimeSpan.Zero);
1727 } else if ((style & DateTimeStyles.AssumeLocal) != 0) {
1728 var offset = use_invariant ?
1730 TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
1731 dto = new DateTimeOffset (result, offset);
1733 dto = new DateTimeOffset (result);
1735 } catch { } // We handle this error in DateTimeOffset.Parse
1743 tzoffset = -tzoffset;
1744 tzoffmin = -tzoffmin;
1747 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1748 } catch {} // We handle this error in DateTimeOffset.Parse
1750 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1753 long newticks = (result - dto.Offset).Ticks;
1755 newticks += TimeSpan.TicksPerDay;
1756 result = new DateTime (newticks, DateTimeKind.Utc);
1757 if ((style & DateTimeStyles.RoundtripKind) != 0)
1758 result = result.ToLocalTime ();
1759 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1760 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1761 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1762 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1764 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1765 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1766 if (adjustToUniversal)
1767 result = result.ToUniversalTime ();
1768 else if (adjustToLocal)
1769 result = result.ToLocalTime ();
1775 public static DateTime ParseExact (string s, string format,
1776 IFormatProvider provider, DateTimeStyles style)
1779 throw new ArgumentNullException ("format");
1781 string [] formats = new string [1];
1782 formats[0] = format;
1784 return ParseExact (s, formats, provider, style);
1787 public static DateTime ParseExact (string s, string[] formats,
1788 IFormatProvider provider,
1789 DateTimeStyles style)
1791 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1794 throw new ArgumentNullException ("s");
1795 if (formats == null)
1796 throw new ArgumentNullException ("formats");
1797 if (formats.Length == 0)
1798 throw new FormatException ("Format specifier was invalid.");
1802 bool longYear = false;
1804 if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1809 private static void CheckStyle (DateTimeStyles style)
1811 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1813 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1814 (style & DateTimeStyles.AssumeUniversal) != 0)
1815 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1817 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1818 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1821 public static bool TryParse (string s, out DateTime result)
1825 Exception exception = null;
1828 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1835 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1839 Exception exception = null;
1842 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1849 public static bool TryParseExact (string s, string format,
1850 IFormatProvider provider,
1851 DateTimeStyles style,
1852 out DateTime result)
1855 formats = new string [1];
1856 formats[0] = format;
1858 return TryParseExact (s, formats, provider, style, out result);
1861 public static bool TryParseExact (string s, string[] formats,
1862 IFormatProvider provider,
1863 DateTimeStyles style,
1864 out DateTime result)
1867 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1870 bool longYear = false;
1872 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1879 internal static bool CoreParseExact (string s, string [] formats,
1880 DateTimeFormatInfo dfi, DateTimeStyles style,
1881 out DateTime ret, out DateTimeOffset dto,
1882 bool exact, ref bool longYear,
1883 bool setExceptionOnError, ref Exception exception,
1884 bool dateTimeOffset = false)
1886 dto = new DateTimeOffset (0, TimeSpan.Zero);
1888 bool incompleteFormat = false;
1889 for (i = 0; i < formats.Length; i++)
1892 string format = formats[i];
1893 if (format == null || format == String.Empty)
1896 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1902 if (setExceptionOnError)
1903 exception = new FormatException ("Invalid format string");
1904 ret = DateTime.MinValue;
1908 public TimeSpan Subtract (DateTime value)
1910 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1913 public DateTime Subtract(TimeSpan value)
1917 newticks = Ticks - value.Ticks;
1918 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1919 throw new ArgumentOutOfRangeException ();
1920 DateTime ret = new DateTime (newticks);
1921 ret.encoded |= (encoded & KindMask);
1925 public long ToFileTime()
1927 DateTime universalTime = ToUniversalTime();
1929 if (universalTime.Ticks < w32file_epoch) {
1930 throw new ArgumentOutOfRangeException("file time is not valid");
1933 return(universalTime.Ticks - w32file_epoch);
1936 public long ToFileTimeUtc()
1938 if (Kind == DateTimeKind.Local)
1939 return ToFileTime ();
1941 if (Ticks < w32file_epoch) {
1942 throw new ArgumentOutOfRangeException("file time is not valid");
1945 return (Ticks - w32file_epoch);
1948 public string ToLongDateString()
1950 return ToString ("D");
1953 public string ToLongTimeString()
1955 return ToString ("T");
1958 public double ToOADate ()
1960 long t = this.Ticks;
1961 // uninitialized DateTime case
1964 // we can't reach minimum value
1965 if (t < 31242239136000000)
1966 return OAMinValue + 0.001;
1968 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1969 double result = ts.TotalDays;
1970 // t < 0 (where 599264352000000000 == 0.0d for OA)
1971 if (t < 599264352000000000) {
1972 // negative days (int) but decimals are positive
1973 double d = Math.Ceiling (result);
1974 result = d - 2 - (result - d);
1977 // we can't reach maximum value
1978 if (result >= OAMaxValue)
1979 result = OAMaxValue - 0.00000001d;
1984 public string ToShortDateString()
1986 return ToString ("d");
1989 public string ToShortTimeString()
1991 return ToString ("t");
1994 public override string ToString ()
1996 return ToString ("G", null);
1999 public string ToString (IFormatProvider provider)
2001 return ToString (null, provider);
2004 public string ToString (string format)
2006 return ToString (format, null);
2009 public string ToString (string format, IFormatProvider provider)
2011 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2013 if (format == null || format == String.Empty)
2016 if (format.Length == 1) {
2017 char fchar = format [0];
2018 bool use_invariant, useutc;
2019 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2021 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2022 // return ToUniversalTime()._ToString (format, dfi);
2025 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2028 dfi = DateTimeFormatInfo.InvariantInfo;
2031 // Don't convert UTC value. It just adds 'Z' for
2032 // 'u' format, for the same ticks.
2033 return DateTimeUtils.ToString (this, format, dfi);
2036 public DateTime ToLocalTime ()
2038 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2041 public DateTime ToUniversalTime()
2043 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2048 public static DateTime operator +(DateTime d, TimeSpan t)
2051 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2052 if (res < 0 || res > MAX_VALUE_TICKS){
2053 throw new ArgumentOutOfRangeException ();
2056 return new DateTime (res, d.Kind);
2057 } catch (OverflowException){
2058 throw new ArgumentOutOfRangeException ();
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 bool operator !=(DateTime d1, DateTime d2)
2079 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2082 public static bool operator <(DateTime t1, DateTime t2)
2084 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2087 public static bool operator <=(DateTime t1, DateTime t2)
2089 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2092 public static TimeSpan operator -(DateTime d1, DateTime d2)
2094 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2097 public static DateTime operator -(DateTime d, TimeSpan t)
2100 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2101 if (res < 0 || res > MAX_VALUE_TICKS)
2102 throw new ArgumentOutOfRangeException ();
2103 return new DateTime (res, d.Kind);
2104 } catch (OverflowException){
2105 throw new ArgumentOutOfRangeException ();
2109 bool IConvertible.ToBoolean (IFormatProvider provider)
2111 throw new InvalidCastException();
2114 byte IConvertible.ToByte (IFormatProvider provider)
2116 throw new InvalidCastException();
2120 char IConvertible.ToChar (IFormatProvider provider)
2122 throw new InvalidCastException();
2125 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2130 decimal IConvertible.ToDecimal (IFormatProvider provider)
2132 throw new InvalidCastException();
2135 double IConvertible.ToDouble (IFormatProvider provider)
2137 throw new InvalidCastException();
2140 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2142 throw new InvalidCastException();
2145 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2147 throw new InvalidCastException();
2150 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2152 throw new InvalidCastException();
2155 SByte IConvertible.ToSByte (IFormatProvider provider)
2157 throw new InvalidCastException();
2160 Single IConvertible.ToSingle (IFormatProvider provider)
2162 throw new InvalidCastException();
2165 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2167 if (targetType == null)
2168 throw new ArgumentNullException ("targetType");
2170 if (targetType == typeof (DateTime))
2172 else if (targetType == typeof (String))
2173 return this.ToString (provider);
2174 else if (targetType == typeof (Object))
2177 throw new InvalidCastException();
2180 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2182 throw new InvalidCastException();
2185 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2187 throw new InvalidCastException();
2190 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2192 throw new InvalidCastException();
2195 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2198 info.AddValue ("ticks", t);
2200 // This is the new .NET format, encodes the kind on the top bits
2201 info.AddValue ("dateData", (UInt64)encoded);
2205 static DateTime () {
2206 if (MonoTouchAOTHelper.FalseFlag) {
2207 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2208 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();