5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
8 // Marek Safar (marek.safar@gmail.com)
10 // (C) 2001 Marcel Narings
11 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections.Generic;
35 using System.Globalization;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
44 /// The DateTime structure represents dates and time ranging from
45 /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
49 [StructLayout (LayoutKind.Auto)]
50 public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
53 // Encodes the DateTime in 64 bits, top two bits contain the DateTimeKind,
54 // the rest contains the 62 bit value for the ticks. This reduces the
55 // memory usage from 16 to 8 bytes, see bug: 592221. This also fixes the
56 // 622127 issue and simplifies the code in reflection.c to encode DateTimes
59 const long TicksMask = 0x3fffffffffffffff;
60 const long KindMask = unchecked ((long) 0xc000000000000000);
61 const int KindShift = 62;
63 private const int dp400 = 146097;
64 private const int dp100 = 36524;
65 private const int dp4 = 1461;
67 // w32 file time starts counting from 1/1/1601 00:00 GMT
68 // which is the constant ticks from the .NET epoch
69 private const long w32file_epoch = 504911232000000000L;
71 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
72 // -- Microsoft .NET has this value.
73 private const long MAX_VALUE_TICKS = 3155378975999999999L;
76 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
79 internal const long UnixEpoch = 621355968000000000L;
81 // for OLE Automation dates
82 private const long ticks18991230 = 599264352000000000L;
83 private const double OAMinValue = -657435.0d;
84 private const double OAMaxValue = 2958466.0d;
86 public static readonly DateTime MaxValue = new DateTime (3155378975999999999);
87 public static readonly DateTime MinValue = new DateTime (0);
89 // DateTime.Parse patterns
90 // Patterns are divided to date and time patterns. The algorithm will
91 // try combinations of these patterns. The algorithm also looks for
92 // day of the week, AM/PM GMT and Z independently of the patterns.
93 private static readonly string[] ParseTimeFormats = new string [] {
109 "H tt", // Specifies AM to disallow '8'.
110 "H'\u6642'm'\u5206's'\u79D2'",
113 // DateTime.Parse date patterns extend ParseExact patterns as follows:
114 // MMM - month short name or month full name
115 // MMMM - month number or short name or month full name
117 // Parse behaves differently according to the ShorDatePattern of the
118 // DateTimeFormatInfo. The following define the date patterns for
119 // different orders of day, month and year in ShorDatePattern.
120 // Note that the year cannot go between the day and the month.
121 private static readonly string[] ParseYearDayMonthFormats = new string [] {
124 "yyyy'\u5E74'M'\u6708'd'\u65E5",
137 private static readonly string[] ParseYearMonthDayFormats = new string [] {
140 "yyyy'\u5E74'M'\u6708'd'\u65E5",
154 private static readonly string[] ParseDayMonthYearFormats = new string [] {
157 "yyyy'\u5E74'M'\u6708'd'\u65E5",
174 private static readonly string[] ParseMonthDayYearFormats = new string [] {
177 "yyyy'\u5E74'M'\u6708'd'\u65E5",
194 private static readonly string[] ParseGenericYearMonthDayFormats = new string [] {
199 "yyyy'\u5E74'M'\u6708'd'\u65E5",
204 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
205 // Note that these patterns cannot be followed by the time.
206 private static readonly string[] MonthDayShortFormats = new string [] {
211 private static readonly string[] DayMonthShortFormats = new string [] {
217 private static readonly string[] ExoticAndNonStandardFormats = new string[] {
229 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
230 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
232 private static int AbsoluteDays (int year, int month, int day)
237 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
241 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
244 private int FromTicks(Which what)
246 int num400, num100, num4, numyears;
249 int[] days = daysmonth;
250 int totaldays = (int) ((encoded & TicksMask) / TimeSpan.TicksPerDay);
252 num400 = (totaldays / dp400);
253 totaldays -= num400 * dp400;
255 num100 = (totaldays / dp100);
256 if (num100 == 4) // leap
258 totaldays -= (num100 * dp100);
260 num4 = totaldays / dp4;
261 totaldays -= (num4 * dp4);
263 numyears = totaldays / 365 ;
265 if (numyears == 4) //leap
267 if (what == Which.Year )
268 return num400*400 + num100*100 + num4*4 + numyears + 1;
270 totaldays -= (numyears * 365) ;
271 if (what == Which.DayYear )
272 return totaldays + 1;
274 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
275 days = daysmonthleap;
277 while (totaldays >= days[M])
278 totaldays -= days[M++];
280 if (what == Which.Month )
286 static void InvalidTickValue (long ticks)
288 string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
289 throw new ArgumentOutOfRangeException ("ticks", msg);
295 /// Constructs a DateTime for specified ticks
298 public DateTime (long ticks)
300 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
301 InvalidTickValue (ticks);
305 public DateTime (int year, int month, int day)
306 : this (year, month, day,0,0,0,0) {}
308 public DateTime (int year, int month, int day, int hour, int minute, int second)
309 : this (year, month, day, hour, minute, second, 0) {}
311 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
313 if (year < 1 || year > 9999 ||
314 month < 1 || month >12 ||
315 day < 1 || day > DaysInMonth(year, month) ||
316 hour < 0 || hour > 23 ||
317 minute < 0 || minute > 59 ||
318 second < 0 || second > 59 ||
319 millisecond < 0 || millisecond > 999)
320 throw new ArgumentOutOfRangeException ("Parameters describe an " +
321 "unrepresentable DateTime.");
323 encoded = new TimeSpan (AbsoluteDays (year,month,day), hour, minute, second, millisecond).Ticks;
326 public DateTime (int year, int month, int day, Calendar calendar)
327 : this (year, month, day, 0, 0, 0, 0, calendar)
331 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
332 : this (year, month, day, hour, minute, second, 0, calendar)
336 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
338 if (calendar == null)
339 throw new ArgumentNullException ("calendar");
340 encoded = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
343 public DateTime (long ticks, DateTimeKind kind)
345 if (ticks < 0 || ticks > MAX_VALUE_TICKS)
346 InvalidTickValue (ticks);
347 if (kind < 0 || kind > DateTimeKind.Local)
348 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
350 encoded = ((long)kind << KindShift) | ticks;
353 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
354 : this (year, month, day, hour, minute, second)
356 if (kind < 0 || kind > DateTimeKind.Local)
357 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
358 encoded |= ((long)kind << KindShift);
361 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
362 : this (year, month, day, hour, minute, second, millisecond)
364 if (kind < 0 || kind > DateTimeKind.Local)
365 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
366 encoded |= ((long)kind << KindShift);
369 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
370 : this (year, month, day, hour, minute, second, millisecond, calendar)
372 if (kind < 0 || kind > DateTimeKind.Local)
373 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
374 encoded |= ((long)kind << KindShift);
378 // Not visible, but can be invoked during deserialization
380 DateTime (SerializationInfo info, StreamingContext context)
382 if (info.HasKey ("dateData")){
383 encoded = (Int64)info.GetUInt64 ("dateData");
384 } else if (info.HasKey ("ticks")){
385 encoded = info.GetInt64 ("ticks") & TicksMask;
394 public DateTime Date {
396 DateTime ret = new DateTime (Year, Month, Day);
397 ret.encoded |= encoded & KindMask;
404 return FromTicks (Which.Month);
410 return FromTicks (Which.Day);
414 public DayOfWeek DayOfWeek {
416 return (DayOfWeek) ((((encoded & TicksMask)/TimeSpan.TicksPerDay)+1) % 7);
420 public int DayOfYear {
422 return FromTicks (Which.DayYear);
426 public TimeSpan TimeOfDay {
428 return new TimeSpan ((encoded & TicksMask) % TimeSpan.TicksPerDay);
435 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerDay / TimeSpan.TicksPerHour);
441 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute);
447 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond);
451 public int Millisecond {
453 return (int) ((encoded & TicksMask) % TimeSpan.TicksPerSecond / TimeSpan.TicksPerMillisecond);
457 [MethodImplAttribute(MethodImplOptions.InternalCall)]
458 internal static extern long GetTimeMonotonic ();
460 [MethodImplAttribute(MethodImplOptions.InternalCall)]
461 internal static extern long GetNow ();
464 // To reduce the time consumed by DateTime.Now, we keep
465 // the difference to map the system time into a local
466 // time into `to_local_time_span', we record the timestamp
467 // for this in `last_now'
469 static object to_local_time_span_object;
470 static long last_now;
472 public static DateTime Now {
474 long now = GetNow ();
475 DateTime dt = new DateTime (now);
477 if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
478 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
483 // This is boxed, so we avoid locking.
484 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
485 ret.encoded |= ((long)DateTimeKind.Local << KindShift);
492 return encoded & TicksMask;
496 public static DateTime Today {
499 DateTime today = new DateTime (now.Year, now.Month, now.Day);
500 today.encoded |= ((long)DateTimeKind.Local << KindShift);
505 public static DateTime UtcNow {
507 return new DateTime (GetNow (), DateTimeKind.Utc);
513 return FromTicks (Which.Year);
517 public DateTimeKind Kind {
519 return (DateTimeKind) ((ulong)encoded >> KindShift);
525 public DateTime Add (TimeSpan value)
527 DateTime ret = AddTicks (value.Ticks);
531 public DateTime AddDays (double value)
533 return AddMilliseconds (Math.Round (value * 86400000));
536 public DateTime AddTicks (long value)
538 long res = value + (encoded & TicksMask);
539 if (res < 0 || res > MAX_VALUE_TICKS)
540 throw new ArgumentOutOfRangeException();
542 DateTime ret = new DateTime (res);
543 ret.encoded |= (encoded & KindMask);
547 public DateTime AddHours (double value)
549 return AddMilliseconds (value * 3600000);
552 public DateTime AddMilliseconds (double value)
554 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
555 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
556 throw new ArgumentOutOfRangeException();
558 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
560 return AddTicks (msticks);
563 // required to match MS implementation for OADate (OLE Automation)
564 private DateTime AddRoundedMilliseconds (double ms)
566 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
567 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
568 throw new ArgumentOutOfRangeException ();
570 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
572 return AddTicks (msticks);
575 public DateTime AddMinutes (double value)
577 return AddMilliseconds (value * 60000);
580 public DateTime AddMonths (int months)
582 int day, month, year, maxday ;
586 month = this.Month + (months % 12);
587 year = this.Year + months/12 ;
599 maxday = DaysInMonth(year, month);
603 temp = new DateTime (year, month, day);
604 temp.encoded |= encoded & KindMask;
605 return temp.Add (this.TimeOfDay);
608 public DateTime AddSeconds (double value)
610 return AddMilliseconds (value * 1000);
613 public DateTime AddYears (int value)
615 return AddMonths (value * 12);
618 public static int Compare (DateTime t1, DateTime t2)
620 long t1t = t1.encoded & TicksMask;
621 long t2t = t2.encoded & TicksMask;
631 public int CompareTo (object value)
636 if (!(value is System.DateTime))
637 throw new ArgumentException (Locale.GetText (
638 "Value is not a System.DateTime"));
640 return Compare (this, (DateTime) value);
643 public bool IsDaylightSavingTime ()
645 if ((int)((ulong)encoded >> KindShift) == (int) DateTimeKind.Utc)
647 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
650 public int CompareTo (DateTime value)
652 return Compare (this, value);
655 public bool Equals (DateTime value)
657 return (value.encoded & TicksMask) == (encoded & TicksMask);
660 public long ToBinary ()
662 if ((encoded & ((long)DateTimeKind.Local << KindShift)) != 0)
663 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
668 public static DateTime FromBinary (long dateData)
670 switch ((ulong)dateData >> KindShift) {
672 return new DateTime (dateData & TicksMask, DateTimeKind.Utc);
673 case 0: // Unspecified
674 return new DateTime (dateData, DateTimeKind.Unspecified);
676 return new DateTime (dateData & TicksMask, DateTimeKind.Utc).ToLocalTime ();
680 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
682 return new DateTime (value.Ticks, kind);
685 public static int DaysInMonth (int year, int month)
689 if (month < 1 || month >12)
690 throw new ArgumentOutOfRangeException ();
692 if (year < 1 || year > 9999)
693 throw new ArgumentOutOfRangeException ();
695 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
699 public override bool Equals (object value)
701 if (!(value is System.DateTime))
704 return (((DateTime) value).encoded & TicksMask) == (encoded & TicksMask);
707 public static bool Equals (DateTime t1, DateTime t2 )
709 return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
712 public static DateTime FromFileTime (long fileTime)
715 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
717 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
720 public static DateTime FromFileTimeUtc (long fileTime)
723 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
725 return new DateTime (w32file_epoch + fileTime, DateTimeKind.Utc);
728 public static DateTime FromOADate (double d)
730 // An OLE Automation date is implemented as a floating-point number
731 // whose value is the number of days from midnight, 30 December 1899.
733 // d must be negative 657435.0 through positive 2958466.0.
734 if ((d <= OAMinValue) || (d >= OAMaxValue))
735 throw new ArgumentException ("d", "[-657435,2958466]");
737 DateTime dt = new DateTime (ticks18991230);
739 Double days = Math.Ceiling (d);
740 // integer part is the number of days (negative)
741 dt = dt.AddRoundedMilliseconds (days * 86400000);
742 // but decimals are the number of hours (in days fractions) and positive
743 Double hours = (days - d);
744 dt = dt.AddRoundedMilliseconds (hours * 86400000);
747 dt = dt.AddRoundedMilliseconds (d * 86400000);
753 public string[] GetDateTimeFormats()
755 return GetDateTimeFormats (CultureInfo.CurrentCulture);
758 public string[] GetDateTimeFormats(char format)
760 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
761 throw new FormatException ("Invalid format character.");
762 string[] result = new string[1];
763 result[0] = this.ToString(format.ToString());
767 public string[] GetDateTimeFormats(IFormatProvider provider)
769 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
770 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
771 var l = new List<string> ();
772 foreach (char c in "dDgGfFmMrRstTuUyY")
773 l.AddRange (GetDateTimeFormats (c, info));
777 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
779 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
780 throw new FormatException ("Invalid format character.");
782 // LAMESPEC: There is NO assurance that 'U' ALWAYS
783 // euqals to 'F', but since we have to iterate all
784 // the pattern strings, we cannot just use
785 // ToString("U", provider) here. I believe that the
786 // method's behavior cannot be formalized.
787 bool adjustutc = false;
796 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
797 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
800 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
802 string [] results = new string [patterns.Length];
803 DateTime val = adjustutc ? ToUniversalTime () : this;
804 for (int i = 0; i < results.Length; i++)
805 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
809 public override int GetHashCode ()
811 return (int) encoded;
814 public TypeCode GetTypeCode ()
816 return TypeCode.DateTime;
819 public static bool IsLeapYear (int year)
821 if (year < 1 || year > 9999)
822 throw new ArgumentOutOfRangeException ();
823 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
826 public static DateTime Parse (string s)
828 return Parse (s, null);
831 public static DateTime Parse (string s, IFormatProvider provider)
833 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
836 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
839 throw new ArgumentNullException ("s");
843 Exception exception = null;
844 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
850 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
852 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
853 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
855 dto = new DateTimeOffset (0, TimeSpan.Zero);
856 if (s == null || s.Length == 0) {
857 if (setExceptionOnError)
858 exception = new FormatException (formatExceptionMessage);
863 if (provider == null)
864 provider = CultureInfo.CurrentCulture;
865 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
867 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
868 string[] allDateFormats = YearMonthDayFormats (dfi);
869 if (allDateFormats == null){
874 bool longYear = false;
875 for (int i = 0; i < allDateFormats.Length; i++) {
876 string firstPart = allDateFormats [i];
877 bool incompleteFormat = false;
878 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
881 if (!incompleteFormat)
884 for (int j = 0; j < ParseTimeFormats.Length; j++) {
885 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
893 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
894 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
895 if (dayIndex == -1 || monthIndex == -1){
897 if (setExceptionOnError)
898 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
901 bool is_day_before_month = dayIndex < monthIndex;
902 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
903 for (int i = 0; i < monthDayFormats.Length; i++) {
904 bool incompleteFormat = false;
905 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
909 for (int j = 0; j < ParseTimeFormats.Length; j++) {
910 string firstPart = ParseTimeFormats [j];
911 bool incompleteFormat = false;
912 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
914 if (!incompleteFormat)
917 for (int i = 0; i < monthDayFormats.Length; i++) {
918 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
921 for (int i = 0; i < allDateFormats.Length; i++) {
922 string dateFormat = allDateFormats [i];
923 if (dateFormat[dateFormat.Length - 1] == 'T')
924 continue; // T formats must be before the time part
925 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
930 // Try as a last resort all the patterns
931 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
934 if (ParseExact (s, ExoticAndNonStandardFormats, dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
937 if (!setExceptionOnError)
940 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
941 exception = new FormatException (formatExceptionMessage);
945 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
947 return ParseExact (s, format, provider, DateTimeStyles.None);
950 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
952 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
953 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
954 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
955 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
956 return ParseGenericYearMonthDayFormats;
958 if (yearIndex < monthIndex)
959 if (monthIndex < dayIndex)
960 return ParseYearMonthDayFormats;
961 else if (yearIndex < dayIndex)
962 return ParseYearDayMonthFormats;
964 // The year cannot be between the date and the month
965 return ParseGenericYearMonthDayFormats;
967 else if (dayIndex < monthIndex)
968 return ParseDayMonthYearFormats;
969 else if (dayIndex < yearIndex)
970 return ParseMonthDayYearFormats;
972 // The year cannot be between the month and the date
973 return ParseGenericYearMonthDayFormats;
977 private static int _ParseNumber (string s, int valuePos,
991 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
992 if (!Char.IsDigit (s[i]))
998 digits = real_digits;
1000 if (digits < min_digits) {
1005 if (s.Length - valuePos < digits) {
1010 for (i = valuePos; i < digits + valuePos; i++) {
1012 if (!Char.IsDigit (c)) {
1017 number = number * 10 + (byte) (c - '0');
1020 num_parsed = digits;
1024 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1026 // FIXME: I know this is somehow lame code. Probably
1027 // it should iterate all the enum value and return
1028 // the longest match. However right now I don't see
1029 // anything but "1" and "10" - "12" that might match
1030 // two or more values. (They are only abbrev month
1031 // names, so do reverse order search). See bug #80094.
1032 for (int i = values.Length - 1; i >= 0; i--) {
1033 if (!exact && invValues [i].Length > values[i].Length) {
1034 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1036 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1040 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1042 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1051 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1054 maxlength = value.Length;
1056 if (sPos + maxlength <= s.Length && String.CompareOrdinalCaseInsensitive (s, sPos, value, 0, maxlength) == 0) {
1057 num_parsed = maxlength;
1065 // Note that in case of Parse (exact == false) we check both for AM/PM
1066 // and the culture spcific AM/PM strings.
1067 private static bool _ParseAmPm(string s,
1070 DateTimeFormatInfo dfi,
1079 if (!IsLetter (s, valuePos)) {
1080 if (dfi.AMDesignator != "")
1087 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1088 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1089 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1091 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1092 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1093 if (exact || num_parsed != 0)
1101 // Note that in case of Parse (exact == false) we check both for ':'
1102 // and the culture spcific TimeSperator
1103 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1105 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1106 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1109 // Accept any character for DateSeparator, except TimeSeparator,
1110 // a digit or a letter.
1111 // Not documented, but seems to be MS behaviour here. See bug 54047.
1112 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1115 if (exact && s [sPos] != '/')
1118 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1119 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1126 private static bool IsLetter (string s, int pos)
1128 return pos < s.Length && Char.IsLetter (s [pos]);
1131 // To implement better DateTime.Parse we use two format strings one
1132 // for Date and one for Time. This allows us to define two different
1133 // arrays of formats for Time and Dates and to combine them more or less
1134 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1135 private static bool _DoParse (string s,
1139 out DateTime result,
1140 out DateTimeOffset dto,
1141 DateTimeFormatInfo dfi,
1142 DateTimeStyles style,
1143 bool firstPartIsDate,
1144 ref bool incompleteFormat,
1147 bool useutc = false;
1148 bool use_invariant = false;
1149 bool sloppy_parsing = false;
1150 dto = new DateTimeOffset (0, TimeSpan.Zero);
1151 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1152 incompleteFormat = false;
1154 string format = firstPart;
1155 bool afterTFormat = false;
1156 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1157 if (format.Length == 1)
1158 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1160 result = new DateTime (0);
1167 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1168 format = format.TrimStart (null);
1170 s = s.TrimStart (null); // it could be optimized, but will make little good.
1173 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1174 format = format.TrimEnd (null);
1175 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1181 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1182 sloppy_parsing = true;
1184 string chars = format;
1185 int len = format.Length, pos = 0, num = 0;
1189 int day = -1, dayofweek = -1, month = -1, year = -1;
1190 int hour = -1, minute = -1, second = -1;
1191 double fractionalSeconds = -1;
1193 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1194 bool isFirstPart = true;
1195 bool format_with_24_hours = false;
1199 if (valuePos == s.Length)
1203 if (flexibleTwoPartsParsing && pos + num == 0)
1205 bool isLetter = IsLetter(s, valuePos);
1207 if (s [valuePos] == 'Z')
1210 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1211 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1212 valuePos += num_parsed;
1217 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1218 if (IsLetter (s, valuePos + num_parsed))
1220 else if (num_parsed > 0) {
1221 valuePos += num_parsed;
1226 if (!afterTFormat && dayofweek == -1 && isLetter) {
1227 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1228 if (dayofweek == -1)
1229 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1230 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1231 valuePos += num_parsed;
1238 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1245 if (pos + num >= len)
1247 if (flexibleTwoPartsParsing && num == 0) {
1248 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1249 if (!isFirstPart && format == "")
1254 format = secondPart;
1259 isFirstPart = false;
1265 bool leading_zeros = true;
1267 if (chars[pos] == '\'') {
1269 while (pos+num < len) {
1270 if (chars[pos+num] == '\'')
1273 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1283 } else if (chars[pos] == '"') {
1285 while (pos+num < len) {
1286 if (chars[pos+num] == '"')
1289 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1299 } else if (chars[pos] == '\\') {
1304 if (s [valuePos] != chars [pos])
1310 } else if (chars[pos] == '%') {
1313 } else if (char.IsWhiteSpace (s [valuePos]) ||
1314 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1317 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1318 if (!Char.IsWhiteSpace (chars[pos]))
1325 while (ws < s.Length) {
1326 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1333 while (ws < chars.Length) {
1334 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1340 // A whitespace may match a '/' in the pattern.
1341 if (!exact && pos < chars.Length && chars[pos] == '/')
1342 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1347 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1355 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1358 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1360 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1362 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1364 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1370 if (flexibleTwoPartsParsing) {
1372 if (num == 0 || num == 3)
1373 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1374 if (num > 1 && num_parsed == -1)
1375 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1376 if (num > 1 && num_parsed == -1)
1377 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1382 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1384 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1386 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1388 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1395 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1396 } else if (num < 3) {
1397 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1399 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1400 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1402 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1403 longYear = (ly > 9999);
1408 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1409 if (num_parsed <= 2)
1410 year += (year < 30) ? 2000 : 1900;
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);
1427 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1430 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1432 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1437 format_with_24_hours = true;
1443 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1445 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1455 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1457 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1464 leading_zeros = false;
1467 if (num > 6 || fractionalSeconds != -1)
1469 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1470 if (num_parsed == -1)
1472 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1475 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1482 if (s [valuePos] == '+')
1484 else if (s [valuePos] == '-')
1491 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1493 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1495 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1496 valuePos += num_parsed;
1501 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1502 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1503 valuePos += num_parsed;
1504 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1508 else if (!flexibleTwoPartsParsing)
1515 if (s [valuePos] == 'Z') {
1519 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1522 if (s [valuePos] == '+')
1524 else if (s [valuePos] == '-')
1529 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1530 valuePos += num_parsed;
1534 if (Char.IsDigit (s [valuePos]))
1536 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1538 valuePos += num_parsed;
1540 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1547 // LAMESPEC: This should be part of UTCpattern
1548 // string and thus should not be considered here.
1550 // Note that 'Z' is not defined as a pattern
1551 // character. Keep it for X509 certificate
1552 // verification. Also, "Z" != "'Z'" under MS.NET
1553 // ("'Z'" is just literal; handled above)
1555 if (s [valuePos] != 'Z')
1562 if (s [valuePos] != 'G')
1565 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1566 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1567 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1579 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1583 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1589 if (s[valuePos] == '.') {
1595 // '.FFF....' can be mapped to nothing
1596 if (pos + 1 < len && chars[pos + 1] == 'F') {
1598 while (pos + 1 < len && chars[pos + 1] == 'F') {
1610 if (s [valuePos] != chars [pos])
1621 valuePos += num_parsed;
1623 if (!exact && !flexibleTwoPartsParsing) {
1624 switch (chars [pos]) {
1630 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1631 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1639 pos = pos + num + 1;
1643 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1645 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1648 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1654 if (s.Length > valuePos) // extraneous tail.
1659 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1661 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1663 incompleteFormat = true;
1674 if (fractionalSeconds == -1)
1675 fractionalSeconds = 0;
1677 // If no date was given
1678 if ((day == -1) && (month == -1) && (year == -1)) {
1679 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1684 day = DateTime.Today.Day;
1685 month = DateTime.Today.Month;
1686 year = DateTime.Today.Year;
1695 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1698 year = DateTime.Today.Year;
1701 if (ampm == 0) { // AM designator
1702 if (hour >= 12 && format_with_24_hours && exact)
1707 } else if (ampm == 1) { // PM designator
1709 if (format_with_24_hours && exact)
1717 result = dfi.Calendar.ToDateTime (year, month, day, hour, minute, second, 0);
1722 result = result.AddSeconds(fractionalSeconds);
1724 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1728 if (result != DateTime.MinValue) {
1730 dto = new DateTimeOffset (result);
1731 } catch { } // We handle this error in DateTimeOffset.Parse
1739 tzoffset = -tzoffset;
1740 tzoffmin = -tzoffmin;
1743 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1744 } catch {} // We handle this error in DateTimeOffset.Parse
1746 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1749 long newticks = (result - dto.Offset).Ticks;
1751 newticks += TimeSpan.TicksPerDay;
1752 result = new DateTime (newticks, DateTimeKind.Utc);
1753 if ((style & DateTimeStyles.RoundtripKind) != 0)
1754 result = result.ToLocalTime ();
1755 } else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1756 result.encoded |= ((long) DateTimeKind.Utc << KindShift);
1757 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1758 result.encoded |= ((long) DateTimeKind.Local << KindShift);
1760 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1761 if ((DateTimeKind)(((ulong) result.encoded >> KindShift)) != DateTimeKind.Unspecified) {
1762 if (adjustToUniversal)
1763 result = result.ToUniversalTime ();
1764 else if (adjustToLocal)
1765 result = result.ToLocalTime ();
1771 public static DateTime ParseExact (string s, string format,
1772 IFormatProvider provider, DateTimeStyles style)
1775 throw new ArgumentNullException ("format");
1777 string [] formats = new string [1];
1778 formats[0] = format;
1780 return ParseExact (s, formats, provider, style);
1783 public static DateTime ParseExact (string s, string[] formats,
1784 IFormatProvider provider,
1785 DateTimeStyles style)
1787 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1790 throw new ArgumentNullException ("s");
1791 if (formats == null)
1792 throw new ArgumentNullException ("formats");
1793 if (formats.Length == 0)
1794 throw new FormatException ("Format specifier was invalid.");
1797 bool longYear = false;
1799 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1804 private static void CheckStyle (DateTimeStyles style)
1806 if ( (style & DateTimeStyles.RoundtripKind) != 0)
1808 if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
1809 (style & DateTimeStyles.AssumeUniversal) != 0)
1810 throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, AssumeUniversal or AdjustToUniversal.", "style");
1812 if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
1813 throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
1816 public static bool TryParse (string s, out DateTime result)
1820 Exception exception = null;
1823 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1830 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1834 Exception exception = null;
1837 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1844 public static bool TryParseExact (string s, string format,
1845 IFormatProvider provider,
1846 DateTimeStyles style,
1847 out DateTime result)
1850 formats = new string [1];
1851 formats[0] = format;
1853 return TryParseExact (s, formats, provider, style, out result);
1856 public static bool TryParseExact (string s, string[] formats,
1857 IFormatProvider provider,
1858 DateTimeStyles style,
1859 out DateTime result)
1862 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1864 bool longYear = false;
1866 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1873 private static bool ParseExact (string s, string [] formats,
1874 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1875 bool exact, ref bool longYear,
1876 bool setExceptionOnError, ref Exception exception)
1879 bool incompleteFormat = false;
1880 for (i = 0; i < formats.Length; i++)
1883 string format = formats[i];
1884 if (format == null || format == String.Empty)
1888 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1894 if (setExceptionOnError)
1895 exception = new FormatException ("Invalid format string");
1896 ret = DateTime.MinValue;
1900 public TimeSpan Subtract (DateTime value)
1902 return new TimeSpan (Ticks) - new TimeSpan (value.Ticks);
1905 public DateTime Subtract(TimeSpan value)
1909 newticks = Ticks - value.Ticks;
1910 if (newticks < 0 || newticks > MAX_VALUE_TICKS)
1911 throw new ArgumentOutOfRangeException ();
1912 DateTime ret = new DateTime (newticks);
1913 ret.encoded |= (encoded & KindMask);
1917 public long ToFileTime()
1919 DateTime universalTime = ToUniversalTime();
1921 if (universalTime.Ticks < w32file_epoch) {
1922 throw new ArgumentOutOfRangeException("file time is not valid");
1925 return(universalTime.Ticks - w32file_epoch);
1928 public long ToFileTimeUtc()
1930 if (Kind == DateTimeKind.Local)
1931 return ToFileTime ();
1933 if (Ticks < w32file_epoch) {
1934 throw new ArgumentOutOfRangeException("file time is not valid");
1937 return (Ticks - w32file_epoch);
1940 public string ToLongDateString()
1942 return ToString ("D");
1945 public string ToLongTimeString()
1947 return ToString ("T");
1950 public double ToOADate ()
1952 long t = this.Ticks;
1953 // uninitialized DateTime case
1956 // we can't reach minimum value
1957 if (t < 31242239136000000)
1958 return OAMinValue + 0.001;
1960 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1961 double result = ts.TotalDays;
1962 // t < 0 (where 599264352000000000 == 0.0d for OA)
1963 if (t < 599264352000000000) {
1964 // negative days (int) but decimals are positive
1965 double d = Math.Ceiling (result);
1966 result = d - 2 - (result - d);
1969 // we can't reach maximum value
1970 if (result >= OAMaxValue)
1971 result = OAMaxValue - 0.00000001d;
1976 public string ToShortDateString()
1978 return ToString ("d");
1981 public string ToShortTimeString()
1983 return ToString ("t");
1986 public override string ToString ()
1988 return ToString ("G", null);
1991 public string ToString (IFormatProvider provider)
1993 return ToString (null, provider);
1996 public string ToString (string format)
1998 return ToString (format, null);
2001 public string ToString (string format, IFormatProvider provider)
2003 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2005 if (format == null || format == String.Empty)
2008 if (format.Length == 1) {
2009 char fchar = format [0];
2010 bool use_invariant, useutc;
2011 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2013 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2014 // return ToUniversalTime()._ToString (format, dfi);
2017 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2020 dfi = DateTimeFormatInfo.InvariantInfo;
2023 // Don't convert UTC value. It just adds 'Z' for
2024 // 'u' format, for the same ticks.
2025 return DateTimeUtils.ToString (this, format, dfi);
2028 public DateTime ToLocalTime ()
2030 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2033 public DateTime ToUniversalTime()
2035 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2040 public static DateTime operator +(DateTime d, TimeSpan t)
2043 long res = checked ((d.encoded & TicksMask) + t.Ticks);
2044 if (res < 0 || res > MAX_VALUE_TICKS){
2045 throw new ArgumentOutOfRangeException ();
2048 return new DateTime (res, d.Kind);
2049 } catch (OverflowException){
2050 throw new ArgumentOutOfRangeException ();
2054 public static bool operator ==(DateTime d1, DateTime d2)
2056 return ((d1.encoded & TicksMask) == (d2.encoded & TicksMask));
2059 public static bool operator >(DateTime t1,DateTime t2)
2061 return ((t1.encoded & TicksMask) > (t2.encoded & TicksMask));
2064 public static bool operator >=(DateTime t1,DateTime t2)
2066 return ((t1.encoded & TicksMask) >= (t2.encoded & TicksMask));
2069 public static bool operator !=(DateTime d1, DateTime d2)
2071 return ((d1.encoded & TicksMask) != (d2.encoded & TicksMask));
2074 public static bool operator <(DateTime t1, DateTime t2)
2076 return ((t1.encoded & TicksMask) < (t2.encoded & TicksMask));
2079 public static bool operator <=(DateTime t1, DateTime t2)
2081 return ((t1.encoded & TicksMask) <= (t2.encoded & TicksMask));
2084 public static TimeSpan operator -(DateTime d1, DateTime d2)
2086 return new TimeSpan ((d1.encoded & TicksMask) - (d2.encoded & TicksMask));
2089 public static DateTime operator -(DateTime d, TimeSpan t)
2092 long res = checked ((d.encoded & TicksMask) - t.Ticks);
2093 if (res < 0 || res > MAX_VALUE_TICKS)
2094 throw new ArgumentOutOfRangeException ();
2095 return new DateTime (res, d.Kind);
2096 } catch (OverflowException){
2097 throw new ArgumentOutOfRangeException ();
2101 bool IConvertible.ToBoolean (IFormatProvider provider)
2103 throw new InvalidCastException();
2106 byte IConvertible.ToByte (IFormatProvider provider)
2108 throw new InvalidCastException();
2112 char IConvertible.ToChar (IFormatProvider provider)
2114 throw new InvalidCastException();
2117 System.DateTime IConvertible.ToDateTime (IFormatProvider provider)
2122 decimal IConvertible.ToDecimal (IFormatProvider provider)
2124 throw new InvalidCastException();
2127 double IConvertible.ToDouble (IFormatProvider provider)
2129 throw new InvalidCastException();
2132 Int16 IConvertible.ToInt16 (IFormatProvider provider)
2134 throw new InvalidCastException();
2137 Int32 IConvertible.ToInt32 (IFormatProvider provider)
2139 throw new InvalidCastException();
2142 Int64 IConvertible.ToInt64 (IFormatProvider provider)
2144 throw new InvalidCastException();
2147 SByte IConvertible.ToSByte (IFormatProvider provider)
2149 throw new InvalidCastException();
2152 Single IConvertible.ToSingle (IFormatProvider provider)
2154 throw new InvalidCastException();
2157 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2159 if (targetType == null)
2160 throw new ArgumentNullException ("targetType");
2162 if (targetType == typeof (DateTime))
2164 else if (targetType == typeof (String))
2165 return this.ToString (provider);
2166 else if (targetType == typeof (Object))
2169 throw new InvalidCastException();
2172 UInt16 IConvertible.ToUInt16 (IFormatProvider provider)
2174 throw new InvalidCastException();
2177 UInt32 IConvertible.ToUInt32 (IFormatProvider provider)
2179 throw new InvalidCastException();
2182 UInt64 IConvertible.ToUInt64 (IFormatProvider provider)
2184 throw new InvalidCastException();
2187 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2190 info.AddValue ("ticks", t);
2192 // This is the new .NET format, encodes the kind on the top bits
2193 info.AddValue ("dateData", (UInt64)encoded);
2197 static DateTime () {
2198 if (MonoTouchAOTHelper.FalseFlag) {
2199 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
2200 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();