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;
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 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
192 // Note that these patterns cannot be followed by the time.
193 private static readonly string[] MonthDayShortFormats = new string [] {
198 private static readonly string[] DayMonthShortFormats = new string [] {
212 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
213 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
215 private static int AbsoluteDays (int year, int month, int day)
220 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
224 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
227 private int FromTicks(Which what)
229 int num400, num100, num4, numyears;
232 int[] days = daysmonth;
233 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
235 num400 = (totaldays / dp400);
236 totaldays -= num400 * dp400;
238 num100 = (totaldays / dp100);
239 if (num100 == 4) // leap
241 totaldays -= (num100 * dp100);
243 num4 = totaldays / dp4;
244 totaldays -= (num4 * dp4);
246 numyears = totaldays / 365 ;
248 if (numyears == 4) //leap
250 if (what == Which.Year )
251 return num400*400 + num100*100 + num4*4 + numyears + 1;
253 totaldays -= (numyears * 365) ;
254 if (what == Which.DayYear )
255 return totaldays + 1;
257 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
258 days = daysmonthleap;
260 while (totaldays >= days[M])
261 totaldays -= days[M++];
263 if (what == Which.Month )
269 static void InvalidTickValue (long ticks)
271 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
272 throw new ArgumentOutOfRangeException ("ticks", msg);
278 /// Constructs a DateTime for specified ticks
281 public DateTime (long ticks)
283 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
284 InvalidTickValue (ticks);
288 public DateTime (int year, int month, int day)
289 : this (year, month, day,0,0,0,0) {}
291 public DateTime (int year, int month, int day, int hour, int minute, int second)
292 : this (year, month, day, hour, minute, second, 0) {}
294 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
296 if (year < 1 || year > 9999 ||
297 month < 1 || month >12 ||
298 day < 1 || day > DaysInMonth(year, month) ||
299 hour < 0 || hour > 23 ||
300 minute < 0 || minute > 59 ||
301 second < 0 || second > 59 ||
302 millisecond < 0 || millisecond > 999)
303 throw new ArgumentOutOfRangeException ("Parameters describe an " +
304 "unrepresentable DateTime.");
306 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
309 public DateTime (int year, int month, int day, Calendar calendar)
310 : this (year, month, day, 0, 0, 0, 0, calendar)
314 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
315 : this (year, month, day, hour, minute, second, 0, calendar)
319 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
321 if (calendar == null)
322 throw new ArgumentNullException ("calendar");
323 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
326 public DateTime (long ticks, DateTimeKind kind)
328 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
329 InvalidTickValue (ticks);
330 if (kind < 0 || kind > DateTimeKind.Local)
331 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
333 encoded = ((long)kind << KindShift) | ticks;
336 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
337 : this (year, month, day, hour, minute, second)
339 if (kind < 0 || kind > DateTimeKind.Local)
340 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
341 encoded |= ((long)kind << KindShift);
344 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
345 : this (year, month, day, hour, minute, second, millisecond)
347 if (kind < 0 || kind > DateTimeKind.Local)
348 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
349 encoded |= ((long)kind << KindShift);
352 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
353 : this (year, month, day, hour, minute, second, millisecond, calendar)
355 if (kind < 0 || kind > DateTimeKind.Local)
356 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
357 encoded |= ((long)kind << KindShift);
361 // Not visible, but can be invoked during deserialization
363 DateTime (SerializationInfo info, StreamingContext context)
365 if (info.HasKey ("dateData")){
366 encoded = info.GetInt64 ("dateData");
367 } else if (info.HasKey ("ticks")){
368 encoded = info.GetInt64 ("ticks") & TicksMask;
377 public DateTime Date {
379 DateTime ret = new DateTime (Year, Month, Day);
380 ret.encoded |= encoded & KindMask;
387 return FromTicks (Which.Month);
393 return FromTicks (Which.Day);
397 public DayOfWeek DayOfWeek {
399 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
403 public int DayOfYear {
405 return FromTicks (Which.DayYear);
409 public TimeSpan TimeOfDay {
411 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
418 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
424 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
430 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
434 public int Millisecond {
436 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
440 [MethodImplAttribute(MethodImplOptions.InternalCall)]
441 internal static extern long GetTimeMonotonic ();
443 [MethodImplAttribute(MethodImplOptions.InternalCall)]
444 internal static extern long GetNow ();
447 // To reduce the time consumed by DateTime.Now, we keep
448 // the difference to map the system time into a local
449 // time into `to_local_time_span', we record the timestamp
450 // for this in `last_now'
452 static object to_local_time_span_object;
453 static long last_now;
455 public static DateTime Now {
457 long now = GetNow ();
458 DateTime dt = new DateTime (now);
460 if ((now - last_now) > TimeSpan.TicksPerMinute){
461 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
466 // This is boxed, so we avoid locking.
467 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
468 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
475 return encoded & TicksMask;
479 public static DateTime Today {
482 DateTime today = new DateTime (now.Year, now.Month, now.Day);
483 today.encoded |= ((long)DateTimeKind.Local << KindShift);
488 public static DateTime UtcNow {
490 return new DateTime (GetNow (), DateTimeKind.Utc);
496 return FromTicks (Which.Year);
500 public DateTimeKind Kind {
502 return (DateTimeKind) ((ulong)encoded >> KindShift);
508 public DateTime Add (TimeSpan value)
510 DateTime ret = AddTicks (value.Ticks);
514 public DateTime AddDays (double value)
516 return AddMilliseconds (Math.Round (value * 86400000));
519 public DateTime AddTicks (long value)
521 long res = value + (encoded & TicksMask);
522 if (res < 0 || res > MAX_VALUE_TICKS)
523 throw new ArgumentOutOfRangeException();
525 DateTime ret = new DateTime (res);
526 ret.encoded |= (encoded & KindMask);
530 public DateTime AddHours (double value)
532 return AddMilliseconds (value * 3600000);
535 public DateTime AddMilliseconds (double value)
537 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
538 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
539 throw new ArgumentOutOfRangeException();
541 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
543 return AddTicks (msticks);
546 // required to match MS implementation for OADate (OLE Automation)
547 private DateTime AddRoundedMilliseconds (double ms)
549 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
550 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
551 throw new ArgumentOutOfRangeException ();
553 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
555 return AddTicks (msticks);
558 public DateTime AddMinutes (double value)
560 return AddMilliseconds (value * 60000);
563 public DateTime AddMonths (int months)
565 int day, month, year, maxday ;
569 month = this.Month + (months % 12);
570 year = this.Year + months/12 ;
582 maxday = DaysInMonth(year, month);
586 temp = new DateTime (year, month, day);
587 temp.encoded |= encoded & KindMask;
588 return temp.Add (this.TimeOfDay);
591 public DateTime AddSeconds (double value)
593 return AddMilliseconds (value * 1000);
596 public DateTime AddYears (int value)
598 return AddMonths (value * 12);
601 public static int Compare (DateTime t1, DateTime t2)
603 long t1t = t1.encoded & TicksMask;
604 long t2t = t2.encoded & TicksMask;
614 public int CompareTo (object value)
619 if (!(value is System.DateTime))
620 throw new ArgumentException (Locale.GetText (
621 "Value is not a System.DateTime"));
623 return Compare (this, (DateTime) value);
626 public bool IsDaylightSavingTime ()
628 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
630 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
633 public int CompareTo (DateTime value)
635 return Compare (this, value);
638 public bool Equals (DateTime value)
640 return (value.encoded & TicksMask) == (encoded & TicksMask);
643 public long ToBinary ()
645 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
646 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
651 public static DateTime FromBinary (long dateData)
653 switch ((ulong)dateData >> KindShift) {
655 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
656 case 0: // Unspecified
657 return new DateTime (dateData, DateTimeKind.Unspecified);
659 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
663 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
665 return new DateTime (value.Ticks, kind);
668 public static int DaysInMonth (int year, int month)
672 if (month < 1 || month >12)
673 throw new ArgumentOutOfRangeException ();
675 if (year < 1 || year > 9999)
676 throw new ArgumentOutOfRangeException ();
678 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
682 public override bool Equals (object value)
684 if (!(value is System.DateTime))
687 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
690 public static bool Equals (DateTime t1, DateTime t2 )
692 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
695 public static DateTime FromFileTime (long fileTime)
698 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
700 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
703 public static DateTime FromFileTimeUtc (long fileTime)
706 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
708 return new DateTime (w32file_epoch + fileTime);
711 public static DateTime FromOADate (double d)
713 // An OLE Automation date is implemented as a floating-point number
714 // whose value is the number of days from midnight, 30 December 1899.
716 // d must be negative 657435.0 through positive 2958466.0.
717 if ((d <= OAMinValue) || (d >= OAMaxValue))
718 throw new ArgumentException ("d", "[-657435,2958466]");
720 DateTime dt = new DateTime (ticks18991230);
722 Double days = Math.Ceiling (d);
723 // integer part is the number of days (negative)
724 dt = dt.AddRoundedMilliseconds (days * 86400000);
725 // but decimals are the number of hours (in days fractions) and positive
726 Double hours = (days - d);
727 dt = dt.AddRoundedMilliseconds (hours * 86400000);
730 dt = dt.AddRoundedMilliseconds (d * 86400000);
736 public string[] GetDateTimeFormats()
738 return GetDateTimeFormats (CultureInfo.CurrentCulture);
741 public string[] GetDateTimeFormats(char format)
743 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
744 throw new FormatException ("Invalid format character.");
745 string[] result = new string[1];
746 result[0] = this.ToString(format.ToString());
750 public string[] GetDateTimeFormats(IFormatProvider provider)
752 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
753 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
754 ArrayList al = new ArrayList ();
755 foreach (char c in "dDgGfFmMrRstTuUyY")
756 al.AddRange (GetDateTimeFormats (c, info));
757 return al.ToArray (typeof (string)) as string [];
760 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
762 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
763 throw new FormatException ("Invalid format character.");
765 // LAMESPEC: There is NO assurance that 'U' ALWAYS
766 // euqals to 'F', but since we have to iterate all
767 // the pattern strings, we cannot just use
768 // ToString("U", provider) here. I believe that the
769 // method's behavior cannot be formalized.
770 bool adjustutc = false;
779 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
780 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
783 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
785 string [] results = new string [patterns.Length];
786 DateTime val = adjustutc ? ToUniversalTime () : this;
787 for (int i = 0; i < results.Length; i++)
788 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
792 public override int GetHashCode ()
794 return (int) encoded;
797 public TypeCode GetTypeCode ()
799 return TypeCode.DateTime;
802 public static bool IsLeapYear (int year)
804 if (year < 1 || year > 9999)
805 throw new ArgumentOutOfRangeException ();
806 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
809 public static DateTime Parse (string s)
811 return Parse (s, null);
814 public static DateTime Parse (string s, IFormatProvider provider)
816 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
819 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
822 throw new ArgumentNullException ("s");
826 Exception exception = null;
827 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
833 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
835 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
836 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
838 dto = new DateTimeOffset (0, TimeSpan.Zero);
839 if (s == null || s.Length == 0) {
840 if (setExceptionOnError)
841 exception = new FormatException (formatExceptionMessage);
846 if (provider == null)
847 provider = CultureInfo.CurrentCulture;
848 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
850 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
851 string[] allDateFormats = YearMonthDayFormats (dfi, setExceptionOnError, ref exception);
852 if (allDateFormats == null){
857 bool longYear = false;
858 for (int i = 0; i < allDateFormats.Length; i++) {
859 string firstPart = allDateFormats [i];
860 bool incompleteFormat = false;
861 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
864 if (!incompleteFormat)
867 for (int j = 0; j < ParseTimeFormats.Length; j++) {
868 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
876 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
877 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
878 if (dayIndex == -1 || monthIndex == -1){
880 if (setExceptionOnError)
881 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
884 bool is_day_before_month = dayIndex < monthIndex;
885 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
886 for (int i = 0; i < monthDayFormats.Length; i++) {
887 bool incompleteFormat = false;
888 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
892 for (int j = 0; j < ParseTimeFormats.Length; j++) {
893 string firstPart = ParseTimeFormats [j];
894 bool incompleteFormat = false;
895 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
897 if (!incompleteFormat)
900 for (int i = 0; i < monthDayFormats.Length; i++) {
901 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
904 for (int i = 0; i < allDateFormats.Length; i++) {
905 string dateFormat = allDateFormats [i];
906 if (dateFormat[dateFormat.Length - 1] == 'T')
907 continue; // T formats must be before the time part
908 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
913 // Try as a last resort all the patterns
914 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
917 if (!setExceptionOnError)
920 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
921 exception = new FormatException (formatExceptionMessage);
925 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
927 return ParseExact (s, format, provider, DateTimeStyles.None);
930 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi, bool setExceptionOnError, ref Exception exc)
932 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
933 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
934 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
935 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1){
936 if (setExceptionOnError)
937 exc = new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
941 if (yearIndex < monthIndex)
942 if (monthIndex < dayIndex)
943 return ParseYearMonthDayFormats;
944 else if (yearIndex < dayIndex)
945 return ParseYearDayMonthFormats;
947 // The year cannot be between the date and the month
948 if (setExceptionOnError)
949 exc = new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
952 else if (dayIndex < monthIndex)
953 return ParseDayMonthYearFormats;
954 else if (dayIndex < yearIndex)
955 return ParseMonthDayYearFormats;
957 // The year cannot be between the month and the date
958 if (setExceptionOnError)
959 exc = new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
964 private static int _ParseNumber (string s, int valuePos,
978 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
979 if (!Char.IsDigit (s[i]))
985 digits = real_digits;
987 if (digits < min_digits) {
992 if (s.Length - valuePos < digits) {
997 for (i = valuePos; i < digits + valuePos; i++) {
999 if (!Char.IsDigit (c)) {
1004 number = number * 10 + (byte) (c - '0');
1007 num_parsed = digits;
1011 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1013 // FIXME: I know this is somehow lame code. Probably
1014 // it should iterate all the enum value and return
1015 // the longest match. However right now I don't see
1016 // anything but "1" and "10" - "12" that might match
1017 // two or more values. (They are only abbrev month
1018 // names, so do reverse order search). See bug #80094.
1019 for (int i = values.Length - 1; i >= 0; i--) {
1020 if (!exact && invValues [i].Length > values[i].Length) {
1021 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1023 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1027 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1029 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1038 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1041 maxlength = value.Length;
1043 if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1044 num_parsed = maxlength;
1052 // Note that in case of Parse (exact == false) we check both for AM/PM
1053 // and the culture spcific AM/PM strings.
1054 private static bool _ParseAmPm(string s,
1057 DateTimeFormatInfo dfi,
1066 if (!IsLetter (s, valuePos)) {
1067 if (dfi.AMDesignator != "")
1074 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1075 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1076 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1078 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1079 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1080 if (exact || num_parsed != 0)
1088 // Note that in case of Parse (exact == false) we check both for ':'
1089 // and the culture spcific TimeSperator
1090 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1092 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1093 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1096 // Accept any character for DateSeparator, except TimeSeparator,
1097 // a digit or a letter.
1098 // Not documented, but seems to be MS behaviour here. See bug 54047.
1099 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1102 if (exact && s [sPos] != '/')
1105 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1106 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1113 private static bool IsLetter (string s, int pos)
1115 return pos < s.Length && Char.IsLetter (s [pos]);
1118 // To implement better DateTime.Parse we use two format strings one
1119 // for Date and one for Time. This allows us to define two different
1120 // arrays of formats for Time and Dates and to combine them more or less
1121 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1122 private static bool _DoParse (string s,
1126 out DateTime result,
1127 out DateTimeOffset dto,
1128 DateTimeFormatInfo dfi,
1129 DateTimeStyles style,
1130 bool firstPartIsDate,
1131 ref bool incompleteFormat,
1134 bool useutc = false;
1135 bool use_invariant = false;
1136 bool sloppy_parsing = false;
1137 dto = new DateTimeOffset (0, TimeSpan.Zero);
1138 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1139 incompleteFormat = false;
1141 string format = firstPart;
1142 bool afterTFormat = false;
1143 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1144 if (format.Length == 1)
1145 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1147 result = new DateTime (0);
1154 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1155 format = format.TrimStart (null);
1157 s = s.TrimStart (null); // it could be optimized, but will make little good.
1160 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1161 format = format.TrimEnd (null);
1162 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1168 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1169 sloppy_parsing = true;
1171 string chars = format;
1172 int len = format.Length, pos = 0, num = 0;
1176 int day = -1, dayofweek = -1, month = -1, year = -1;
1177 int hour = -1, minute = -1, second = -1;
1178 double fractionalSeconds = -1;
1180 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1181 bool isFirstPart = true;
1185 if (valuePos == s.Length)
1189 if (flexibleTwoPartsParsing && pos + num == 0)
1191 bool isLetter = IsLetter(s, valuePos);
1193 if (s [valuePos] == 'Z')
1196 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1197 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1198 valuePos += num_parsed;
1203 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1204 if (IsLetter (s, valuePos + num_parsed))
1206 else if (num_parsed > 0) {
1207 valuePos += num_parsed;
1212 if (!afterTFormat && dayofweek == -1 && isLetter) {
1213 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1214 if (dayofweek == -1)
1215 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1216 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1217 valuePos += num_parsed;
1224 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1231 if (pos + num >= len)
1233 if (flexibleTwoPartsParsing && num == 0) {
1234 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1235 if (!isFirstPart && format == "")
1240 format = secondPart;
1245 isFirstPart = false;
1251 bool leading_zeros = true;
1253 if (chars[pos] == '\'') {
1255 while (pos+num < len) {
1256 if (chars[pos+num] == '\'')
1259 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1269 } else if (chars[pos] == '"') {
1271 while (pos+num < len) {
1272 if (chars[pos+num] == '"')
1275 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1285 } else if (chars[pos] == '\\') {
1290 if (s [valuePos] != chars [pos])
1296 } else if (chars[pos] == '%') {
1299 } else if (char.IsWhiteSpace (s [valuePos]) ||
1300 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1303 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1304 if (!Char.IsWhiteSpace (chars[pos]))
1311 while (ws < s.Length) {
1312 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1319 while (ws < chars.Length) {
1320 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1326 // A whitespace may match a '/' in the pattern.
1327 if (!exact && pos < chars.Length && chars[pos] == '/')
1328 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1333 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1341 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1344 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1346 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1348 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1350 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1356 if (flexibleTwoPartsParsing) {
1358 if (num == 0 || num == 3)
1359 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1360 if (num > 1 && num_parsed == -1)
1361 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1362 if (num > 1 && num_parsed == -1)
1363 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1368 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1370 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1372 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1374 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1381 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1382 } else if (num < 3) {
1383 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1385 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1386 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1388 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1389 longYear = (ly > 9999);
1394 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1395 if (num_parsed <= 2)
1396 year += (year < 30) ? 2000 : 1900;
1402 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1404 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1413 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1416 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1418 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1429 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1431 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1441 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1443 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1450 leading_zeros = false;
1453 if (num > 6 || fractionalSeconds != -1)
1455 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1456 if (num_parsed == -1)
1458 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1461 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1468 if (s [valuePos] == '+')
1470 else if (s [valuePos] == '-')
1477 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1479 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1481 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1482 valuePos += num_parsed;
1487 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1488 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1489 valuePos += num_parsed;
1490 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1494 else if (!flexibleTwoPartsParsing)
1501 if (s [valuePos] == 'Z') {
1505 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1508 if (s [valuePos] == '+')
1510 else if (s [valuePos] == '-')
1515 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1516 valuePos += num_parsed;
1520 if (Char.IsDigit (s [valuePos]))
1522 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1524 valuePos += num_parsed;
1526 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1533 // LAMESPEC: This should be part of UTCpattern
1534 // string and thus should not be considered here.
1536 // Note that 'Z' is not defined as a pattern
1537 // character. Keep it for X509 certificate
1538 // verification. Also, "Z" != "'Z'" under MS.NET
1539 // ("'Z'" is just literal; handled above)
1541 if (s [valuePos] != 'Z')
1548 if (s [valuePos] != 'G')
1551 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1552 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1553 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1565 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1569 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1575 if (s [valuePos] != chars [pos])
1586 valuePos += num_parsed;
1588 if (!exact && !flexibleTwoPartsParsing) {
1589 switch (chars [pos]) {
1595 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1596 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1604 pos = pos + num + 1;
1608 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1610 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1613 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1619 if (s.Length > valuePos) // extraneous tail.
1624 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1626 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1628 incompleteFormat = true;
1639 if (fractionalSeconds == -1)
1640 fractionalSeconds = 0;
1642 // If no date was given
1643 if ((day == -1) && (month == -1) && (year == -1)) {
1644 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1649 day = DateTime.Today.Day;
1650 month = DateTime.Today.Month;
1651 year = DateTime.Today.Year;
1660 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1663 year = DateTime.Today.Year;
1666 if (ampm == 0 && hour == 12)
1669 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1672 // For anything out of range
1674 if (year < 1 || year > 9999 ||
1675 month < 1 || month >12 ||
1676 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1677 hour < 0 || hour > 23 ||
1678 minute < 0 || minute > 59 ||
1679 second < 0 || second > 59)
1682 result = new DateTime (year, month, day, hour, minute, second, 0);
1683 result = result.AddSeconds(fractionalSeconds);
1685 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1689 if (result != DateTime.MinValue) {
1691 dto = new DateTimeOffset (result);
1692 } catch { } // We handle this error in DateTimeOffset.Parse
1700 tzoffset = -tzoffset;
1701 tzoffmin = -tzoffmin;
1704 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1705 } catch {} // We handle this error in DateTimeOffset.Parse
1707 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1710 long newticks = (result - dto.Offset).Ticks;
1712 newticks += TimeSpan.TicksPerDay;
1713 result = new DateTime (newticks, DateTimeKind.Utc);
1714 if ((style & DateTimeStyles.RoundtripKind) != 0)
1715 result = result.ToLocalTime ();
1716 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1717 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1718 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1719 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1721 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1722 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1723 if (adjustToUniversal)
1724 result = result.ToUniversalTime ();
1725 else if (adjustToLocal)
1726 result = result.ToLocalTime ();
1732 public static DateTime ParseExact (string s, string format,
1733 IFormatProvider provider, DateTimeStyles style)
1736 throw new ArgumentNullException ("format");
1738 string [] formats = new string [1];
1739 formats[0] = format;
1741 return ParseExact (s, formats, provider, style);
1744 public static DateTime ParseExact (string s, string[] formats,
1745 IFormatProvider provider,
1746 DateTimeStyles style)
1748 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1751 throw new ArgumentNullException ("s");
1752 if (formats == null)
1753 throw new ArgumentNullException ("formats");
1754 if (formats.Length == 0)
1755 throw new FormatException ("Format specifier was invalid.");
1758 bool longYear = false;
1760 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1765 private static void CheckStyle (DateTimeStyles style)
1767 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1769 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1770 (style & DateTimeStyles.AssumeUniversal) != 0)
1771 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
1773 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1774 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1777 public static bool TryParse (string s, out DateTime result)
1781 Exception exception = null;
1784 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1791 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1795 Exception exception = null;
1798 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1805 public static bool TryParseExact (string s, string format,
1806 IFormatProvider provider,
1807 DateTimeStyles style,
1808 out DateTime result)
1811 formats = new string [1];
1812 formats[0] = format;
1814 return TryParseExact (s, formats, provider, style, out result);
1817 public static bool TryParseExact (string s, string[] formats,
1818 IFormatProvider provider,
1819 DateTimeStyles style,
1820 out DateTime result)
1823 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1825 bool longYear = false;
1827 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1834 private static bool ParseExact (string s, string [] formats,
1835 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1836 bool exact, ref bool longYear,
1837 bool setExceptionOnError, ref Exception exception)
1840 bool incompleteFormat = false;
1841 for (i = 0; i < formats.Length; i++)
1844 string format = formats[i];
1845 if (format == null || format == String.Empty)
1849 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1855 if (setExceptionOnError)
1856 exception = new FormatException ("Invalid format string");
1857 ret = DateTime.MinValue;
1861 public TimeSpan Subtract (DateTime value)
1863 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1866 public DateTime Subtract(TimeSpan value)
1870 newticks = Ticks - value.Ticks;
1871 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1872 throw new ArgumentOutOfRangeException ();
1873 DateTime ret = new DateTime (newticks);
1874 ret.encoded |= (encoded & KindMask);
1878 public long ToFileTime()
1880 DateTime universalTime = ToUniversalTime();
1882 if (universalTime.Ticks < w32file_epoch) {
1883 throw new ArgumentOutOfRangeException("file time is not valid");
1886 return(universalTime.Ticks - w32file_epoch);
1889 public long ToFileTimeUtc()
1891 if (Ticks < w32file_epoch) {
1892 throw new ArgumentOutOfRangeException("file time is not valid");
1895 return (Ticks - w32file_epoch);
1898 public string ToLongDateString()
1900 return ToString ("D");
1903 public string ToLongTimeString()
1905 return ToString ("T");
1908 public double ToOADate ()
1910 long t = this.Ticks;
1911 // uninitialized DateTime case
1914 // we can't reach minimum value
1915 if (t < 31242239136000000)
1916 return OAMinValue + 0.001;
1918 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1919 double result = ts.TotalDays;
1920 // t < 0 (where 599264352000000000 == 0.0d for OA)
1921 if (t < 599264352000000000) {
1922 // negative days (int) but decimals are positive
1923 double d = Math.Ceiling (result);
1924 result = d - 2 - (result - d);
1927 // we can't reach maximum value
1928 if (result >= OAMaxValue)
1929 result = OAMaxValue - 0.00000001d;
1934 public string ToShortDateString()
1936 return ToString ("d");
1939 public string ToShortTimeString()
1941 return ToString ("t");
1944 public override string ToString ()
1946 return ToString ("G", null);
1949 public string ToString (IFormatProvider provider)
1951 return ToString (null, provider);
1954 public string ToString (string format)
1956 return ToString (format, null);
1959 public string ToString (string format, IFormatProvider provider)
1961 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1963 if (format == null || format == String.Empty)
1966 bool useutc = false, use_invariant = false;
1968 if (format.Length == 1) {
1969 char fchar = format [0];
1970 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1972 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
1973 // return ToUniversalTime()._ToString (format, dfi);
1976 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
1979 // Don't convert UTC value. It just adds 'Z' for
1980 // 'u' format, for the same ticks.
1981 return DateTimeUtils.ToString (this, format, dfi);
1984 public DateTime ToLocalTime ()
1986 return TimeZone.CurrentTimeZone.ToLocalTime (this);
1989 public DateTime ToUniversalTime()
1991 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
1996 public static DateTime operator +(DateTime d, TimeSpan t)
1999 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2000 if (res < 0 || res > MAX_VALUE_TICKS){
2001 throw new ArgumentOutOfRangeException ();
2004 return new DateTime (res, d.Kind);
2005 } catch (OverflowException){
2006 throw new ArgumentOutOfRangeException ();
2010 public static bool operator ==(DateTime d1, DateTime d2)
2012 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2015 public static bool operator >(DateTime t1,DateTime t2)
2017 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2020 public static bool operator >=(DateTime t1,DateTime t2)
2022 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2025 public static bool operator !=(DateTime d1, DateTime d2)
2027 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2030 public static bool operator <(DateTime t1, DateTime t2)
2032 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2035 public static bool operator <=(DateTime t1, DateTime t2)
2037 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2040 public static TimeSpan operator -(DateTime d1, DateTime d2)
2042 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2045 public static DateTime operator -(DateTime d, TimeSpan t)
2048 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2049 if (res < 0 || res > MAX_VALUE_TICKS)
2050 throw new ArgumentOutOfRangeException ();
2051 return new DateTime (res, d.Kind);
2052 } catch (OverflowException){
2053 throw new ArgumentOutOfRangeException ();
2057 bool IConvertible.ToBoolean (IFormatProvider provider)
2059 throw new InvalidCastException();
2062 byte IConvertible.ToByte (IFormatProvider provider)
2064 throw new InvalidCastException();
2068 char IConvertible.ToChar (IFormatProvider provider)
2070 throw new InvalidCastException();
2073 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2078 decimal IConvertible.ToDecimal (IFormatProvider provider)
2080 throw new InvalidCastException();
2083 double IConvertible.ToDouble (IFormatProvider provider)
2085 throw new InvalidCastException();
2088 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2090 throw new InvalidCastException();
2093 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2095 throw new InvalidCastException();
2098 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2100 throw new InvalidCastException();
2103 SByte IConvertible.ToSByte (IFormatProvider provider)
2105 throw new InvalidCastException();
2108 Single IConvertible.ToSingle (IFormatProvider provider)
2110 throw new InvalidCastException();
2113 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2115 if (targetType == null)
2116 throw new ArgumentNullException ("targetType");
2118 if (targetType == typeof (DateTime))
2120 else if (targetType == typeof (String))
2121 return this.ToString (provider);
2122 else if (targetType == typeof (Object))
2125 throw new InvalidCastException();
2128 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2130 throw new InvalidCastException();
2133 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2135 throw new InvalidCastException();
2138 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2140 throw new InvalidCastException();
2143 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2146 info.AddValue ("ticks", t);
2148 // This is the new .NET format, encodes the kind on the top bits
2149 info.AddValue ("dateData", encoded);
2153 static DateTime () {
2154 if (MonoTouchAOTHelper.FalseFlag) {
2155 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2156 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();