5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
9 // (C) 2001 Marcel Narings
10 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections.Generic;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
42 /// The DateTime structure represents dates and time ranging from
43 /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
47 [StructLayout (LayoutKind.Auto)]
48 public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
51 // Encodes the DateTime in 64 bits, top two bits contain the DateTimeKind,
52 // the rest contains the 62 bit value for the ticks. This reduces the
53 // memory usage from 16 to 8 bytes, see bug: 592221. This also fixes the
54 // 622127 issue and simplifies the code in reflection.c to encode DateTimes
57 const long TicksMask = 0x3fffffffffffffff;
58 const long KindMask = unchecked ((long) 0xc000000000000000);
59 const int KindShift = 62;
61 private const int dp400 = 146097;
62 private const int dp100 = 36524;
63 private const int dp4 = 1461;
65 // w32 file time starts counting from 1/1/1601 00:00 GMT
66 // which is the constant ticks from the .NET epoch
67 private const long w32file_epoch = 504911232000000000L;
69 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
70 // -- Microsoft .NET has this value.
71 private const long MAX_VALUE_TICKS = 3155378975999999999L;
74 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
77 internal const long UnixEpoch = 621355968000000000L;
79 // for OLE Automation dates
80 private const long ticks18991230 = 599264352000000000L;
81 private const double OAMinValue = -657435.0d;
82 private const double OAMaxValue = 2958466.0d;
84 public static readonly DateTime MaxValue = new DateTime (3155378975999999999);
85 public static readonly DateTime MinValue = new DateTime (0);
87 // DateTime.Parse patterns
88 // Patterns are divided to date and time patterns. The algorithm will
89 // try combinations of these patterns. The algorithm also looks for
90 // day of the week, AM/PM GMT and Z independently of the patterns.
91 private static readonly string[] ParseTimeFormats = new string [] {
106 "H tt", // Specifies AM to disallow '8'.
107 "H'\u6642'm'\u5206's'\u79D2'",
110 // DateTime.Parse date patterns extend ParseExact patterns as follows:
111 // MMM - month short name or month full name
112 // MMMM - month number or short name or month full name
114 // Parse behaves differently according to the ShorDatePattern of the
115 // DateTimeFormatInfo. The following define the date patterns for
116 // different orders of day, month and year in ShorDatePattern.
117 // Note that the year cannot go between the day and the month.
118 private static readonly string[] ParseYearDayMonthFormats = new string [] {
121 "yyyy'\u5E74'M'\u6708'd'\u65E5",
134 private static readonly string[] ParseYearMonthDayFormats = new string [] {
137 "yyyy'\u5E74'M'\u6708'd'\u65E5",
151 private static readonly string[] ParseDayMonthYearFormats = new string [] {
154 "yyyy'\u5E74'M'\u6708'd'\u65E5",
171 private static readonly string[] ParseMonthDayYearFormats = new string [] {
174 "yyyy'\u5E74'M'\u6708'd'\u65E5",
191 private static readonly string[] ParseGenericYearMonthDayFormats = new string [] {
196 "yyyy'\u5E74'M'\u6708'd'\u65E5",
201 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
202 // Note that these patterns cannot be followed by the time.
203 private static readonly string[] MonthDayShortFormats = new string [] {
208 private static readonly string[] DayMonthShortFormats = new string [] {
222 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
223 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
225 private static int AbsoluteDays (int year, int month, int day)
230 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
234 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
237 private int FromTicks(Which what)
239 int num400, num100, num4, numyears;
242 int[] days = daysmonth;
243 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
245 num400 = (totaldays / dp400);
246 totaldays -= num400 * dp400;
248 num100 = (totaldays / dp100);
249 if (num100 == 4) // leap
251 totaldays -= (num100 * dp100);
253 num4 = totaldays / dp4;
254 totaldays -= (num4 * dp4);
256 numyears = totaldays / 365 ;
258 if (numyears == 4) //leap
260 if (what == Which.Year )
261 return num400*400 + num100*100 + num4*4 + numyears + 1;
263 totaldays -= (numyears * 365) ;
264 if (what == Which.DayYear )
265 return totaldays + 1;
267 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
268 days = daysmonthleap;
270 while (totaldays >= days[M])
271 totaldays -= days[M++];
273 if (what == Which.Month )
279 static void InvalidTickValue (long ticks)
281 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
282 throw new ArgumentOutOfRangeException ("ticks", msg);
288 /// Constructs a DateTime for specified ticks
291 public DateTime (long ticks)
293 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
294 InvalidTickValue (ticks);
298 public DateTime (int year, int month, int day)
299 : this (year, month, day,0,0,0,0) {}
301 public DateTime (int year, int month, int day, int hour, int minute, int second)
302 : this (year, month, day, hour, minute, second, 0) {}
304 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
306 if (year < 1 || year > 9999 ||
307 month < 1 || month >12 ||
308 day < 1 || day > DaysInMonth(year, month) ||
309 hour < 0 || hour > 23 ||
310 minute < 0 || minute > 59 ||
311 second < 0 || second > 59 ||
312 millisecond < 0 || millisecond > 999)
313 throw new ArgumentOutOfRangeException ("Parameters describe an " +
314 "unrepresentable DateTime.");
316 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
319 public DateTime (int year, int month, int day, Calendar calendar)
320 : this (year, month, day, 0, 0, 0, 0, calendar)
324 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
325 : this (year, month, day, hour, minute, second, 0, calendar)
329 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
331 if (calendar == null)
332 throw new ArgumentNullException ("calendar");
333 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
336 public DateTime (long ticks, DateTimeKind kind)
338 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
339 InvalidTickValue (ticks);
340 if (kind < 0 || kind > DateTimeKind.Local)
341 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
343 encoded = ((long)kind << KindShift) | ticks;
346 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
347 : this (year, month, day, hour, minute, second)
349 if (kind < 0 || kind > DateTimeKind.Local)
350 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
351 encoded |= ((long)kind << KindShift);
354 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
355 : this (year, month, day, hour, minute, second, millisecond)
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, Calendar calendar, DateTimeKind kind)
363 : this (year, month, day, hour, minute, second, millisecond, calendar)
365 if (kind < 0 || kind > DateTimeKind.Local)
366 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
367 encoded |= ((long)kind << KindShift);
371 // Not visible, but can be invoked during deserialization
373 DateTime (SerializationInfo info, StreamingContext context)
375 if (info.HasKey ("dateData")){
376 encoded = info.GetInt64 ("dateData");
377 } else if (info.HasKey ("ticks")){
378 encoded = info.GetInt64 ("ticks") & TicksMask;
387 public DateTime Date {
389 DateTime ret = new DateTime (Year, Month, Day);
390 ret.encoded |= encoded & KindMask;
397 return FromTicks (Which.Month);
403 return FromTicks (Which.Day);
407 public DayOfWeek DayOfWeek {
409 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
413 public int DayOfYear {
415 return FromTicks (Which.DayYear);
419 public TimeSpan TimeOfDay {
421 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
428 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
434 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
440 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
444 public int Millisecond {
446 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
450 [MethodImplAttribute(MethodImplOptions.InternalCall)]
451 internal static extern long GetTimeMonotonic ();
453 [MethodImplAttribute(MethodImplOptions.InternalCall)]
454 internal static extern long GetNow ();
457 // To reduce the time consumed by DateTime.Now, we keep
458 // the difference to map the system time into a local
459 // time into `to_local_time_span', we record the timestamp
460 // for this in `last_now'
462 static object to_local_time_span_object;
463 static long last_now;
465 public static DateTime Now {
467 long now = GetNow ();
468 DateTime dt = new DateTime (now);
470 if ((now - last_now) > TimeSpan.TicksPerMinute){
471 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
476 // This is boxed, so we avoid locking.
477 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
478 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
485 return encoded & TicksMask;
489 public static DateTime Today {
492 DateTime today = new DateTime (now.Year, now.Month, now.Day);
493 today.encoded |= ((long)DateTimeKind.Local << KindShift);
498 public static DateTime UtcNow {
500 return new DateTime (GetNow (), DateTimeKind.Utc);
506 return FromTicks (Which.Year);
510 public DateTimeKind Kind {
512 return (DateTimeKind) ((ulong)encoded >> KindShift);
518 public DateTime Add (TimeSpan value)
520 DateTime ret = AddTicks (value.Ticks);
524 public DateTime AddDays (double value)
526 return AddMilliseconds (Math.Round (value * 86400000));
529 public DateTime AddTicks (long value)
531 long res = value + (encoded & TicksMask);
532 if (res < 0 || res > MAX_VALUE_TICKS)
533 throw new ArgumentOutOfRangeException();
535 DateTime ret = new DateTime (res);
536 ret.encoded |= (encoded & KindMask);
540 public DateTime AddHours (double value)
542 return AddMilliseconds (value * 3600000);
545 public DateTime AddMilliseconds (double value)
547 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
548 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
549 throw new ArgumentOutOfRangeException();
551 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
553 return AddTicks (msticks);
556 // required to match MS implementation for OADate (OLE Automation)
557 private DateTime AddRoundedMilliseconds (double ms)
559 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
560 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
561 throw new ArgumentOutOfRangeException ();
563 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
565 return AddTicks (msticks);
568 public DateTime AddMinutes (double value)
570 return AddMilliseconds (value * 60000);
573 public DateTime AddMonths (int months)
575 int day, month, year, maxday ;
579 month = this.Month + (months % 12);
580 year = this.Year + months/12 ;
592 maxday = DaysInMonth(year, month);
596 temp = new DateTime (year, month, day);
597 temp.encoded |= encoded & KindMask;
598 return temp.Add (this.TimeOfDay);
601 public DateTime AddSeconds (double value)
603 return AddMilliseconds (value * 1000);
606 public DateTime AddYears (int value)
608 return AddMonths (value * 12);
611 public static int Compare (DateTime t1, DateTime t2)
613 long t1t = t1.encoded & TicksMask;
614 long t2t = t2.encoded & TicksMask;
624 public int CompareTo (object value)
629 if (!(value is System.DateTime))
630 throw new ArgumentException (Locale.GetText (
631 "Value is not a System.DateTime"));
633 return Compare (this, (DateTime) value);
636 public bool IsDaylightSavingTime ()
638 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
640 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
643 public int CompareTo (DateTime value)
645 return Compare (this, value);
648 public bool Equals (DateTime value)
650 return (value.encoded & TicksMask) == (encoded & TicksMask);
653 public long ToBinary ()
655 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
656 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
661 public static DateTime FromBinary (long dateData)
663 switch ((ulong)dateData >> KindShift) {
665 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
666 case 0: // Unspecified
667 return new DateTime (dateData, DateTimeKind.Unspecified);
669 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
673 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
675 return new DateTime (value.Ticks, kind);
678 public static int DaysInMonth (int year, int month)
682 if (month < 1 || month >12)
683 throw new ArgumentOutOfRangeException ();
685 if (year < 1 || year > 9999)
686 throw new ArgumentOutOfRangeException ();
688 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
692 public override bool Equals (object value)
694 if (!(value is System.DateTime))
697 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
700 public static bool Equals (DateTime t1, DateTime t2 )
702 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
705 public static DateTime FromFileTime (long fileTime)
708 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
710 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
713 public static DateTime FromFileTimeUtc (long fileTime)
716 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
718 return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
721 public static DateTime FromOADate (double d)
723 // An OLE Automation date is implemented as a floating-point number
724 // whose value is the number of days from midnight, 30 December 1899.
726 // d must be negative 657435.0 through positive 2958466.0.
727 if ((d <= OAMinValue) || (d >= OAMaxValue))
728 throw new ArgumentException ("d", "[-657435,2958466]");
730 DateTime dt = new DateTime (ticks18991230);
732 Double days = Math.Ceiling (d);
733 // integer part is the number of days (negative)
734 dt = dt.AddRoundedMilliseconds (days * 86400000);
735 // but decimals are the number of hours (in days fractions) and positive
736 Double hours = (days - d);
737 dt = dt.AddRoundedMilliseconds (hours * 86400000);
740 dt = dt.AddRoundedMilliseconds (d * 86400000);
746 public string[] GetDateTimeFormats()
748 return GetDateTimeFormats (CultureInfo.CurrentCulture);
751 public string[] GetDateTimeFormats(char format)
753 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
754 throw new FormatException ("Invalid format character.");
755 string[] result = new string[1];
756 result[0] = this.ToString(format.ToString());
760 public string[] GetDateTimeFormats(IFormatProvider provider)
762 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
763 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
764 var l = new List<string> ();
765 foreach (char c in "dDgGfFmMrRstTuUyY")
766 l.AddRange (GetDateTimeFormats (c, info));
770 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
772 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
773 throw new FormatException ("Invalid format character.");
775 // LAMESPEC: There is NO assurance that 'U' ALWAYS
776 // euqals to 'F', but since we have to iterate all
777 // the pattern strings, we cannot just use
778 // ToString("U", provider) here. I believe that the
779 // method's behavior cannot be formalized.
780 bool adjustutc = false;
789 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
790 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
793 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
795 string [] results = new string [patterns.Length];
796 DateTime val = adjustutc ? ToUniversalTime () : this;
797 for (int i = 0; i < results.Length; i++)
798 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
802 public override int GetHashCode ()
804 return (int) encoded;
807 public TypeCode GetTypeCode ()
809 return TypeCode.DateTime;
812 public static bool IsLeapYear (int year)
814 if (year < 1 || year > 9999)
815 throw new ArgumentOutOfRangeException ();
816 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
819 public static DateTime Parse (string s)
821 return Parse (s, null);
824 public static DateTime Parse (string s, IFormatProvider provider)
826 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
829 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
832 throw new ArgumentNullException ("s");
836 Exception exception = null;
837 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
843 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
845 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
846 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
848 dto = new DateTimeOffset (0, TimeSpan.Zero);
849 if (s == null || s.Length == 0) {
850 if (setExceptionOnError)
851 exception = new FormatException (formatExceptionMessage);
856 if (provider == null)
857 provider = CultureInfo.CurrentCulture;
858 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
860 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
861 string[] allDateFormats = YearMonthDayFormats (dfi);
862 if (allDateFormats == null){
867 bool longYear = false;
868 for (int i = 0; i < allDateFormats.Length; i++) {
869 string firstPart = allDateFormats [i];
870 bool incompleteFormat = false;
871 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
874 if (!incompleteFormat)
877 for (int j = 0; j < ParseTimeFormats.Length; j++) {
878 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
886 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
887 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
888 if (dayIndex == -1 || monthIndex == -1){
890 if (setExceptionOnError)
891 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
894 bool is_day_before_month = dayIndex < monthIndex;
895 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
896 for (int i = 0; i < monthDayFormats.Length; i++) {
897 bool incompleteFormat = false;
898 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
902 for (int j = 0; j < ParseTimeFormats.Length; j++) {
903 string firstPart = ParseTimeFormats [j];
904 bool incompleteFormat = false;
905 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
907 if (!incompleteFormat)
910 for (int i = 0; i < monthDayFormats.Length; i++) {
911 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
914 for (int i = 0; i < allDateFormats.Length; i++) {
915 string dateFormat = allDateFormats [i];
916 if (dateFormat[dateFormat.Length - 1] == 'T')
917 continue; // T formats must be before the time part
918 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
923 // Try as a last resort all the patterns
924 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
927 if (!setExceptionOnError)
930 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
931 exception = new FormatException (formatExceptionMessage);
935 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
937 return ParseExact (s, format, provider, DateTimeStyles.None);
940 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
942 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
943 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
944 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
945 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
946 return ParseGenericYearMonthDayFormats;
948 if (yearIndex < monthIndex)
949 if (monthIndex < dayIndex)
950 return ParseYearMonthDayFormats;
951 else if (yearIndex < dayIndex)
952 return ParseYearDayMonthFormats;
954 // The year cannot be between the date and the month
955 return ParseGenericYearMonthDayFormats;
957 else if (dayIndex < monthIndex)
958 return ParseDayMonthYearFormats;
959 else if (dayIndex < yearIndex)
960 return ParseMonthDayYearFormats;
962 // The year cannot be between the month and the date
963 return ParseGenericYearMonthDayFormats;
967 private static int _ParseNumber (string s, int valuePos,
981 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
982 if (!Char.IsDigit (s[i]))
988 digits = real_digits;
990 if (digits < min_digits) {
995 if (s.Length - valuePos < digits) {
1000 for (i = valuePos; i < digits + valuePos; i++) {
1002 if (!Char.IsDigit (c)) {
1007 number = number * 10 + (byte) (c - '0');
1010 num_parsed = digits;
1014 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1016 // FIXME: I know this is somehow lame code. Probably
1017 // it should iterate all the enum value and return
1018 // the longest match. However right now I don't see
1019 // anything but "1" and "10" - "12" that might match
1020 // two or more values. (They are only abbrev month
1021 // names, so do reverse order search). See bug #80094.
1022 for (int i = values.Length - 1; i >= 0; i--) {
1023 if (!exact && invValues [i].Length > values[i].Length) {
1024 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1026 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1030 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1032 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1041 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1044 maxlength = value.Length;
1046 if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1047 num_parsed = maxlength;
1055 // Note that in case of Parse (exact == false) we check both for AM/PM
1056 // and the culture spcific AM/PM strings.
1057 private static bool _ParseAmPm(string s,
1060 DateTimeFormatInfo dfi,
1069 if (!IsLetter (s, valuePos)) {
1070 if (dfi.AMDesignator != "")
1077 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1078 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1079 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1081 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1082 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1083 if (exact || num_parsed != 0)
1091 // Note that in case of Parse (exact == false) we check both for ':'
1092 // and the culture spcific TimeSperator
1093 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1095 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1096 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1099 // Accept any character for DateSeparator, except TimeSeparator,
1100 // a digit or a letter.
1101 // Not documented, but seems to be MS behaviour here. See bug 54047.
1102 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1105 if (exact && s [sPos] != '/')
1108 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1109 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1116 private static bool IsLetter (string s, int pos)
1118 return pos < s.Length && Char.IsLetter (s [pos]);
1121 // To implement better DateTime.Parse we use two format strings one
1122 // for Date and one for Time. This allows us to define two different
1123 // arrays of formats for Time and Dates and to combine them more or less
1124 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1125 private static bool _DoParse (string s,
1129 out DateTime result,
1130 out DateTimeOffset dto,
1131 DateTimeFormatInfo dfi,
1132 DateTimeStyles style,
1133 bool firstPartIsDate,
1134 ref bool incompleteFormat,
1137 bool useutc = false;
1138 bool use_invariant = false;
1139 bool sloppy_parsing = false;
1140 dto = new DateTimeOffset (0, TimeSpan.Zero);
1141 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1142 incompleteFormat = false;
1144 string format = firstPart;
1145 bool afterTFormat = false;
1146 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1147 if (format.Length == 1)
1148 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1150 result = new DateTime (0);
1157 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1158 format = format.TrimStart (null);
1160 s = s.TrimStart (null); // it could be optimized, but will make little good.
1163 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1164 format = format.TrimEnd (null);
1165 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1171 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1172 sloppy_parsing = true;
1174 string chars = format;
1175 int len = format.Length, pos = 0, num = 0;
1179 int day = -1, dayofweek = -1, month = -1, year = -1;
1180 int hour = -1, minute = -1, second = -1;
1181 double fractionalSeconds = -1;
1183 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1184 bool isFirstPart = true;
1188 if (valuePos == s.Length)
1192 if (flexibleTwoPartsParsing && pos + num == 0)
1194 bool isLetter = IsLetter(s, valuePos);
1196 if (s [valuePos] == 'Z')
1199 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1200 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1201 valuePos += num_parsed;
1206 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1207 if (IsLetter (s, valuePos + num_parsed))
1209 else if (num_parsed > 0) {
1210 valuePos += num_parsed;
1215 if (!afterTFormat && dayofweek == -1 && isLetter) {
1216 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1217 if (dayofweek == -1)
1218 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1219 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1220 valuePos += num_parsed;
1227 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1234 if (pos + num >= len)
1236 if (flexibleTwoPartsParsing && num == 0) {
1237 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1238 if (!isFirstPart && format == "")
1243 format = secondPart;
1248 isFirstPart = false;
1254 bool leading_zeros = true;
1256 if (chars[pos] == '\'') {
1258 while (pos+num < len) {
1259 if (chars[pos+num] == '\'')
1262 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1272 } else if (chars[pos] == '"') {
1274 while (pos+num < len) {
1275 if (chars[pos+num] == '"')
1278 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1288 } else if (chars[pos] == '\\') {
1293 if (s [valuePos] != chars [pos])
1299 } else if (chars[pos] == '%') {
1302 } else if (char.IsWhiteSpace (s [valuePos]) ||
1303 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1306 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1307 if (!Char.IsWhiteSpace (chars[pos]))
1314 while (ws < s.Length) {
1315 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1322 while (ws < chars.Length) {
1323 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1329 // A whitespace may match a '/' in the pattern.
1330 if (!exact && pos < chars.Length && chars[pos] == '/')
1331 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1336 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1344 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1347 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1349 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1351 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1353 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1359 if (flexibleTwoPartsParsing) {
1361 if (num == 0 || num == 3)
1362 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1363 if (num > 1 && num_parsed == -1)
1364 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1365 if (num > 1 && num_parsed == -1)
1366 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1371 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1373 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1375 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1377 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1384 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1385 } else if (num < 3) {
1386 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1388 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1389 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1391 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1392 longYear = (ly > 9999);
1397 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1398 if (num_parsed <= 2)
1399 year += (year < 30) ? 2000 : 1900;
1405 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1407 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1416 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1419 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1421 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1432 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1434 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1444 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1446 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1453 leading_zeros = false;
1456 if (num > 6 || fractionalSeconds != -1)
1458 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1459 if (num_parsed == -1)
1461 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1464 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1471 if (s [valuePos] == '+')
1473 else if (s [valuePos] == '-')
1480 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1482 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1484 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1485 valuePos += num_parsed;
1490 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1491 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1492 valuePos += num_parsed;
1493 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1497 else if (!flexibleTwoPartsParsing)
1504 if (s [valuePos] == 'Z') {
1508 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1511 if (s [valuePos] == '+')
1513 else if (s [valuePos] == '-')
1518 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1519 valuePos += num_parsed;
1523 if (Char.IsDigit (s [valuePos]))
1525 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1527 valuePos += num_parsed;
1529 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1536 // LAMESPEC: This should be part of UTCpattern
1537 // string and thus should not be considered here.
1539 // Note that 'Z' is not defined as a pattern
1540 // character. Keep it for X509 certificate
1541 // verification. Also, "Z" != "'Z'" under MS.NET
1542 // ("'Z'" is just literal; handled above)
1544 if (s [valuePos] != 'Z')
1551 if (s [valuePos] != 'G')
1554 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1555 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1556 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1568 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1572 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1578 if (s [valuePos] != chars [pos])
1589 valuePos += num_parsed;
1591 if (!exact && !flexibleTwoPartsParsing) {
1592 switch (chars [pos]) {
1598 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1599 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1607 pos = pos + num + 1;
1611 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1613 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1616 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1622 if (s.Length > valuePos) // extraneous tail.
1627 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1629 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1631 incompleteFormat = true;
1642 if (fractionalSeconds == -1)
1643 fractionalSeconds = 0;
1645 // If no date was given
1646 if ((day == -1) && (month == -1) && (year == -1)) {
1647 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1652 day = DateTime.Today.Day;
1653 month = DateTime.Today.Month;
1654 year = DateTime.Today.Year;
1663 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1666 year = DateTime.Today.Year;
1669 if (ampm == 0 && hour == 12)
1672 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1675 // For anything out of range
1677 if (year < 1 || year > 9999 ||
1678 month < 1 || month >12 ||
1679 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1680 hour < 0 || hour > 23 ||
1681 minute < 0 || minute > 59 ||
1682 second < 0 || second > 59)
1685 result = new DateTime (year, month, day, hour, minute, second, 0);
1686 result = result.AddSeconds(fractionalSeconds);
1688 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1692 if (result != DateTime.MinValue) {
1694 dto = new DateTimeOffset (result);
1695 } catch { } // We handle this error in DateTimeOffset.Parse
1703 tzoffset = -tzoffset;
1704 tzoffmin = -tzoffmin;
1707 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1708 } catch {} // We handle this error in DateTimeOffset.Parse
1710 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1713 long newticks = (result - dto.Offset).Ticks;
1715 newticks += TimeSpan.TicksPerDay;
1716 result = new DateTime (newticks, DateTimeKind.Utc);
1717 if ((style & DateTimeStyles.RoundtripKind) != 0)
1718 result = result.ToLocalTime ();
1719 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1720 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1721 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1722 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1724 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1725 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1726 if (adjustToUniversal)
1727 result = result.ToUniversalTime ();
1728 else if (adjustToLocal)
1729 result = result.ToLocalTime ();
1735 public static DateTime ParseExact (string s, string format,
1736 IFormatProvider provider, DateTimeStyles style)
1739 throw new ArgumentNullException ("format");
1741 string [] formats = new string [1];
1742 formats[0] = format;
1744 return ParseExact (s, formats, provider, style);
1747 public static DateTime ParseExact (string s, string[] formats,
1748 IFormatProvider provider,
1749 DateTimeStyles style)
1751 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1754 throw new ArgumentNullException ("s");
1755 if (formats == null)
1756 throw new ArgumentNullException ("formats");
1757 if (formats.Length == 0)
1758 throw new FormatException ("Format specifier was invalid.");
1761 bool longYear = false;
1763 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1768 private static void CheckStyle (DateTimeStyles style)
1770 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1772 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1773 (style & DateTimeStyles.AssumeUniversal) != 0)
1774 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
1776 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1777 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1780 public static bool TryParse (string s, out DateTime result)
1784 Exception exception = null;
1787 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1794 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1798 Exception exception = null;
1801 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1808 public static bool TryParseExact (string s, string format,
1809 IFormatProvider provider,
1810 DateTimeStyles style,
1811 out DateTime result)
1814 formats = new string [1];
1815 formats[0] = format;
1817 return TryParseExact (s, formats, provider, style, out result);
1820 public static bool TryParseExact (string s, string[] formats,
1821 IFormatProvider provider,
1822 DateTimeStyles style,
1823 out DateTime result)
1826 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1828 bool longYear = false;
1830 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1837 private static bool ParseExact (string s, string [] formats,
1838 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1839 bool exact, ref bool longYear,
1840 bool setExceptionOnError, ref Exception exception)
1843 bool incompleteFormat = false;
1844 for (i = 0; i < formats.Length; i++)
1847 string format = formats[i];
1848 if (format == null || format == String.Empty)
1852 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1858 if (setExceptionOnError)
1859 exception = new FormatException ("Invalid format string");
1860 ret = DateTime.MinValue;
1864 public TimeSpan Subtract (DateTime value)
1866 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1869 public DateTime Subtract(TimeSpan value)
1873 newticks = Ticks - value.Ticks;
1874 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1875 throw new ArgumentOutOfRangeException ();
1876 DateTime ret = new DateTime (newticks);
1877 ret.encoded |= (encoded & KindMask);
1881 public long ToFileTime()
1883 DateTime universalTime = ToUniversalTime();
1885 if (universalTime.Ticks < w32file_epoch) {
1886 throw new ArgumentOutOfRangeException("file time is not valid");
1889 return(universalTime.Ticks - w32file_epoch);
1892 public long ToFileTimeUtc()
1894 if (Ticks < w32file_epoch) {
1895 throw new ArgumentOutOfRangeException("file time is not valid");
1898 return (Ticks - w32file_epoch);
1901 public string ToLongDateString()
1903 return ToString ("D");
1906 public string ToLongTimeString()
1908 return ToString ("T");
1911 public double ToOADate ()
1913 long t = this.Ticks;
1914 // uninitialized DateTime case
1917 // we can't reach minimum value
1918 if (t < 31242239136000000)
1919 return OAMinValue + 0.001;
1921 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1922 double result = ts.TotalDays;
1923 // t < 0 (where 599264352000000000 == 0.0d for OA)
1924 if (t < 599264352000000000) {
1925 // negative days (int) but decimals are positive
1926 double d = Math.Ceiling (result);
1927 result = d - 2 - (result - d);
1930 // we can't reach maximum value
1931 if (result >= OAMaxValue)
1932 result = OAMaxValue - 0.00000001d;
1937 public string ToShortDateString()
1939 return ToString ("d");
1942 public string ToShortTimeString()
1944 return ToString ("t");
1947 public override string ToString ()
1949 return ToString ("G", null);
1952 public string ToString (IFormatProvider provider)
1954 return ToString (null, provider);
1957 public string ToString (string format)
1959 return ToString (format, null);
1962 public string ToString (string format, IFormatProvider provider)
1964 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1966 if (format == null || format == String.Empty)
1969 bool useutc = false, use_invariant = false;
1971 if (format.Length == 1) {
1972 char fchar = format [0];
1973 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1975 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
1976 // return ToUniversalTime()._ToString (format, dfi);
1979 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
1982 // Don't convert UTC value. It just adds 'Z' for
1983 // 'u' format, for the same ticks.
1984 return DateTimeUtils.ToString (this, format, dfi);
1987 public DateTime ToLocalTime ()
1989 return TimeZone.CurrentTimeZone.ToLocalTime (this);
1992 public DateTime ToUniversalTime()
1994 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
1999 public static DateTime operator +(DateTime d, TimeSpan t)
2002 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2003 if (res < 0 || res > MAX_VALUE_TICKS){
2004 throw new ArgumentOutOfRangeException ();
2007 return new DateTime (res, d.Kind);
2008 } catch (OverflowException){
2009 throw new ArgumentOutOfRangeException ();
2013 public static bool operator ==(DateTime d1, DateTime d2)
2015 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2018 public static bool operator >(DateTime t1,DateTime t2)
2020 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2023 public static bool operator >=(DateTime t1,DateTime t2)
2025 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2028 public static bool operator !=(DateTime d1, DateTime d2)
2030 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2033 public static bool operator <(DateTime t1, DateTime t2)
2035 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2038 public static bool operator <=(DateTime t1, DateTime t2)
2040 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2043 public static TimeSpan operator -(DateTime d1, DateTime d2)
2045 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2048 public static DateTime operator -(DateTime d, TimeSpan t)
2051 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2052 if (res < 0 || res > MAX_VALUE_TICKS)
2053 throw new ArgumentOutOfRangeException ();
2054 return new DateTime (res, d.Kind);
2055 } catch (OverflowException){
2056 throw new ArgumentOutOfRangeException ();
2060 bool IConvertible.ToBoolean (IFormatProvider provider)
2062 throw new InvalidCastException();
2065 byte IConvertible.ToByte (IFormatProvider provider)
2067 throw new InvalidCastException();
2071 char IConvertible.ToChar (IFormatProvider provider)
2073 throw new InvalidCastException();
2076 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2081 decimal IConvertible.ToDecimal (IFormatProvider provider)
2083 throw new InvalidCastException();
2086 double IConvertible.ToDouble (IFormatProvider provider)
2088 throw new InvalidCastException();
2091 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2093 throw new InvalidCastException();
2096 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2098 throw new InvalidCastException();
2101 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2103 throw new InvalidCastException();
2106 SByte IConvertible.ToSByte (IFormatProvider provider)
2108 throw new InvalidCastException();
2111 Single IConvertible.ToSingle (IFormatProvider provider)
2113 throw new InvalidCastException();
2116 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2118 if (targetType == null)
2119 throw new ArgumentNullException ("targetType");
2121 if (targetType == typeof (DateTime))
2123 else if (targetType == typeof (String))
2124 return this.ToString (provider);
2125 else if (targetType == typeof (Object))
2128 throw new InvalidCastException();
2131 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2133 throw new InvalidCastException();
2136 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2138 throw new InvalidCastException();
2141 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2143 throw new InvalidCastException();
2146 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2149 info.AddValue ("ticks", t);
2151 // This is the new .NET format, encodes the kind on the top bits
2152 info.AddValue ("dateData", encoded);
2156 static DateTime () {
2157 if (MonoTouchAOTHelper.FalseFlag) {
2158 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2159 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();