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;
1185 bool format_with_24_hours = false;
1189 if (valuePos == s.Length)
1193 if (flexibleTwoPartsParsing && pos + num == 0)
1195 bool isLetter = IsLetter(s, valuePos);
1197 if (s [valuePos] == 'Z')
1200 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1201 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1202 valuePos += num_parsed;
1207 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1208 if (IsLetter (s, valuePos + num_parsed))
1210 else if (num_parsed > 0) {
1211 valuePos += num_parsed;
1216 if (!afterTFormat && dayofweek == -1 && isLetter) {
1217 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1218 if (dayofweek == -1)
1219 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1220 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1221 valuePos += num_parsed;
1228 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1235 if (pos + num >= len)
1237 if (flexibleTwoPartsParsing && num == 0) {
1238 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1239 if (!isFirstPart && format == "")
1244 format = secondPart;
1249 isFirstPart = false;
1255 bool leading_zeros = true;
1257 if (chars[pos] == '\'') {
1259 while (pos+num < len) {
1260 if (chars[pos+num] == '\'')
1263 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1273 } else if (chars[pos] == '"') {
1275 while (pos+num < len) {
1276 if (chars[pos+num] == '"')
1279 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1289 } else if (chars[pos] == '\\') {
1294 if (s [valuePos] != chars [pos])
1300 } else if (chars[pos] == '%') {
1303 } else if (char.IsWhiteSpace (s [valuePos]) ||
1304 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1307 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1308 if (!Char.IsWhiteSpace (chars[pos]))
1315 while (ws < s.Length) {
1316 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1323 while (ws < chars.Length) {
1324 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1330 // A whitespace may match a '/' in the pattern.
1331 if (!exact && pos < chars.Length && chars[pos] == '/')
1332 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1337 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1345 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1348 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1350 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1352 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1354 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1360 if (flexibleTwoPartsParsing) {
1362 if (num == 0 || num == 3)
1363 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1364 if (num > 1 && num_parsed == -1)
1365 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1366 if (num > 1 && num_parsed == -1)
1367 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1372 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1374 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1376 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1378 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1385 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1386 } else if (num < 3) {
1387 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1389 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1390 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1392 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1393 longYear = (ly > 9999);
1398 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1399 if (num_parsed <= 2)
1400 year += (year < 30) ? 2000 : 1900;
1406 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1408 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1417 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1420 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1422 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1427 format_with_24_hours = true;
1433 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1435 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1445 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1447 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1454 leading_zeros = false;
1457 if (num > 6 || fractionalSeconds != -1)
1459 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1460 if (num_parsed == -1)
1462 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1465 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1472 if (s [valuePos] == '+')
1474 else if (s [valuePos] == '-')
1481 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1483 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1485 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1486 valuePos += num_parsed;
1491 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1492 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1493 valuePos += num_parsed;
1494 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1498 else if (!flexibleTwoPartsParsing)
1505 if (s [valuePos] == 'Z') {
1509 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1512 if (s [valuePos] == '+')
1514 else if (s [valuePos] == '-')
1519 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1520 valuePos += num_parsed;
1524 if (Char.IsDigit (s [valuePos]))
1526 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1528 valuePos += num_parsed;
1530 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1537 // LAMESPEC: This should be part of UTCpattern
1538 // string and thus should not be considered here.
1540 // Note that 'Z' is not defined as a pattern
1541 // character. Keep it for X509 certificate
1542 // verification. Also, "Z" != "'Z'" under MS.NET
1543 // ("'Z'" is just literal; handled above)
1545 if (s [valuePos] != 'Z')
1552 if (s [valuePos] != 'G')
1555 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1556 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1557 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1569 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1573 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1579 if (s [valuePos] != chars [pos])
1590 valuePos += num_parsed;
1592 if (!exact && !flexibleTwoPartsParsing) {
1593 switch (chars [pos]) {
1599 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1600 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1608 pos = pos + num + 1;
1612 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1614 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1617 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1623 if (s.Length > valuePos) // extraneous tail.
1628 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1630 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1632 incompleteFormat = true;
1643 if (fractionalSeconds == -1)
1644 fractionalSeconds = 0;
1646 // If no date was given
1647 if ((day == -1) && (month == -1) && (year == -1)) {
1648 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1653 day = DateTime.Today.Day;
1654 month = DateTime.Today.Month;
1655 year = DateTime.Today.Year;
1664 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1667 year = DateTime.Today.Year;
1670 if (ampm == 0) { // AM designator
1671 if (hour >= 12 && format_with_24_hours && exact)
1676 } else if (ampm == 1) { // PM designator
1678 if (format_with_24_hours && exact)
1685 // For anything out of range
1687 if (year < 1 || year > 9999 ||
1688 month < 1 || month >12 ||
1689 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1690 hour < 0 || hour > 23 ||
1691 minute < 0 || minute > 59 ||
1692 second < 0 || second > 59)
1695 result = new DateTime (year, month, day, hour, minute, second, 0);
1696 result = result.AddSeconds(fractionalSeconds);
1698 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1702 if (result != DateTime.MinValue) {
1704 dto = new DateTimeOffset (result);
1705 } catch { } // We handle this error in DateTimeOffset.Parse
1713 tzoffset = -tzoffset;
1714 tzoffmin = -tzoffmin;
1717 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1718 } catch {} // We handle this error in DateTimeOffset.Parse
1720 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1723 long newticks = (result - dto.Offset).Ticks;
1725 newticks += TimeSpan.TicksPerDay;
1726 result = new DateTime (newticks, DateTimeKind.Utc);
1727 if ((style & DateTimeStyles.RoundtripKind) != 0)
1728 result = result.ToLocalTime ();
1729 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1730 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1731 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1732 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1734 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1735 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1736 if (adjustToUniversal)
1737 result = result.ToUniversalTime ();
1738 else if (adjustToLocal)
1739 result = result.ToLocalTime ();
1745 public static DateTime ParseExact (string s, string format,
1746 IFormatProvider provider, DateTimeStyles style)
1749 throw new ArgumentNullException ("format");
1751 string [] formats = new string [1];
1752 formats[0] = format;
1754 return ParseExact (s, formats, provider, style);
1757 public static DateTime ParseExact (string s, string[] formats,
1758 IFormatProvider provider,
1759 DateTimeStyles style)
1761 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1764 throw new ArgumentNullException ("s");
1765 if (formats == null)
1766 throw new ArgumentNullException ("formats");
1767 if (formats.Length == 0)
1768 throw new FormatException ("Format specifier was invalid.");
1771 bool longYear = false;
1773 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1778 private static void CheckStyle (DateTimeStyles style)
1780 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1782 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1783 (style & DateTimeStyles.AssumeUniversal) != 0)
1784 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
1786 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1787 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1790 public static bool TryParse (string s, out DateTime result)
1794 Exception exception = null;
1797 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1804 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1808 Exception exception = null;
1811 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1818 public static bool TryParseExact (string s, string format,
1819 IFormatProvider provider,
1820 DateTimeStyles style,
1821 out DateTime result)
1824 formats = new string [1];
1825 formats[0] = format;
1827 return TryParseExact (s, formats, provider, style, out result);
1830 public static bool TryParseExact (string s, string[] formats,
1831 IFormatProvider provider,
1832 DateTimeStyles style,
1833 out DateTime result)
1836 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1838 bool longYear = false;
1840 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1847 private static bool ParseExact (string s, string [] formats,
1848 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1849 bool exact, ref bool longYear,
1850 bool setExceptionOnError, ref Exception exception)
1853 bool incompleteFormat = false;
1854 for (i = 0; i < formats.Length; i++)
1857 string format = formats[i];
1858 if (format == null || format == String.Empty)
1862 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1868 if (setExceptionOnError)
1869 exception = new FormatException ("Invalid format string");
1870 ret = DateTime.MinValue;
1874 public TimeSpan Subtract (DateTime value)
1876 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1879 public DateTime Subtract(TimeSpan value)
1883 newticks = Ticks - value.Ticks;
1884 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1885 throw new ArgumentOutOfRangeException ();
1886 DateTime ret = new DateTime (newticks);
1887 ret.encoded |= (encoded & KindMask);
1891 public long ToFileTime()
1893 DateTime universalTime = ToUniversalTime();
1895 if (universalTime.Ticks < w32file_epoch) {
1896 throw new ArgumentOutOfRangeException("file time is not valid");
1899 return(universalTime.Ticks - w32file_epoch);
1902 public long ToFileTimeUtc()
1904 if (Ticks < w32file_epoch) {
1905 throw new ArgumentOutOfRangeException("file time is not valid");
1908 return (Ticks - w32file_epoch);
1911 public string ToLongDateString()
1913 return ToString ("D");
1916 public string ToLongTimeString()
1918 return ToString ("T");
1921 public double ToOADate ()
1923 long t = this.Ticks;
1924 // uninitialized DateTime case
1927 // we can't reach minimum value
1928 if (t < 31242239136000000)
1929 return OAMinValue + 0.001;
1931 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1932 double result = ts.TotalDays;
1933 // t < 0 (where 599264352000000000 == 0.0d for OA)
1934 if (t < 599264352000000000) {
1935 // negative days (int) but decimals are positive
1936 double d = Math.Ceiling (result);
1937 result = d - 2 - (result - d);
1940 // we can't reach maximum value
1941 if (result >= OAMaxValue)
1942 result = OAMaxValue - 0.00000001d;
1947 public string ToShortDateString()
1949 return ToString ("d");
1952 public string ToShortTimeString()
1954 return ToString ("t");
1957 public override string ToString ()
1959 return ToString ("G", null);
1962 public string ToString (IFormatProvider provider)
1964 return ToString (null, provider);
1967 public string ToString (string format)
1969 return ToString (format, null);
1972 public string ToString (string format, IFormatProvider provider)
1974 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1976 if (format == null || format == String.Empty)
1979 bool useutc = false, use_invariant = false;
1981 if (format.Length == 1) {
1982 char fchar = format [0];
1983 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1985 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
1986 // return ToUniversalTime()._ToString (format, dfi);
1989 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
1992 // Don't convert UTC value. It just adds 'Z' for
1993 // 'u' format, for the same ticks.
1994 return DateTimeUtils.ToString (this, format, dfi);
1997 public DateTime ToLocalTime ()
1999 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2002 public DateTime ToUniversalTime()
2004 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2009 public static DateTime operator +(DateTime d, TimeSpan t)
2012 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2013 if (res < 0 || res > MAX_VALUE_TICKS){
2014 throw new ArgumentOutOfRangeException ();
2017 return new DateTime (res, d.Kind);
2018 } catch (OverflowException){
2019 throw new ArgumentOutOfRangeException ();
2023 public static bool operator ==(DateTime d1, DateTime d2)
2025 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2028 public static bool operator >(DateTime t1,DateTime t2)
2030 return ((t1.encoded & TicksMask) > (t2.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 d1, DateTime d2)
2040 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2043 public static bool operator <(DateTime t1, DateTime t2)
2045 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2048 public static bool operator <=(DateTime t1, DateTime t2)
2050 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2053 public static TimeSpan operator -(DateTime d1, DateTime d2)
2055 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2058 public static DateTime operator -(DateTime d, TimeSpan t)
2061 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2062 if (res < 0 || res > MAX_VALUE_TICKS)
2063 throw new ArgumentOutOfRangeException ();
2064 return new DateTime (res, d.Kind);
2065 } catch (OverflowException){
2066 throw new ArgumentOutOfRangeException ();
2070 bool IConvertible.ToBoolean (IFormatProvider provider)
2072 throw new InvalidCastException();
2075 byte IConvertible.ToByte (IFormatProvider provider)
2077 throw new InvalidCastException();
2081 char IConvertible.ToChar (IFormatProvider provider)
2083 throw new InvalidCastException();
2086 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2091 decimal IConvertible.ToDecimal (IFormatProvider provider)
2093 throw new InvalidCastException();
2096 double IConvertible.ToDouble (IFormatProvider provider)
2098 throw new InvalidCastException();
2101 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2103 throw new InvalidCastException();
2106 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2108 throw new InvalidCastException();
2111 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2113 throw new InvalidCastException();
2116 SByte IConvertible.ToSByte (IFormatProvider provider)
2118 throw new InvalidCastException();
2121 Single IConvertible.ToSingle (IFormatProvider provider)
2123 throw new InvalidCastException();
2126 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2128 if (targetType == null)
2129 throw new ArgumentNullException ("targetType");
2131 if (targetType == typeof (DateTime))
2133 else if (targetType == typeof (String))
2134 return this.ToString (provider);
2135 else if (targetType == typeof (Object))
2138 throw new InvalidCastException();
2141 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2143 throw new InvalidCastException();
2146 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2148 throw new InvalidCastException();
2151 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2153 throw new InvalidCastException();
2156 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2159 info.AddValue ("ticks", t);
2161 // This is the new .NET format, encodes the kind on the top bits
2162 info.AddValue ("dateData", encoded);
2166 static DateTime () {
2167 if (MonoTouchAOTHelper.FalseFlag) {
2168 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2169 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();