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 ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1401 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1403 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1404 longYear = (ly > 9999);
1409 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1410 if (num_parsed <= 2)
1411 year += (year < 30) ? 2000 : 1900;
1417 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1419 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1428 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1431 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1433 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1438 format_with_24_hours = true;
1444 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1446 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1456 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1458 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1465 leading_zeros = false;
1468 if (num > 6 || fractionalSeconds != -1)
1470 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1471 if (num_parsed == -1)
1473 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1476 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1483 if (s [valuePos] == '+')
1485 else if (s [valuePos] == '-')
1492 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1494 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1496 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1497 valuePos += num_parsed;
1502 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1503 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1504 valuePos += num_parsed;
1505 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1509 else if (!flexibleTwoPartsParsing)
1516 if (s [valuePos] == 'Z') {
1520 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1523 if (s [valuePos] == '+')
1525 else if (s [valuePos] == '-')
1530 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1531 valuePos += num_parsed;
1535 if (Char.IsDigit (s [valuePos]))
1537 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1539 valuePos += num_parsed;
1541 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1548 // LAMESPEC: This should be part of UTCpattern
1549 // string and thus should not be considered here.
1551 // Note that 'Z' is not defined as a pattern
1552 // character. Keep it for X509 certificate
1553 // verification. Also, "Z" != "'Z'" under MS.NET
1554 // ("'Z'" is just literal; handled above)
1556 if (s [valuePos] != 'Z')
1563 if (s [valuePos] != 'G')
1566 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1567 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1568 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1580 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1584 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1590 if (s[valuePos] == '.') {
1596 // '.FFF....' can be mapped to nothing
1597 if (pos + 1 < len && chars[pos + 1] == 'F') {
1599 while (pos + 1 < len && chars[pos + 1] == 'F') {
1611 if (s [valuePos] != chars [pos])
1622 valuePos += num_parsed;
1624 if (!exact && !flexibleTwoPartsParsing) {
1625 switch (chars [pos]) {
1631 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1632 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1640 pos = pos + num + 1;
1644 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1646 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1649 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1655 if (s.Length > valuePos) // extraneous tail.
1660 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1662 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1664 incompleteFormat = true;
1675 if (fractionalSeconds == -1)
1676 fractionalSeconds = 0;
1678 // If no date was given
1679 if ((day == -1) && (month == -1) && (year == -1)) {
1680 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1685 day = DateTime.Today.Day;
1686 month = DateTime.Today.Month;
1687 year = DateTime.Today.Year;
1696 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1699 year = DateTime.Today.Year;
1702 if (ampm == 0) { // AM designator
1703 if (hour >= 12 && format_with_24_hours && exact)
1708 } else if (ampm == 1) { // PM designator
1710 if (format_with_24_hours && exact)
1718 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1723 result = result.AddSeconds(fractionalSeconds);
1725 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1729 if (result != DateTime.MinValue) {
1731 dto = new DateTimeOffset (result);
1732 } catch { } // We handle this error in DateTimeOffset.Parse
1740 tzoffset = -tzoffset;
1741 tzoffmin = -tzoffmin;
1744 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1745 } catch {} // We handle this error in DateTimeOffset.Parse
1747 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1750 long newticks = (result - dto.Offset).Ticks;
1752 newticks += TimeSpan.TicksPerDay;
1753 result = new DateTime (newticks, DateTimeKind.Utc);
1754 if ((style & DateTimeStyles.RoundtripKind) != 0)
1755 result = result.ToLocalTime ();
1756 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1757 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1758 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1759 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1761 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1762 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1763 if (adjustToUniversal)
1764 result = result.ToUniversalTime ();
1765 else if (adjustToLocal)
1766 result = result.ToLocalTime ();
1772 public static DateTime ParseExact (string s, string format,
1773 IFormatProvider provider, DateTimeStyles style)
1776 throw new ArgumentNullException ("format");
1778 string [] formats = new string [1];
1779 formats[0] = format;
1781 return ParseExact (s, formats, provider, style);
1784 public static DateTime ParseExact (string s, string[] formats,
1785 IFormatProvider provider,
1786 DateTimeStyles style)
1788 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1791 throw new ArgumentNullException ("s");
1792 if (formats == null)
1793 throw new ArgumentNullException ("formats");
1794 if (formats.Length == 0)
1795 throw new FormatException ("Format specifier was invalid.");
1799 bool longYear = false;
1801 if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1806 private static void CheckStyle (DateTimeStyles style)
1808 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1810 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1811 (style & DateTimeStyles.AssumeUniversal) != 0)
1812 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1814 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1815 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1818 public static bool TryParse (string s, out DateTime result)
1822 Exception exception = null;
1825 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1832 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1836 Exception exception = null;
1839 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1846 public static bool TryParseExact (string s, string format,
1847 IFormatProvider provider,
1848 DateTimeStyles style,
1849 out DateTime result)
1852 formats = new string [1];
1853 formats[0] = format;
1855 return TryParseExact (s, formats, provider, style, out result);
1858 public static bool TryParseExact (string s, string[] formats,
1859 IFormatProvider provider,
1860 DateTimeStyles style,
1861 out DateTime result)
1864 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1867 bool longYear = false;
1869 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1876 internal static bool CoreParseExact (string s, string [] formats,
1877 DateTimeFormatInfo dfi, DateTimeStyles style,
1878 out DateTime ret, out DateTimeOffset dto,
1879 bool exact, ref bool longYear,
1880 bool setExceptionOnError, ref Exception exception,
1881 bool dateTimeOffset = false)
1884 bool incompleteFormat = false;
1885 for (i = 0; i < formats.Length; i++)
1888 string format = formats[i];
1889 if (format == null || format == String.Empty)
1892 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1898 if (setExceptionOnError)
1899 exception = new FormatException ("Invalid format string");
1900 ret = DateTime.MinValue;
1904 public TimeSpan Subtract (DateTime value)
1906 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1909 public DateTime Subtract(TimeSpan value)
1913 newticks = Ticks - value.Ticks;
1914 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1915 throw new ArgumentOutOfRangeException ();
1916 DateTime ret = new DateTime (newticks);
1917 ret.encoded |= (encoded & KindMask);
1921 public long ToFileTime()
1923 DateTime universalTime = ToUniversalTime();
1925 if (universalTime.Ticks < w32file_epoch) {
1926 throw new ArgumentOutOfRangeException("file time is not valid");
1929 return(universalTime.Ticks - w32file_epoch);
1932 public long ToFileTimeUtc()
1934 if (Kind == DateTimeKind.Local)
1935 return ToFileTime ();
1937 if (Ticks < w32file_epoch) {
1938 throw new ArgumentOutOfRangeException("file time is not valid");
1941 return (Ticks - w32file_epoch);
1944 public string ToLongDateString()
1946 return ToString ("D");
1949 public string ToLongTimeString()
1951 return ToString ("T");
1954 public double ToOADate ()
1956 long t = this.Ticks;
1957 // uninitialized DateTime case
1960 // we can't reach minimum value
1961 if (t < 31242239136000000)
1962 return OAMinValue + 0.001;
1964 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1965 double result = ts.TotalDays;
1966 // t < 0 (where 599264352000000000 == 0.0d for OA)
1967 if (t < 599264352000000000) {
1968 // negative days (int) but decimals are positive
1969 double d = Math.Ceiling (result);
1970 result = d - 2 - (result - d);
1973 // we can't reach maximum value
1974 if (result >= OAMaxValue)
1975 result = OAMaxValue - 0.00000001d;
1980 public string ToShortDateString()
1982 return ToString ("d");
1985 public string ToShortTimeString()
1987 return ToString ("t");
1990 public override string ToString ()
1992 return ToString ("G", null);
1995 public string ToString (IFormatProvider provider)
1997 return ToString (null, provider);
2000 public string ToString (string format)
2002 return ToString (format, null);
2005 public string ToString (string format, IFormatProvider provider)
2007 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2009 if (format == null || format == String.Empty)
2012 if (format.Length == 1) {
2013 char fchar = format [0];
2014 bool use_invariant, useutc;
2015 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2017 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2018 // return ToUniversalTime()._ToString (format, dfi);
2021 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2024 dfi = DateTimeFormatInfo.InvariantInfo;
2027 // Don't convert UTC value. It just adds 'Z' for
2028 // 'u' format, for the same ticks.
2029 return DateTimeUtils.ToString (this, format, dfi);
2032 public DateTime ToLocalTime ()
2034 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2037 public DateTime ToUniversalTime()
2039 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2044 public static DateTime operator +(DateTime d, TimeSpan t)
2047 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2048 if (res < 0 || res > MAX_VALUE_TICKS){
2049 throw new ArgumentOutOfRangeException ();
2052 return new DateTime (res, d.Kind);
2053 } catch (OverflowException){
2054 throw new ArgumentOutOfRangeException ();
2058 public static bool operator ==(DateTime d1, DateTime d2)
2060 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2063 public static bool operator >(DateTime t1,DateTime t2)
2065 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2068 public static bool operator >=(DateTime t1,DateTime t2)
2070 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2073 public static bool operator !=(DateTime d1, DateTime d2)
2075 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2078 public static bool operator <(DateTime t1, DateTime t2)
2080 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2083 public static bool operator <=(DateTime t1, DateTime t2)
2085 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2088 public static TimeSpan operator -(DateTime d1, DateTime d2)
2090 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2093 public static DateTime operator -(DateTime d, TimeSpan t)
2096 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2097 if (res < 0 || res > MAX_VALUE_TICKS)
2098 throw new ArgumentOutOfRangeException ();
2099 return new DateTime (res, d.Kind);
2100 } catch (OverflowException){
2101 throw new ArgumentOutOfRangeException ();
2105 bool IConvertible.ToBoolean (IFormatProvider provider)
2107 throw new InvalidCastException();
2110 byte IConvertible.ToByte (IFormatProvider provider)
2112 throw new InvalidCastException();
2116 char IConvertible.ToChar (IFormatProvider provider)
2118 throw new InvalidCastException();
2121 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2126 decimal IConvertible.ToDecimal (IFormatProvider provider)
2128 throw new InvalidCastException();
2131 double IConvertible.ToDouble (IFormatProvider provider)
2133 throw new InvalidCastException();
2136 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2138 throw new InvalidCastException();
2141 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2143 throw new InvalidCastException();
2146 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2148 throw new InvalidCastException();
2151 SByte IConvertible.ToSByte (IFormatProvider provider)
2153 throw new InvalidCastException();
2156 Single IConvertible.ToSingle (IFormatProvider provider)
2158 throw new InvalidCastException();
2161 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2163 if (targetType == null)
2164 throw new ArgumentNullException ("targetType");
2166 if (targetType == typeof (DateTime))
2168 else if (targetType == typeof (String))
2169 return this.ToString (provider);
2170 else if (targetType == typeof (Object))
2173 throw new InvalidCastException();
2176 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2178 throw new InvalidCastException();
2181 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2183 throw new InvalidCastException();
2186 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2188 throw new InvalidCastException();
2191 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2194 info.AddValue ("ticks", t);
2196 // This is the new .NET format, encodes the kind on the top bits
2197 info.AddValue ("dateData", (UInt64)encoded);
2201 static DateTime () {
2202 if (MonoTouchAOTHelper.FalseFlag) {
2203 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2204 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();