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'",
114 // DateTime.Parse date patterns extend ParseExact patterns as follows:
115 // MMM - month short name or month full name
116 // MMMM - month number or short name or month full name
118 // Parse behaves differently according to the ShorDatePattern of the
119 // DateTimeFormatInfo. The following define the date patterns for
120 // different orders of day, month and year in ShorDatePattern.
121 // Note that the year cannot go between the day and the month.
122 private static readonly string[] ParseYearDayMonthFormats = new string [] {
125 "yyyy'\u5E74'M'\u6708'd'\u65E5",
138 private static readonly string[] ParseYearMonthDayFormats = new string [] {
141 "yyyy'\u5E74'M'\u6708'd'\u65E5",
155 private static readonly string[] ParseDayMonthYearFormats = new string [] {
158 "yyyy'\u5E74'M'\u6708'd'\u65E5",
175 private static readonly string[] ParseMonthDayYearFormats = new string [] {
178 "yyyy'\u5E74'M'\u6708'd'\u65E5",
195 private static readonly string[] ParseGenericYearMonthDayFormats = new string [] {
200 "yyyy'\u5E74'M'\u6708'd'\u65E5",
205 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
206 // Note that these patterns cannot be followed by the time.
207 private static readonly string[] MonthDayShortFormats = new string [] {
212 private static readonly string[] DayMonthShortFormats = new string [] {
218 private static readonly string[] ExoticAndNonStandardFormats = new string[] {
230 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
231 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
233 private static int AbsoluteDays (int year, int month, int day)
238 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
242 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
245 private int FromTicks(Which what)
247 int num400, num100, num4, numyears;
250 int[] days = daysmonth;
251 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
253 num400 = (totaldays / dp400);
254 totaldays -= num400 * dp400;
256 num100 = (totaldays / dp100);
257 if (num100 == 4) // leap
259 totaldays -= (num100 * dp100);
261 num4 = totaldays / dp4;
262 totaldays -= (num4 * dp4);
264 numyears = totaldays / 365 ;
266 if (numyears == 4) //leap
268 if (what == Which.Year )
269 return num400*400 + num100*100 + num4*4 + numyears + 1;
271 totaldays -= (numyears * 365) ;
272 if (what == Which.DayYear )
273 return totaldays + 1;
275 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
276 days = daysmonthleap;
278 while (totaldays >= days[M])
279 totaldays -= days[M++];
281 if (what == Which.Month )
287 static void InvalidTickValue (long ticks)
289 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
290 throw new ArgumentOutOfRangeException ("ticks", msg);
296 /// Constructs a DateTime for specified ticks
299 public DateTime (long ticks)
301 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
302 InvalidTickValue (ticks);
306 public DateTime (int year, int month, int day)
307 : this (year, month, day,0,0,0,0) {}
309 public DateTime (int year, int month, int day, int hour, int minute, int second)
310 : this (year, month, day, hour, minute, second, 0) {}
312 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
314 if (year < 1 || year > 9999 ||
315 month < 1 || month >12 ||
316 day < 1 || day > DaysInMonth(year, month) ||
317 hour < 0 || hour > 23 ||
318 minute < 0 || minute > 59 ||
319 second < 0 || second > 59 ||
320 millisecond < 0 || millisecond > 999)
321 throw new ArgumentOutOfRangeException ("Parameters describe an " +
322 "unrepresentable DateTime.");
324 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
327 public DateTime (int year, int month, int day, Calendar calendar)
328 : this (year, month, day, 0, 0, 0, 0, calendar)
332 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
333 : this (year, month, day, hour, minute, second, 0, calendar)
337 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
339 if (calendar == null)
340 throw new ArgumentNullException ("calendar");
341 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
344 public DateTime (long ticks, DateTimeKind kind)
346 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
347 InvalidTickValue (ticks);
348 if (kind < 0 || kind > DateTimeKind.Local)
349 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
351 encoded = ((long)kind << KindShift) | ticks;
354 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
355 : this (year, month, day, hour, minute, second)
357 if (kind < 0 || kind > DateTimeKind.Local)
358 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
359 encoded |= ((long)kind << KindShift);
362 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
363 : this (year, month, day, hour, minute, second, millisecond)
365 if (kind < 0 || kind > DateTimeKind.Local)
366 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
367 encoded |= ((long)kind << KindShift);
370 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
371 : this (year, month, day, hour, minute, second, millisecond, calendar)
373 if (kind < 0 || kind > DateTimeKind.Local)
374 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
375 encoded |= ((long)kind << KindShift);
379 // Not visible, but can be invoked during deserialization
381 DateTime (SerializationInfo info, StreamingContext context)
383 if (info.HasKey ("dateData")){
384 encoded = (Int64)info.GetUInt64 ("dateData");
385 } else if (info.HasKey ("ticks")){
386 encoded = info.GetInt64 ("ticks") & TicksMask;
395 public DateTime Date {
397 DateTime ret = new DateTime (Year, Month, Day);
398 ret.encoded |= encoded & KindMask;
405 return FromTicks (Which.Month);
411 return FromTicks (Which.Day);
415 public DayOfWeek DayOfWeek {
417 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
421 public int DayOfYear {
423 return FromTicks (Which.DayYear);
427 public TimeSpan TimeOfDay {
429 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
436 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
442 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
448 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
452 public int Millisecond {
454 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
458 [MethodImplAttribute(MethodImplOptions.InternalCall)]
459 internal static extern long GetTimeMonotonic ();
461 [MethodImplAttribute(MethodImplOptions.InternalCall)]
462 internal static extern long GetNow ();
465 // To reduce the time consumed by DateTime.Now, we keep
466 // the difference to map the system time into a local
467 // time into `to_local_time_span', we record the timestamp
468 // for this in `last_now'
470 static object to_local_time_span_object;
471 static long last_now;
473 public static DateTime Now {
475 long now = GetNow ();
476 DateTime dt = new DateTime (now);
478 if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
479 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
484 // This is boxed, so we avoid locking.
485 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
486 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
493 return encoded & TicksMask;
497 public static DateTime Today {
500 DateTime today = new DateTime (now.Year, now.Month, now.Day);
501 today.encoded |= ((long)DateTimeKind.Local << KindShift);
506 public static DateTime UtcNow {
508 return new DateTime (GetNow (), DateTimeKind.Utc);
514 return FromTicks (Which.Year);
518 public DateTimeKind Kind {
520 return (DateTimeKind) ((ulong)encoded >> KindShift);
526 public DateTime Add (TimeSpan value)
528 DateTime ret = AddTicks (value.Ticks);
532 public DateTime AddDays (double value)
534 return AddMilliseconds (Math.Round (value * 86400000));
537 public DateTime AddTicks (long value)
539 long res = value + (encoded & TicksMask);
540 if (res < 0 || res > MAX_VALUE_TICKS)
541 throw new ArgumentOutOfRangeException();
543 DateTime ret = new DateTime (res);
544 ret.encoded |= (encoded & KindMask);
548 public DateTime AddHours (double value)
550 return AddMilliseconds (value * 3600000);
553 public DateTime AddMilliseconds (double value)
555 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
556 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
557 throw new ArgumentOutOfRangeException();
559 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
561 return AddTicks (msticks);
564 // required to match MS implementation for OADate (OLE Automation)
565 private DateTime AddRoundedMilliseconds (double ms)
567 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
568 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
569 throw new ArgumentOutOfRangeException ();
571 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
573 return AddTicks (msticks);
576 public DateTime AddMinutes (double value)
578 return AddMilliseconds (value * 60000);
581 public DateTime AddMonths (int months)
583 int day, month, year, maxday ;
587 month = this.Month + (months % 12);
588 year = this.Year + months/12 ;
600 maxday = DaysInMonth(year, month);
604 temp = new DateTime (year, month, day);
605 temp.encoded |= encoded & KindMask;
606 return temp.Add (this.TimeOfDay);
609 public DateTime AddSeconds (double value)
611 return AddMilliseconds (value * 1000);
614 public DateTime AddYears (int value)
616 return AddMonths (value * 12);
619 public static int Compare (DateTime t1, DateTime t2)
621 long t1t = t1.encoded & TicksMask;
622 long t2t = t2.encoded & TicksMask;
632 public int CompareTo (object value)
637 if (!(value is System.DateTime))
638 throw new ArgumentException (Locale.GetText (
639 "Value is not a System.DateTime"));
641 return Compare (this, (DateTime) value);
644 public bool IsDaylightSavingTime ()
646 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
648 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
651 public int CompareTo (DateTime value)
653 return Compare (this, value);
656 public bool Equals (DateTime value)
658 return (value.encoded & TicksMask) == (encoded & TicksMask);
661 public long ToBinary ()
663 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
664 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
669 public static DateTime FromBinary (long dateData)
671 switch ((ulong)dateData >> KindShift) {
673 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
674 case 0: // Unspecified
675 return new DateTime (dateData, DateTimeKind.Unspecified);
677 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
681 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
683 return new DateTime (value.Ticks, kind);
686 public static int DaysInMonth (int year, int month)
690 if (month < 1 || month >12)
691 throw new ArgumentOutOfRangeException ();
693 if (year < 1 || year > 9999)
694 throw new ArgumentOutOfRangeException ();
696 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
700 public override bool Equals (object value)
702 if (!(value is System.DateTime))
705 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
708 public static bool Equals (DateTime t1, DateTime t2 )
710 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
713 public static DateTime FromFileTime (long fileTime)
716 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
718 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
721 public static DateTime FromFileTimeUtc (long fileTime)
724 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
726 return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
729 public static DateTime FromOADate (double d)
731 // An OLE Automation date is implemented as a floating-point number
732 // whose value is the number of days from midnight, 30 December 1899.
734 // d must be negative 657435.0 through positive 2958466.0.
735 if ((d <= OAMinValue) || (d >= OAMaxValue))
736 throw new ArgumentException ("d", "[-657435,2958466]");
738 DateTime dt = new DateTime (ticks18991230);
740 Double days = Math.Ceiling (d);
741 // integer part is the number of days (negative)
742 dt = dt.AddRoundedMilliseconds (days * 86400000);
743 // but decimals are the number of hours (in days fractions) and positive
744 Double hours = (days - d);
745 dt = dt.AddRoundedMilliseconds (hours * 86400000);
748 dt = dt.AddRoundedMilliseconds (d * 86400000);
754 public string[] GetDateTimeFormats()
756 return GetDateTimeFormats (CultureInfo.CurrentCulture);
759 public string[] GetDateTimeFormats(char format)
761 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
762 throw new FormatException ("Invalid format character.");
763 string[] result = new string[1];
764 result[0] = this.ToString(format.ToString());
768 public string[] GetDateTimeFormats(IFormatProvider provider)
770 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
771 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
772 var l = new List<string> ();
773 foreach (char c in "dDgGfFmMrRstTuUyY")
774 l.AddRange (GetDateTimeFormats (c, info));
778 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
780 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
781 throw new FormatException ("Invalid format character.");
783 // LAMESPEC: There is NO assurance that 'U' ALWAYS
784 // euqals to 'F', but since we have to iterate all
785 // the pattern strings, we cannot just use
786 // ToString("U", provider) here. I believe that the
787 // method's behavior cannot be formalized.
788 bool adjustutc = false;
797 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
798 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
801 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
803 string [] results = new string [patterns.Length];
804 DateTime val = adjustutc ? ToUniversalTime () : this;
805 for (int i = 0; i < results.Length; i++)
806 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
810 public override int GetHashCode ()
812 return (int) encoded;
815 public TypeCode GetTypeCode ()
817 return TypeCode.DateTime;
820 public static bool IsLeapYear (int year)
822 if (year < 1 || year > 9999)
823 throw new ArgumentOutOfRangeException ();
824 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
827 public static DateTime Parse (string s)
829 return Parse (s, null);
832 public static DateTime Parse (string s, IFormatProvider provider)
834 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
837 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
840 throw new ArgumentNullException ("s");
844 Exception exception = null;
845 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
851 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
853 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
854 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
856 dto = new DateTimeOffset (0, TimeSpan.Zero);
857 if (s == null || s.Length == 0) {
858 if (setExceptionOnError)
859 exception = new FormatException (formatExceptionMessage);
864 if (provider == null)
865 provider = CultureInfo.CurrentCulture;
866 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
868 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
869 string[] allDateFormats = YearMonthDayFormats (dfi);
870 if (allDateFormats == null){
875 bool longYear = false;
876 for (int i = 0; i < allDateFormats.Length; i++) {
877 string firstPart = allDateFormats [i];
878 bool incompleteFormat = false;
879 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
882 if (!incompleteFormat)
885 for (int j = 0; j < ParseTimeFormats.Length; j++) {
886 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
894 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
895 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
896 if (dayIndex == -1 || monthIndex == -1){
898 if (setExceptionOnError)
899 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
902 bool is_day_before_month = dayIndex < monthIndex;
903 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
904 for (int i = 0; i < monthDayFormats.Length; i++) {
905 bool incompleteFormat = false;
906 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
910 for (int j = 0; j < ParseTimeFormats.Length; j++) {
911 string firstPart = ParseTimeFormats [j];
912 bool incompleteFormat = false;
913 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
915 if (!incompleteFormat)
918 for (int i = 0; i < monthDayFormats.Length; i++) {
919 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
922 for (int i = 0; i < allDateFormats.Length; i++) {
923 string dateFormat = allDateFormats [i];
924 if (dateFormat[dateFormat.Length - 1] == 'T')
925 continue; // T formats must be before the time part
926 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
931 // Try as a last resort all the patterns
932 if (CoreParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
935 if (CoreParseExact (s, ExoticAndNonStandardFormats, dfi, styles, out result, out dto, false, ref longYear, setExceptionOnError, ref exception))
938 if (!setExceptionOnError)
941 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
942 exception = new FormatException (formatExceptionMessage);
946 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
948 return ParseExact (s, format, provider, DateTimeStyles.None);
951 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
953 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
954 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
955 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
956 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
957 return ParseGenericYearMonthDayFormats;
959 if (yearIndex < monthIndex)
960 if (monthIndex < dayIndex)
961 return ParseYearMonthDayFormats;
962 else if (yearIndex < dayIndex)
963 return ParseYearDayMonthFormats;
965 // The year cannot be between the date and the month
966 return ParseGenericYearMonthDayFormats;
968 else if (dayIndex < monthIndex)
969 return ParseDayMonthYearFormats;
970 else if (dayIndex < yearIndex)
971 return ParseMonthDayYearFormats;
973 // The year cannot be between the month and the date
974 return ParseGenericYearMonthDayFormats;
978 private static int _ParseNumber (string s, int valuePos,
992 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
993 if (!Char.IsDigit (s[i]))
999 digits = real_digits;
1001 if (digits < min_digits) {
1006 if (s.Length - valuePos < digits) {
1011 for (i = valuePos; i < digits + valuePos; i++) {
1013 if (!Char.IsDigit (c)) {
1018 number = number * 10 + (byte) (c - '0');
1021 num_parsed = digits;
1025 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1027 // FIXME: I know this is somehow lame code. Probably
1028 // it should iterate all the enum value and return
1029 // the longest match. However right now I don't see
1030 // anything but "1" and "10" - "12" that might match
1031 // two or more values. (They are only abbrev month
1032 // names, so do reverse order search). See bug #80094.
1033 for (int i = values.Length - 1; i >= 0; i--) {
1034 if (!exact && invValues [i].Length > values[i].Length) {
1035 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1037 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1041 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1043 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1052 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1055 maxlength = value.Length;
1057 if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
1058 num_parsed = maxlength;
1066 // Note that in case of Parse (exact == false) we check both for AM/PM
1067 // and the culture spcific AM/PM strings.
1068 private static bool _ParseAmPm(string s,
1071 DateTimeFormatInfo dfi,
1080 if (!IsLetter (s, valuePos)) {
1081 if (dfi.AMDesignator != "")
1088 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1089 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1090 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1092 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1093 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1094 if (exact || num_parsed != 0)
1102 // Note that in case of Parse (exact == false) we check both for ':'
1103 // and the culture spcific TimeSperator
1104 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1106 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1107 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1110 // Accept any character for DateSeparator, except TimeSeparator,
1111 // a digit or a letter.
1112 // Not documented, but seems to be MS behaviour here. See bug 54047.
1113 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1116 if (exact && s [sPos] != '/')
1119 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1120 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1127 private static bool IsLetter (string s, int pos)
1129 return pos < s.Length && Char.IsLetter (s [pos]);
1132 // To implement better DateTime.Parse we use two format strings one
1133 // for Date and one for Time. This allows us to define two different
1134 // arrays of formats for Time and Dates and to combine them more or less
1135 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1136 private static bool _DoParse (string s,
1140 out DateTime result,
1141 out DateTimeOffset dto,
1142 DateTimeFormatInfo dfi,
1143 DateTimeStyles style,
1144 bool firstPartIsDate,
1145 ref bool incompleteFormat,
1147 bool dateTimeOffset = false)
1149 bool useutc = false;
1150 bool use_invariant = false;
1151 bool sloppy_parsing = false;
1152 dto = new DateTimeOffset (0, TimeSpan.Zero);
1153 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1154 incompleteFormat = false;
1156 string format = firstPart;
1157 bool afterTFormat = false;
1158 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1159 if (format.Length == 1)
1160 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant, dateTimeOffset);
1162 result = new DateTime (0);
1169 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1170 format = format.TrimStart (null);
1172 s = s.TrimStart (null); // it could be optimized, but will make little good.
1175 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1176 format = format.TrimEnd (null);
1177 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1183 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1184 sloppy_parsing = true;
1186 string chars = format;
1187 int len = format.Length, pos = 0, num = 0;
1191 int day = -1, dayofweek = -1, month = -1, year = -1;
1192 int hour = -1, minute = -1, second = -1;
1193 double fractionalSeconds = -1;
1195 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1196 bool isFirstPart = true;
1197 bool format_with_24_hours = false;
1201 if (valuePos == s.Length)
1205 if (flexibleTwoPartsParsing && pos + num == 0)
1207 bool isLetter = IsLetter(s, valuePos);
1209 if (s [valuePos] == 'Z')
1212 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1213 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1214 valuePos += num_parsed;
1219 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1220 if (IsLetter (s, valuePos + num_parsed))
1222 else if (num_parsed > 0) {
1223 valuePos += num_parsed;
1228 if (!afterTFormat && dayofweek == -1 && isLetter) {
1229 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1230 if (dayofweek == -1)
1231 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1232 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1233 valuePos += num_parsed;
1240 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1247 if (pos + num >= len)
1249 if (flexibleTwoPartsParsing && num == 0) {
1250 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1251 if (!isFirstPart && format == "")
1256 format = secondPart;
1261 isFirstPart = false;
1267 bool leading_zeros = true;
1269 if (chars[pos] == '\'') {
1271 while (pos+num < len) {
1272 if (chars[pos+num] == '\'')
1275 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1285 } else if (chars[pos] == '"') {
1287 while (pos+num < len) {
1288 if (chars[pos+num] == '"')
1291 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1301 } else if (chars[pos] == '\\') {
1306 if (s [valuePos] != chars [pos])
1312 } else if (chars[pos] == '%') {
1315 } else if (char.IsWhiteSpace (s [valuePos]) ||
1316 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1319 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1320 if (!Char.IsWhiteSpace (chars[pos]))
1327 while (ws < s.Length) {
1328 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1335 while (ws < chars.Length) {
1336 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1342 // A whitespace may match a '/' in the pattern.
1343 if (!exact && pos < chars.Length && chars[pos] == '/')
1344 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1349 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1357 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1360 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1362 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1364 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1366 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1372 if (flexibleTwoPartsParsing) {
1374 if (num == 0 || num == 3)
1375 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1376 if (num > 1 && num_parsed == -1)
1377 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1378 if (num > 1 && num_parsed == -1)
1379 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1384 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1386 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1388 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1390 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1397 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1398 } else if (num < 3) {
1399 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1401 year = _ParseNumber (s, valuePos, exact ? num + 1 : 3, num + 1, false, sloppy_parsing, out num_parsed);
1402 longYear = (year > 9999);
1405 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1406 if (num_parsed <= 2)
1407 year += (year < 30) ? 2000 : 1900;
1413 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1415 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1424 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1427 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1429 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1434 format_with_24_hours = true;
1440 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1442 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1452 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1454 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1461 leading_zeros = false;
1464 if (num > 6 || fractionalSeconds != -1)
1466 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1467 if (num_parsed == -1)
1469 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1472 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1479 if (s [valuePos] == '+')
1481 else if (s [valuePos] == '-')
1488 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1490 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1492 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1493 valuePos += num_parsed;
1498 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1499 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1500 valuePos += num_parsed;
1501 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1505 else if (!flexibleTwoPartsParsing)
1512 if (s [valuePos] == 'Z') {
1516 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1519 if (s [valuePos] == '+')
1521 else if (s [valuePos] == '-')
1526 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1527 valuePos += num_parsed;
1531 if (Char.IsDigit (s [valuePos]))
1533 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1535 valuePos += num_parsed;
1537 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1543 // LAMESPEC: This should be part of UTCpattern
1544 // string and thus should not be considered here.
1546 // Note that 'Z' is not defined as a pattern
1547 // character. Keep it for X509 certificate
1548 // verification. Also, "Z" != "'Z'" under MS.NET
1549 // ("'Z'" is just literal; handled above)
1551 if (s [valuePos] != 'Z')
1558 if (s [valuePos] != 'G')
1561 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1562 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1563 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1575 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1579 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1585 if (s[valuePos] == '.') {
1591 // '.FFF....' can be mapped to nothing
1592 if (pos + 1 < len && chars[pos + 1] == 'F') {
1594 while (pos + 1 < len && chars[pos + 1] == 'F') {
1606 if (s [valuePos] != chars [pos])
1617 valuePos += num_parsed;
1619 if (!exact && !flexibleTwoPartsParsing) {
1620 switch (chars [pos]) {
1626 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1627 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1635 pos = pos + num + 1;
1639 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1641 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1644 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1650 if (s.Length > valuePos) // extraneous tail.
1655 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1657 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1659 incompleteFormat = true;
1670 if (fractionalSeconds == -1)
1671 fractionalSeconds = 0;
1673 // If no date was given
1674 if ((day == -1) && (month == -1) && (year == -1)) {
1675 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1680 day = DateTime.Today.Day;
1681 month = DateTime.Today.Month;
1682 year = DateTime.Today.Year;
1691 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1694 year = DateTime.Today.Year;
1697 if (ampm == 0) { // AM designator
1698 if (hour >= 12 && format_with_24_hours && exact)
1703 } else if (ampm == 1) { // PM designator
1705 if (format_with_24_hours && exact)
1713 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1718 result = result.AddSeconds(fractionalSeconds);
1720 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1724 if (result != DateTime.MinValue) {
1726 if ((style & DateTimeStyles.AssumeUniversal) != 0) {
1727 dto = new DateTimeOffset (result, TimeSpan.Zero);
1728 } else if ((style & DateTimeStyles.AssumeLocal) != 0) {
1729 var offset = use_invariant ?
1731 TimeZone.CurrentTimeZone.GetUtcOffset (DateTime.Now);
1732 dto = new DateTimeOffset (result, offset);
1734 dto = new DateTimeOffset (result);
1736 } catch { } // We handle this error in DateTimeOffset.Parse
1744 tzoffset = -tzoffset;
1745 tzoffmin = -tzoffmin;
1748 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1749 } catch {} // We handle this error in DateTimeOffset.Parse
1751 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1754 long newticks = (result - dto.Offset).Ticks;
1756 newticks += TimeSpan.TicksPerDay;
1757 result = new DateTime (newticks, DateTimeKind.Utc);
1758 if ((style & DateTimeStyles.RoundtripKind) != 0)
1759 result = result.ToLocalTime ();
1760 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1761 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1762 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1763 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1765 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1766 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1767 if (adjustToUniversal)
1768 result = result.ToUniversalTime ();
1769 else if (adjustToLocal)
1770 result = result.ToLocalTime ();
1776 public static DateTime ParseExact (string s, string format,
1777 IFormatProvider provider, DateTimeStyles style)
1780 throw new ArgumentNullException ("format");
1782 string [] formats = new string [1];
1783 formats[0] = format;
1785 return ParseExact (s, formats, provider, style);
1788 public static DateTime ParseExact (string s, string[] formats,
1789 IFormatProvider provider,
1790 DateTimeStyles style)
1792 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1795 throw new ArgumentNullException ("s");
1796 if (formats == null)
1797 throw new ArgumentNullException ("formats");
1798 if (formats.Length == 0)
1799 throw new FormatException ("Format specifier was invalid.");
1803 bool longYear = false;
1805 if (!CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, true, ref e))
1810 private static void CheckStyle (DateTimeStyles style)
1812 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1814 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1815 (style & DateTimeStyles.AssumeUniversal) != 0)
1816 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1818 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1819 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1822 public static bool TryParse (string s, out DateTime result)
1826 Exception exception = null;
1829 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1836 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1840 Exception exception = null;
1843 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1850 public static bool TryParseExact (string s, string format,
1851 IFormatProvider provider,
1852 DateTimeStyles style,
1853 out DateTime result)
1856 formats = new string [1];
1857 formats[0] = format;
1859 return TryParseExact (s, formats, provider, style, out result);
1862 public static bool TryParseExact (string s, string[] formats,
1863 IFormatProvider provider,
1864 DateTimeStyles style,
1865 out DateTime result)
1868 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1871 bool longYear = false;
1873 return CoreParseExact (s, formats, dfi, style, out result, out dto, true, ref longYear, false, ref e);
1880 internal static bool CoreParseExact (string s, string [] formats,
1881 DateTimeFormatInfo dfi, DateTimeStyles style,
1882 out DateTime ret, out DateTimeOffset dto,
1883 bool exact, ref bool longYear,
1884 bool setExceptionOnError, ref Exception exception,
1885 bool dateTimeOffset = false)
1887 dto = new DateTimeOffset (0, TimeSpan.Zero);
1889 bool incompleteFormat = false;
1890 for (i = 0; i < formats.Length; i++)
1893 string format = formats[i];
1894 if (format == null || format == String.Empty)
1897 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear, dateTimeOffset)) {
1903 if (setExceptionOnError)
1904 exception = new FormatException ("Invalid format string");
1905 ret = DateTime.MinValue;
1909 public TimeSpan Subtract (DateTime value)
1911 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1914 public DateTime Subtract(TimeSpan value)
1918 newticks = Ticks - value.Ticks;
1919 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1920 throw new ArgumentOutOfRangeException ();
1921 DateTime ret = new DateTime (newticks);
1922 ret.encoded |= (encoded & KindMask);
1926 public long ToFileTime()
1928 DateTime universalTime = ToUniversalTime();
1930 if (universalTime.Ticks < w32file_epoch) {
1931 throw new ArgumentOutOfRangeException("file time is not valid");
1934 return(universalTime.Ticks - w32file_epoch);
1937 public long ToFileTimeUtc()
1939 if (Kind == DateTimeKind.Local)
1940 return ToFileTime ();
1942 if (Ticks < w32file_epoch) {
1943 throw new ArgumentOutOfRangeException("file time is not valid");
1946 return (Ticks - w32file_epoch);
1949 public string ToLongDateString()
1951 return ToString ("D");
1954 public string ToLongTimeString()
1956 return ToString ("T");
1959 public double ToOADate ()
1961 long t = this.Ticks;
1962 // uninitialized DateTime case
1965 // we can't reach minimum value
1966 if (t < 31242239136000000)
1967 return OAMinValue + 0.001;
1969 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1970 double result = ts.TotalDays;
1971 // t < 0 (where 599264352000000000 == 0.0d for OA)
1972 if (t < 599264352000000000) {
1973 // negative days (int) but decimals are positive
1974 double d = Math.Ceiling (result);
1975 result = d - 2 - (result - d);
1978 // we can't reach maximum value
1979 if (result >= OAMaxValue)
1980 result = OAMaxValue - 0.00000001d;
1985 public string ToShortDateString()
1987 return ToString ("d");
1990 public string ToShortTimeString()
1992 return ToString ("t");
1995 public override string ToString ()
1997 return ToString ("G", null);
2000 public string ToString (IFormatProvider provider)
2002 return ToString (null, provider);
2005 public string ToString (string format)
2007 return ToString (format, null);
2010 public string ToString (string format, IFormatProvider provider)
2012 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2014 if (format == null || format == String.Empty)
2017 if (format.Length == 1) {
2018 char fchar = format [0];
2019 bool use_invariant, useutc;
2020 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2022 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2023 // return ToUniversalTime()._ToString (format, dfi);
2026 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2029 dfi = DateTimeFormatInfo.InvariantInfo;
2032 // Don't convert UTC value. It just adds 'Z' for
2033 // 'u' format, for the same ticks.
2034 return DateTimeUtils.ToString (this, format, dfi);
2037 public DateTime ToLocalTime ()
2039 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2042 public DateTime ToUniversalTime()
2044 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2049 public static DateTime operator +(DateTime d, TimeSpan t)
2052 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2053 if (res < 0 || res > MAX_VALUE_TICKS){
2054 throw new ArgumentOutOfRangeException ();
2057 return new DateTime (res, d.Kind);
2058 } catch (OverflowException){
2059 throw new ArgumentOutOfRangeException ();
2063 public static bool operator ==(DateTime d1, DateTime d2)
2065 return ((d1.encoded & TicksMask) == (d2.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 t1,DateTime t2)
2075 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2078 public static bool operator !=(DateTime d1, DateTime d2)
2080 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2083 public static bool operator <(DateTime t1, DateTime t2)
2085 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2088 public static bool operator <=(DateTime t1, DateTime t2)
2090 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2093 public static TimeSpan operator -(DateTime d1, DateTime d2)
2095 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2098 public static DateTime operator -(DateTime d, TimeSpan t)
2101 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2102 if (res < 0 || res > MAX_VALUE_TICKS)
2103 throw new ArgumentOutOfRangeException ();
2104 return new DateTime (res, d.Kind);
2105 } catch (OverflowException){
2106 throw new ArgumentOutOfRangeException ();
2110 bool IConvertible.ToBoolean (IFormatProvider provider)
2112 throw new InvalidCastException();
2115 byte IConvertible.ToByte (IFormatProvider provider)
2117 throw new InvalidCastException();
2121 char IConvertible.ToChar (IFormatProvider provider)
2123 throw new InvalidCastException();
2126 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2131 decimal IConvertible.ToDecimal (IFormatProvider provider)
2133 throw new InvalidCastException();
2136 double IConvertible.ToDouble (IFormatProvider provider)
2138 throw new InvalidCastException();
2141 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2143 throw new InvalidCastException();
2146 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2148 throw new InvalidCastException();
2151 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2153 throw new InvalidCastException();
2156 SByte IConvertible.ToSByte (IFormatProvider provider)
2158 throw new InvalidCastException();
2161 Single IConvertible.ToSingle (IFormatProvider provider)
2163 throw new InvalidCastException();
2166 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2168 if (targetType == null)
2169 throw new ArgumentNullException ("targetType");
2171 if (targetType == typeof (DateTime))
2173 else if (targetType == typeof (String))
2174 return this.ToString (provider);
2175 else if (targetType == typeof (Object))
2178 throw new InvalidCastException();
2181 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2183 throw new InvalidCastException();
2186 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2188 throw new InvalidCastException();
2191 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2193 throw new InvalidCastException();
2196 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2199 info.AddValue ("ticks", t);
2201 // This is the new .NET format, encodes the kind on the top bits
2202 info.AddValue ("dateData", (UInt64)encoded);
2206 static DateTime () {
2207 if (MonoTouchAOTHelper.FalseFlag) {
2208 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2209 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();