move FrameworkName from corlib to System
[mcs.git] / class / corlib / System / DateTime.cs
blob94c8a73a63577e7ab524aa04daeba40c501d7734
1 //
2 // System.DateTime.cs
3 //
4 // author:
5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
8 //
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:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
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;
36 using System.Text;
37 using System.Runtime.Serialization;
39 namespace System
41 /// <summary>
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.
44 /// </summary>
45 ///
46 [Serializable]
47 [StructLayout (LayoutKind.Auto)]
48 public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
50 #if MONOTOUCH
51 static DateTime () {
52 if (MonoTouchAOTHelper.FalseFlag) {
53 var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
54 var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();
57 #endif
58 private TimeSpan ticks;
60 DateTimeKind kind;
62 private const int dp400 = 146097;
63 private const int dp100 = 36524;
64 private const int dp4 = 1461;
66 // w32 file time starts counting from 1/1/1601 00:00 GMT
67 // which is the constant ticks from the .NET epoch
68 private const long w32file_epoch = 504911232000000000L;
70 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
71 // -- Microsoft .NET has this value.
72 private const long MAX_VALUE_TICKS = 3155378975999999999L;
75 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
76 // in Ticks
78 internal const long UnixEpoch = 621355968000000000L;
80 // for OLE Automation dates
81 private const long ticks18991230 = 599264352000000000L;
82 private const double OAMinValue = -657435.0d;
83 private const double OAMaxValue = 2958466.0d;
85 public static readonly DateTime MaxValue = new DateTime (false, new TimeSpan (MAX_VALUE_TICKS));
86 public static readonly DateTime MinValue = new DateTime (false, new TimeSpan (0));
88 // DateTime.Parse patterns
89 // Patterns are divided to date and time patterns. The algorithm will
90 // try combinations of these patterns. The algorithm also looks for
91 // day of the week, AM/PM GMT and Z independently of the patterns.
92 private static readonly string[] ParseTimeFormats = new string [] {
93 "H:m:s.fffffffzzz",
94 "H:m:s.fffffff",
95 "H:m:s tt zzz",
96 "H:m:szzz",
97 "H:m:s",
98 "H:mzzz",
99 "H:m",
100 "H tt", // Specifies AM to disallow '8'.
101 "H'\u6642'm'\u5206's'\u79D2'",
104 // DateTime.Parse date patterns extend ParseExact patterns as follows:
105 // MMM - month short name or month full name
106 // MMMM - month number or short name or month full name
108 // Parse behaves differently according to the ShorDatePattern of the
109 // DateTimeFormatInfo. The following define the date patterns for
110 // different orders of day, month and year in ShorDatePattern.
111 // Note that the year cannot go between the day and the month.
112 private static readonly string[] ParseYearDayMonthFormats = new string [] {
113 "yyyy/M/dT",
114 "M/yyyy/dT",
115 "yyyy'\u5E74'M'\u6708'd'\u65E5",
118 "yyyy/d/MMMM",
119 "yyyy/MMM/d",
120 "d/MMMM/yyyy",
121 "MMM/d/yyyy",
122 "d/yyyy/MMMM",
123 "MMM/yyyy/d",
125 "yy/d/M",
128 private static readonly string[] ParseYearMonthDayFormats = new string [] {
129 "yyyy/M/dT",
130 "M/yyyy/dT",
131 "yyyy'\u5E74'M'\u6708'd'\u65E5",
133 "yyyy/MMMM/d",
134 "yyyy/d/MMM",
135 "MMMM/d/yyyy",
136 "d/MMM/yyyy",
137 "MMMM/yyyy/d",
138 "d/yyyy/MMM",
140 "yy/MMMM/d",
141 "yy/d/MMM",
142 "MMM/yy/d",
145 private static readonly string[] ParseDayMonthYearFormats = new string [] {
146 "yyyy/M/dT",
147 "M/yyyy/dT",
148 "yyyy'\u5E74'M'\u6708'd'\u65E5",
150 "yyyy/MMMM/d",
151 "yyyy/d/MMM",
152 "d/MMMM/yyyy",
153 "MMM/d/yyyy",
154 "MMMM/yyyy/d",
155 "d/yyyy/MMM",
157 "d/MMMM/yy",
158 "yy/MMM/d",
159 "d/yy/MMM",
160 "yy/d/MMM",
161 "MMM/d/yy",
162 "MMM/yy/d",
165 private static readonly string[] ParseMonthDayYearFormats = new string [] {
166 "yyyy/M/dT",
167 "M/yyyy/dT",
168 "yyyy'\u5E74'M'\u6708'd'\u65E5",
170 "yyyy/MMMM/d",
171 "yyyy/d/MMM",
172 "MMMM/d/yyyy",
173 "d/MMM/yyyy",
174 "MMMM/yyyy/d",
175 "d/yyyy/MMM",
177 "MMMM/d/yy",
178 "MMM/yy/d",
179 "d/MMM/yy",
180 "yy/MMM/d",
181 "d/yy/MMM",
182 "yy/d/MMM",
185 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
186 // Note that these patterns cannot be followed by the time.
187 private static readonly string[] MonthDayShortFormats = new string [] {
188 "MMMM/d",
189 "d/MMM",
190 "yyyy/MMMM",
192 private static readonly string[] DayMonthShortFormats = new string [] {
193 "d/MMMM",
194 "MMM/yy",
195 "yyyy/MMMM",
198 private enum Which
200 Day,
201 DayYear,
202 Month,
203 Year
206 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
207 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
209 private static int AbsoluteDays (int year, int month, int day)
211 int[] days;
212 int temp = 0, m=1 ;
214 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
216 while (m < month)
217 temp += days[m++];
218 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
221 private int FromTicks(Which what)
223 int num400, num100, num4, numyears;
224 int M =1;
226 int[] days = daysmonth;
227 int totaldays = this.ticks.Days;
229 num400 = (totaldays / dp400);
230 totaldays -= num400 * dp400;
232 num100 = (totaldays / dp100);
233 if (num100 == 4) // leap
234 num100 = 3;
235 totaldays -= (num100 * dp100);
237 num4 = totaldays / dp4;
238 totaldays -= (num4 * dp4);
240 numyears = totaldays / 365 ;
242 if (numyears == 4) //leap
243 numyears =3 ;
244 if (what == Which.Year )
245 return num400*400 + num100*100 + num4*4 + numyears + 1;
247 totaldays -= (numyears * 365) ;
248 if (what == Which.DayYear )
249 return totaldays + 1;
251 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
252 days = daysmonthleap;
254 while (totaldays >= days[M])
255 totaldays -= days[M++];
257 if (what == Which.Month )
258 return M;
260 return totaldays +1;
264 // Constructors
266 /// <summary>
267 /// Constructs a DateTime for specified ticks
268 /// </summary>
269 ///
270 public DateTime (long ticks)
272 this.ticks = new TimeSpan (ticks);
273 if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
274 string msg = Locale.GetText ("Value {0} is outside the valid range [{1},{2}].",
275 ticks, MinValue.Ticks, MaxValue.Ticks);
276 throw new ArgumentOutOfRangeException ("ticks", msg);
278 kind = DateTimeKind.Unspecified;
281 public DateTime (int year, int month, int day)
282 : this (year, month, day,0,0,0,0) {}
284 public DateTime (int year, int month, int day, int hour, int minute, int second)
285 : this (year, month, day, hour, minute, second, 0) {}
287 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
289 if ( year < 1 || year > 9999 ||
290 month < 1 || month >12 ||
291 day < 1 || day > DaysInMonth(year, month) ||
292 hour < 0 || hour > 23 ||
293 minute < 0 || minute > 59 ||
294 second < 0 || second > 59 ||
295 millisecond < 0 || millisecond > 999)
296 throw new ArgumentOutOfRangeException ("Parameters describe an " +
297 "unrepresentable DateTime.");
299 ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
301 kind = DateTimeKind.Unspecified;
304 public DateTime (int year, int month, int day, Calendar calendar)
305 : this (year, month, day, 0, 0, 0, 0, calendar)
309 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
310 : this (year, month, day, hour, minute, second, 0, calendar)
314 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
316 if (calendar == null)
317 throw new ArgumentNullException ("calendar");
318 ticks = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).ticks;
319 kind = DateTimeKind.Unspecified;
322 internal DateTime (bool check, TimeSpan value)
324 if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
325 throw new ArgumentOutOfRangeException ();
327 ticks = value;
329 kind = DateTimeKind.Unspecified;
332 public DateTime (long ticks, DateTimeKind kind) : this (ticks)
334 CheckDateTimeKind (kind);
335 this.kind = kind;
338 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
339 : this (year, month, day, hour, minute, second)
341 CheckDateTimeKind (kind);
342 this.kind = kind;
345 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
346 : this (year, month, day, hour, minute, second, millisecond)
348 CheckDateTimeKind (kind);
349 this.kind = kind;
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 CheckDateTimeKind (kind);
356 this.kind = kind;
360 // Not visible, but can be invoked during deserialization
362 DateTime (SerializationInfo info, StreamingContext context)
364 if (info.HasKey ("dateData")){
365 long dateData = info.GetInt64 ("dateData");
366 kind = (DateTimeKind) (dateData >> 62);
367 ticks = new TimeSpan (dateData & 0x3fffffffffffffff);
368 } else if (info.HasKey ("ticks")){
369 ticks = new TimeSpan (info.GetInt64 ("ticks"));
370 kind = DateTimeKind.Unspecified;
371 } else {
372 kind = DateTimeKind.Unspecified;
373 ticks = new TimeSpan (0);
378 /* Properties */
380 public DateTime Date
382 get
384 DateTime ret = new DateTime (Year, Month, Day);
385 ret.kind = kind;
386 return ret;
390 public int Month
392 get
394 return FromTicks(Which.Month);
398 public int Day
400 get
402 return FromTicks(Which.Day);
406 public DayOfWeek DayOfWeek
408 get
410 return ( (DayOfWeek) ((ticks.Days+1) % 7) );
414 public int DayOfYear
416 get
418 return FromTicks(Which.DayYear);
422 public TimeSpan TimeOfDay
424 get
426 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
431 public int Hour
433 get
435 return ticks.Hours;
439 public int Minute
441 get
443 return ticks.Minutes;
447 public int Second
449 get
451 return ticks.Seconds;
455 public int Millisecond
457 get
459 return ticks.Milliseconds;
463 [MethodImplAttribute(MethodImplOptions.InternalCall)]
464 internal static extern long GetTimeMonotonic ();
466 [MethodImplAttribute(MethodImplOptions.InternalCall)]
467 internal static extern long GetNow ();
470 // To reduce the time consumed by DateTime.Now, we keep
471 // the difference to map the system time into a local
472 // time into `to_local_time_span', we record the timestamp
473 // for this in `last_now'
475 static object to_local_time_span_object;
476 static long last_now;
478 public static DateTime Now
480 get
482 long now = GetNow ();
483 DateTime dt = new DateTime (now);
485 if ((now - last_now) > TimeSpan.TicksPerMinute){
486 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
487 last_now = now;
491 // This is boxed, so we avoid locking.
492 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
493 ret.kind = DateTimeKind.Local;
494 return ret;
498 public long Ticks
500 get
502 return ticks.Ticks;
506 public static DateTime Today
508 get {
509 DateTime now = Now;
510 DateTime today = new DateTime (now.Year, now.Month, now.Day);
511 today.kind = now.kind;
512 return today;
516 public static DateTime UtcNow
518 get {
519 return new DateTime (GetNow (), DateTimeKind.Utc);
523 public int Year
525 get
527 return FromTicks(Which.Year);
531 public DateTimeKind Kind {
532 get {
533 return kind;
537 /* methods */
539 public DateTime Add (TimeSpan value)
541 DateTime ret = AddTicks (value.Ticks);
542 ret.kind = kind;
543 return ret;
546 public DateTime AddDays (double value)
548 return AddMilliseconds (Math.Round (value * 86400000));
551 public DateTime AddTicks (long value)
553 if ((value + ticks.Ticks) > MAX_VALUE_TICKS || (value + ticks.Ticks) < 0) {
554 throw new ArgumentOutOfRangeException();
556 DateTime ret = new DateTime (value + ticks.Ticks);
557 ret.kind = kind;
558 return ret;
561 public DateTime AddHours (double value)
563 return AddMilliseconds (value * 3600000);
566 public DateTime AddMilliseconds (double value)
568 if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
569 (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
570 throw new ArgumentOutOfRangeException();
572 long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
574 return AddTicks (msticks);
577 // required to match MS implementation for OADate (OLE Automation)
578 private DateTime AddRoundedMilliseconds (double ms)
580 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
581 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
582 throw new ArgumentOutOfRangeException ();
584 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
586 return AddTicks (msticks);
589 public DateTime AddMinutes (double value)
591 return AddMilliseconds (value * 60000);
594 public DateTime AddMonths (int months)
596 int day, month, year, maxday ;
597 DateTime temp ;
599 day = this.Day;
600 month = this.Month + (months % 12);
601 year = this.Year + months/12 ;
603 if (month < 1)
605 month = 12 + month ;
606 year -- ;
608 else if (month>12)
610 month = month -12;
611 year ++;
613 maxday = DaysInMonth(year, month);
614 if (day > maxday)
615 day = maxday;
617 temp = new DateTime (year, month, day);
618 temp.kind = kind;
619 return temp.Add (this.TimeOfDay);
622 public DateTime AddSeconds (double value)
624 return AddMilliseconds (value * 1000);
627 public DateTime AddYears (int value)
629 return AddMonths (value * 12);
632 public static int Compare (DateTime t1, DateTime t2)
634 if (t1.ticks < t2.ticks)
635 return -1;
636 else if (t1.ticks > t2.ticks)
637 return 1;
638 else
639 return 0;
642 public int CompareTo (object value)
644 if (value == null)
645 return 1;
647 if (!(value is System.DateTime))
648 throw new ArgumentException (Locale.GetText (
649 "Value is not a System.DateTime"));
651 return Compare (this, (DateTime) value);
654 public bool IsDaylightSavingTime ()
656 if (kind == DateTimeKind.Utc)
657 return false;
658 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
661 public int CompareTo (DateTime value)
663 return Compare (this, value);
666 public bool Equals (DateTime value)
668 return value.ticks == ticks;
671 public long ToBinary ()
673 switch (kind) {
674 case DateTimeKind.Utc:
675 return Ticks | 0x4000000000000000;
676 case DateTimeKind.Local:
677 return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
678 default:
679 return Ticks;
683 public static DateTime FromBinary (long dateData)
685 switch ((ulong)dateData >> 62) {
686 case 1:
687 return new DateTime (dateData ^ 0x4000000000000000, DateTimeKind.Utc);
688 case 0:
689 return new DateTime (dateData, DateTimeKind.Unspecified);
690 default:
691 return new DateTime (dateData & 0x3fffffffffffffff, DateTimeKind.Utc).ToLocalTime ();
695 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
697 return new DateTime (value.Ticks, kind);
700 public static int DaysInMonth (int year, int month)
702 int[] days ;
704 if (month < 1 || month >12)
705 throw new ArgumentOutOfRangeException ();
707 if (year < 1 || year > 9999)
708 throw new ArgumentOutOfRangeException ();
710 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
711 return days[month];
714 public override bool Equals (object value)
716 if (!(value is System.DateTime))
717 return false;
719 return ((DateTime) value).ticks == ticks;
722 public static bool Equals (DateTime t1, DateTime t2 )
724 return (t1.ticks == t2.ticks );
727 public static DateTime FromFileTime (long fileTime)
729 if (fileTime < 0)
730 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
732 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
735 public static DateTime FromFileTimeUtc (long fileTime)
737 if (fileTime < 0)
738 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
740 return new DateTime (w32file_epoch + fileTime);
743 public static DateTime FromOADate (double d)
745 // An OLE Automation date is implemented as a floating-point number
746 // whose value is the number of days from midnight, 30 December 1899.
748 // d must be negative 657435.0 through positive 2958466.0.
749 if ((d <= OAMinValue) || (d >= OAMaxValue))
750 throw new ArgumentException ("d", "[-657435,2958466]");
752 DateTime dt = new DateTime (ticks18991230);
753 if (d < 0.0d) {
754 Double days = Math.Ceiling (d);
755 // integer part is the number of days (negative)
756 dt = dt.AddRoundedMilliseconds (days * 86400000);
757 // but decimals are the number of hours (in days fractions) and positive
758 Double hours = (days - d);
759 dt = dt.AddRoundedMilliseconds (hours * 86400000);
761 else {
762 dt = dt.AddRoundedMilliseconds (d * 86400000);
765 return dt;
768 public string[] GetDateTimeFormats()
770 return GetDateTimeFormats (CultureInfo.CurrentCulture);
773 public string[] GetDateTimeFormats(char format)
775 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
776 throw new FormatException ("Invalid format character.");
777 string[] result = new string[1];
778 result[0] = this.ToString(format.ToString());
779 return result;
782 public string[] GetDateTimeFormats(IFormatProvider provider)
784 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
785 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
786 ArrayList al = new ArrayList ();
787 foreach (char c in "dDgGfFmMrRstTuUyY")
788 al.AddRange (GetDateTimeFormats (c, info));
789 return al.ToArray (typeof (string)) as string [];
792 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
794 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
795 throw new FormatException ("Invalid format character.");
797 // LAMESPEC: There is NO assurance that 'U' ALWAYS
798 // euqals to 'F', but since we have to iterate all
799 // the pattern strings, we cannot just use
800 // ToString("U", provider) here. I believe that the
801 // method's behavior cannot be formalized.
802 bool adjustutc = false;
803 switch (format) {
804 case 'U':
805 // case 'r':
806 // case 'R':
807 // case 'u':
808 adjustutc = true;
809 break;
811 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
812 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
815 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
817 string [] results = new string [patterns.Length];
818 DateTime val = adjustutc ? ToUniversalTime () : this;
819 for (int i = 0; i < results.Length; i++)
820 results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
821 return results;
824 private void CheckDateTimeKind (DateTimeKind kind) {
825 if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
826 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
829 public override int GetHashCode ()
831 return (int) ticks.Ticks;
834 public TypeCode GetTypeCode ()
836 return TypeCode.DateTime;
839 public static bool IsLeapYear (int year)
841 if (year < 1 || year > 9999)
842 throw new ArgumentOutOfRangeException ();
843 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
846 public static DateTime Parse (string s)
848 return Parse (s, null);
851 public static DateTime Parse (string s, IFormatProvider provider)
853 return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
856 public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
858 if (s == null)
859 throw new ArgumentNullException ("s");
861 DateTime res;
862 DateTimeOffset dto;
863 Exception exception = null;
864 if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
865 throw exception;
867 return res;
870 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
872 internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
873 out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
875 dto = new DateTimeOffset (0, TimeSpan.Zero);
876 if (s == null || s.Length == 0) {
877 if (setExceptionOnError)
878 exception = new FormatException (formatExceptionMessage);
879 result = MinValue;
880 return false;
883 if (provider == null)
884 provider = CultureInfo.CurrentCulture;
885 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
887 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
888 string[] allDateFormats = YearMonthDayFormats (dfi, setExceptionOnError, ref exception);
889 if (allDateFormats == null){
890 result = MinValue;
891 return false;
894 bool longYear = false;
895 for (int i = 0; i < allDateFormats.Length; i++) {
896 string firstPart = allDateFormats [i];
897 bool incompleteFormat = false;
898 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
899 return true;
901 if (!incompleteFormat)
902 continue;
904 for (int j = 0; j < ParseTimeFormats.Length; j++) {
905 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
906 return true;
911 // Month day formats
913 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
914 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
915 if (dayIndex == -1 || monthIndex == -1){
916 result = MinValue;
917 if (setExceptionOnError)
918 exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
919 return false;
921 bool is_day_before_month = dayIndex < monthIndex;
922 string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
923 for (int i = 0; i < monthDayFormats.Length; i++) {
924 bool incompleteFormat = false;
925 if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
926 return true;
929 for (int j = 0; j < ParseTimeFormats.Length; j++) {
930 string firstPart = ParseTimeFormats [j];
931 bool incompleteFormat = false;
932 if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
933 return true;
934 if (!incompleteFormat)
935 continue;
937 for (int i = 0; i < monthDayFormats.Length; i++) {
938 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
939 return true;
941 for (int i = 0; i < allDateFormats.Length; i++) {
942 string dateFormat = allDateFormats [i];
943 if (dateFormat[dateFormat.Length - 1] == 'T')
944 continue; // T formats must be before the time part
945 if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
946 return true;
950 // Try as a last resort all the patterns
951 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
952 return true;
954 if (!setExceptionOnError)
955 return false;
957 // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
958 exception = new FormatException (formatExceptionMessage);
959 return false;
962 public static DateTime ParseExact (string s, string format, IFormatProvider provider)
964 return ParseExact (s, format, provider, DateTimeStyles.None);
967 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi, bool setExceptionOnError, ref Exception exc)
969 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
970 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
971 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
972 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1){
973 if (setExceptionOnError)
974 exc = new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
975 return null;
978 if (yearIndex < monthIndex)
979 if (monthIndex < dayIndex)
980 return ParseYearMonthDayFormats;
981 else if (yearIndex < dayIndex)
982 return ParseYearDayMonthFormats;
983 else {
984 // The year cannot be between the date and the month
985 if (setExceptionOnError)
986 exc = new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
987 return null;
989 else if (dayIndex < monthIndex)
990 return ParseDayMonthYearFormats;
991 else if (dayIndex < yearIndex)
992 return ParseMonthDayYearFormats;
993 else {
994 // The year cannot be between the month and the date
995 if (setExceptionOnError)
996 exc = new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
997 return null;
1001 private static int _ParseNumber (string s, int valuePos,
1002 int min_digits,
1003 int digits,
1004 bool leadingzero,
1005 bool sloppy_parsing,
1006 out int num_parsed)
1008 int number = 0, i;
1010 if (sloppy_parsing)
1011 leadingzero = false;
1013 if (!leadingzero) {
1014 int real_digits = 0;
1015 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
1016 if (!Char.IsDigit (s[i]))
1017 break;
1019 real_digits++;
1022 digits = real_digits;
1024 if (digits < min_digits) {
1025 num_parsed = -1;
1026 return 0;
1029 if (s.Length - valuePos < digits) {
1030 num_parsed = -1;
1031 return 0;
1034 for (i = valuePos; i < digits + valuePos; i++) {
1035 char c = s[i];
1036 if (!Char.IsDigit (c)) {
1037 num_parsed = -1;
1038 return 0;
1041 number = number * 10 + (byte) (c - '0');
1044 num_parsed = digits;
1045 return number;
1048 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1050 // FIXME: I know this is somehow lame code. Probably
1051 // it should iterate all the enum value and return
1052 // the longest match. However right now I don't see
1053 // anything but "1" and "10" - "12" that might match
1054 // two or more values. (They are only abbrev month
1055 // names, so do reverse order search). See bug #80094.
1056 for (int i = values.Length - 1; i >= 0; i--) {
1057 if (!exact && invValues [i].Length > values[i].Length) {
1058 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1059 return i;
1060 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1061 return i;
1063 else {
1064 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1065 return i;
1066 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1067 return i;
1071 num_parsed = -1;
1072 return -1;
1075 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1077 if (maxlength <= 0)
1078 maxlength = value.Length;
1080 if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1081 num_parsed = maxlength;
1082 return true;
1085 num_parsed = -1;
1086 return false;
1089 // Note that in case of Parse (exact == false) we check both for AM/PM
1090 // and the culture spcific AM/PM strings.
1091 private static bool _ParseAmPm(string s,
1092 int valuePos,
1093 int num,
1094 DateTimeFormatInfo dfi,
1095 bool exact,
1096 out int num_parsed,
1097 ref int ampm)
1099 num_parsed = -1;
1100 if (ampm != -1)
1101 return false;
1103 if (!IsLetter (s, valuePos)) {
1104 if (dfi.AMDesignator != "")
1105 return false;
1106 if (exact)
1107 ampm = 0;
1108 num_parsed = 0;
1109 return true;
1111 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1112 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1113 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1114 ampm = 1;
1115 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1116 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1117 if (exact || num_parsed != 0)
1118 ampm = 0;
1120 else
1121 return false;
1122 return true;
1125 // Note that in case of Parse (exact == false) we check both for ':'
1126 // and the culture spcific TimeSperator
1127 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1129 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1130 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1133 // Accept any character for DateSeparator, except TimeSeparator,
1134 // a digit or a letter.
1135 // Not documented, but seems to be MS behaviour here. See bug 54047.
1136 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1138 num_parsed = -1;
1139 if (exact && s [sPos] != '/')
1140 return false;
1142 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1143 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1144 return(false);
1146 num_parsed = 1;
1147 return true;
1150 private static bool IsLetter (string s, int pos)
1152 return pos < s.Length && Char.IsLetter (s [pos]);
1155 // To implement better DateTime.Parse we use two format strings one
1156 // for Date and one for Time. This allows us to define two different
1157 // arrays of formats for Time and Dates and to combine them more or less
1158 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1159 private static bool _DoParse (string s,
1160 string firstPart,
1161 string secondPart,
1162 bool exact,
1163 out DateTime result,
1164 out DateTimeOffset dto,
1165 DateTimeFormatInfo dfi,
1166 DateTimeStyles style,
1167 bool firstPartIsDate,
1168 ref bool incompleteFormat,
1169 ref bool longYear)
1171 bool useutc = false;
1172 bool use_invariant = false;
1173 bool sloppy_parsing = false;
1174 dto = new DateTimeOffset (0, TimeSpan.Zero);
1175 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1176 incompleteFormat = false;
1177 int valuePos = 0;
1178 string format = firstPart;
1179 bool afterTFormat = false;
1180 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1181 if (format.Length == 1)
1182 format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1184 result = new DateTime (0);
1185 if (format == null)
1186 return false;
1188 if (s == null)
1189 return false;
1191 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1192 format = format.TrimStart (null);
1194 s = s.TrimStart (null); // it could be optimized, but will make little good.
1197 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1198 format = format.TrimEnd (null);
1199 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1202 if (use_invariant)
1203 dfi = invInfo;
1205 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1206 sloppy_parsing = true;
1208 string chars = format;
1209 int len = format.Length, pos = 0, num = 0;
1210 if (len == 0)
1211 return false;
1213 int day = -1, dayofweek = -1, month = -1, year = -1;
1214 int hour = -1, minute = -1, second = -1;
1215 double fractionalSeconds = -1;
1216 int ampm = -1;
1217 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1218 bool isFirstPart = true;
1220 for (; ; )
1222 if (valuePos == s.Length)
1223 break;
1225 int num_parsed = 0;
1226 if (flexibleTwoPartsParsing && pos + num == 0)
1228 bool isLetter = IsLetter(s, valuePos);
1229 if (isLetter) {
1230 if (s [valuePos] == 'Z')
1231 num_parsed = 1;
1232 else
1233 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1234 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1235 valuePos += num_parsed;
1236 useutc = true;
1237 continue;
1240 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1241 if (IsLetter (s, valuePos + num_parsed))
1242 ampm = -1;
1243 else if (num_parsed > 0) {
1244 valuePos += num_parsed;
1245 continue;
1249 if (!afterTFormat && dayofweek == -1 && isLetter) {
1250 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1251 if (dayofweek == -1)
1252 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1253 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1254 valuePos += num_parsed;
1255 continue;
1257 else
1258 dayofweek = -1;
1261 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1262 valuePos += 1;
1263 continue;
1265 num_parsed = 0;
1268 if (pos + num >= len)
1270 if (flexibleTwoPartsParsing && num == 0) {
1271 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1272 if (!isFirstPart && format == "")
1273 break;
1275 pos = 0;
1276 if (isFirstPart)
1277 format = secondPart;
1278 else
1279 format = "";
1280 chars = format;
1281 len = chars.Length;
1282 isFirstPart = false;
1283 continue;
1285 break;
1288 bool leading_zeros = true;
1290 if (chars[pos] == '\'') {
1291 num = 1;
1292 while (pos+num < len) {
1293 if (chars[pos+num] == '\'')
1294 break;
1296 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1297 return false;
1299 valuePos++;
1300 num++;
1303 pos += num + 1;
1304 num = 0;
1305 continue;
1306 } else if (chars[pos] == '"') {
1307 num = 1;
1308 while (pos+num < len) {
1309 if (chars[pos+num] == '"')
1310 break;
1312 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1313 return false;
1315 valuePos++;
1316 num++;
1319 pos += num + 1;
1320 num = 0;
1321 continue;
1322 } else if (chars[pos] == '\\') {
1323 pos += num + 1;
1324 num = 0;
1325 if (pos >= len)
1326 return false;
1327 if (s [valuePos] != chars [pos])
1328 return false;
1330 valuePos++;
1331 pos++;
1332 continue;
1333 } else if (chars[pos] == '%') {
1334 pos++;
1335 continue;
1336 } else if (char.IsWhiteSpace (s [valuePos]) ||
1337 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1338 valuePos++;
1339 num = 0;
1340 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1341 if (!Char.IsWhiteSpace (chars[pos]))
1342 return false;
1343 pos++;
1344 continue;
1347 int ws = valuePos;
1348 while (ws < s.Length) {
1349 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1350 ws++;
1351 else
1352 break;
1354 valuePos = ws;
1355 ws = pos;
1356 while (ws < chars.Length) {
1357 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1358 ws++;
1359 else
1360 break;
1362 pos = ws;
1363 // A whitespace may match a '/' in the pattern.
1364 if (!exact && pos < chars.Length && chars[pos] == '/')
1365 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1366 pos++;
1367 continue;
1370 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1371 num++;
1372 continue;
1375 switch (chars[pos])
1377 case 'd':
1378 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1379 return false;
1380 if (num == 0)
1381 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1382 else if (num == 1)
1383 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1384 else if (num == 2)
1385 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1386 else
1387 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1388 break;
1389 case 'M':
1390 if (month != -1)
1391 return false;
1393 if (flexibleTwoPartsParsing) {
1394 num_parsed = -1;
1395 if (num == 0 || num == 3)
1396 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1397 if (num > 1 && num_parsed == -1)
1398 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1399 if (num > 1 && num_parsed == -1)
1400 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1401 break;
1404 if (num == 0)
1405 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1406 else if (num == 1)
1407 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1408 else if (num == 2)
1409 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1410 else
1411 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1412 break;
1413 case 'y':
1414 if (year != -1)
1415 return false;
1417 if (num == 0) {
1418 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1419 } else if (num < 3) {
1420 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1421 } else {
1422 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1423 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1424 int np = 0;
1425 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1426 longYear = (ly > 9999);
1428 num = 3;
1431 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1432 if (num_parsed <= 2)
1433 year += (year < 30) ? 2000 : 1900;
1434 break;
1435 case 'h':
1436 if (hour != -1)
1437 return false;
1438 if (num == 0)
1439 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1440 else
1441 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1443 if (hour > 12)
1444 return false;
1445 if (hour == 12)
1446 hour = 0;
1448 break;
1449 case 'H':
1450 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1451 return false;
1452 if (num == 0)
1453 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1454 else
1455 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1457 if (hour >= 24)
1458 return false;
1460 // ampm = -2;
1461 break;
1462 case 'm':
1463 if (minute != -1)
1464 return false;
1465 if (num == 0)
1466 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1467 else
1468 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1470 if (minute >= 60)
1471 return false;
1473 break;
1474 case 's':
1475 if (second != -1)
1476 return false;
1477 if (num == 0)
1478 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1479 else
1480 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1482 if (second >= 60)
1483 return false;
1485 break;
1486 case 'F':
1487 leading_zeros = false;
1488 goto case 'f';
1489 case 'f':
1490 if (num > 6 || fractionalSeconds != -1)
1491 return false;
1492 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1493 if (num_parsed == -1)
1494 return false;
1495 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1496 break;
1497 case 't':
1498 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1499 return false;
1500 break;
1501 case 'z':
1502 if (tzsign != -1)
1503 return false;
1505 if (s [valuePos] == '+')
1506 tzsign = 0;
1507 else if (s [valuePos] == '-')
1508 tzsign = 1;
1509 else
1510 return false;
1511 valuePos++;
1513 if (num == 0)
1514 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1515 else if (num == 1)
1516 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1517 else {
1518 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
1519 valuePos += num_parsed;
1520 if (num_parsed < 0)
1521 return false;
1523 num_parsed = 0;
1524 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1525 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1526 valuePos += num_parsed;
1527 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1528 if (num_parsed < 0)
1529 return false;
1531 else if (!flexibleTwoPartsParsing)
1532 return false;
1533 else
1534 num_parsed = 0;
1536 break;
1537 case 'K':
1538 if (s [valuePos] == 'Z') {
1539 valuePos++;
1540 useutc = true;
1542 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1543 if (tzsign != -1)
1544 return false;
1545 if (s [valuePos] == '+')
1546 tzsign = 0;
1547 else if (s [valuePos] == '-')
1548 tzsign = 1;
1549 valuePos++;
1551 // zzz
1552 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1553 valuePos += num_parsed;
1554 if (num_parsed < 0)
1555 return false;
1557 if (Char.IsDigit (s [valuePos]))
1558 num_parsed = 0;
1559 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1560 return false;
1561 valuePos += num_parsed;
1563 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1564 num = 2;
1565 if (num_parsed < 0)
1566 return false;
1568 break;
1570 // LAMESPEC: This should be part of UTCpattern
1571 // string and thus should not be considered here.
1573 // Note that 'Z' is not defined as a pattern
1574 // character. Keep it for X509 certificate
1575 // verification. Also, "Z" != "'Z'" under MS.NET
1576 // ("'Z'" is just literal; handled above)
1577 case 'Z':
1578 if (s [valuePos] != 'Z')
1579 return false;
1580 num = 0;
1581 num_parsed = 1;
1582 useutc = true;
1583 break;
1584 case 'G':
1585 if (s [valuePos] != 'G')
1586 return false;
1588 if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
1589 (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
1590 (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
1592 useutc = true;
1593 num = 2;
1594 num_parsed = 3;
1596 else {
1597 num = 0;
1598 num_parsed = 1;
1600 break;
1601 case ':':
1602 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1603 return false;
1604 break;
1605 case '/':
1606 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1607 return false;
1609 num = 0;
1610 break;
1611 default:
1612 if (s [valuePos] != chars [pos])
1613 return false;
1615 num = 0;
1616 num_parsed = 1;
1617 break;
1620 if (num_parsed < 0)
1621 return false;
1623 valuePos += num_parsed;
1625 if (!exact && !flexibleTwoPartsParsing) {
1626 switch (chars [pos]) {
1627 case 'm':
1628 case 's':
1629 case 'F':
1630 case 'f':
1631 case 'z':
1632 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1633 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1634 useutc = true;
1635 valuePos++;
1637 break;
1641 pos = pos + num + 1;
1642 num = 0;
1645 if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
1646 pos++;
1647 while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
1648 pos++;
1650 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1651 pos++;
1653 if (pos < len)
1654 return false;
1656 if (s.Length > valuePos) // extraneous tail.
1658 if (valuePos == 0)
1659 return false;
1661 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1662 return false;
1663 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1664 return false;
1665 incompleteFormat = true;
1666 return false;
1669 if (hour == -1)
1670 hour = 0;
1671 if (minute == -1)
1672 minute = 0;
1674 if (second == -1)
1675 second = 0;
1676 if (fractionalSeconds == -1)
1677 fractionalSeconds = 0;
1679 // If no date was given
1680 if ((day == -1) && (month == -1) && (year == -1)) {
1681 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1682 day = 1;
1683 month = 1;
1684 year = 1;
1685 } else {
1686 day = DateTime.Today.Day;
1687 month = DateTime.Today.Month;
1688 year = DateTime.Today.Year;
1692 if (day == -1)
1693 day = 1;
1694 if (month == -1)
1695 month = 1;
1696 if (year == -1) {
1697 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1698 year = 1;
1699 else
1700 year = DateTime.Today.Year;
1703 if (ampm == 0 && hour == 12)
1704 hour = 0;
1706 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1707 hour = hour + 12;
1709 // For anything out of range
1710 // return false
1711 if (year < 1 || year > 9999 ||
1712 month < 1 || month >12 ||
1713 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1714 hour < 0 || hour > 23 ||
1715 minute < 0 || minute > 59 ||
1716 second < 0 || second > 59)
1717 return false;
1719 result = new DateTime (year, month, day, hour, minute, second, 0);
1720 result = result.AddSeconds(fractionalSeconds);
1722 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1723 return false;
1725 if (tzsign == -1) {
1726 if (result != DateTime.MinValue) {
1727 try {
1728 dto = new DateTimeOffset (result);
1729 } catch { } // We handle this error in DateTimeOffset.Parse
1731 } else {
1732 if (tzoffmin == -1)
1733 tzoffmin = 0;
1734 if (tzoffset == -1)
1735 tzoffset = 0;
1736 if (tzsign == 1) {
1737 tzoffset = -tzoffset;
1738 tzoffmin = -tzoffmin;
1740 try {
1741 dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
1742 } catch {} // We handle this error in DateTimeOffset.Parse
1744 bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
1746 if (tzsign != -1) {
1747 long newticks = (result.ticks - dto.Offset).Ticks;
1748 if (newticks < 0)
1749 newticks += TimeSpan.TicksPerDay;
1750 result = new DateTime (false, new TimeSpan (newticks));
1751 result.kind = DateTimeKind.Utc;
1752 if ((style & DateTimeStyles.RoundtripKind) != 0)
1753 result = result.ToLocalTime ();
1755 else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
1756 result.kind = DateTimeKind.Utc;
1757 else if ((style & DateTimeStyles.AssumeLocal) != 0)
1758 result.kind = DateTimeKind.Local;
1760 bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
1761 if (result.kind != DateTimeKind.Unspecified)
1763 if (adjustToUniversal)
1764 result = result.ToUniversalTime ();
1765 else if (adjustToLocal)
1766 result = result.ToLocalTime ();
1768 return true;
1771 public static DateTime ParseExact (string s, string format,
1772 IFormatProvider provider, DateTimeStyles style)
1774 if (format == null)
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);
1788 CheckStyle (style);
1789 if (s == null)
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.");
1796 DateTime result;
1797 bool longYear = false;
1798 Exception e = null;
1799 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
1800 throw e;
1801 return result;
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, Asersal 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)
1818 if (s != null){
1819 try {
1820 Exception exception = null;
1821 DateTimeOffset dto;
1823 return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
1824 } catch { }
1826 result = MinValue;
1827 return false;
1830 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1832 if (s != null){
1833 try {
1834 Exception exception = null;
1835 DateTimeOffset dto;
1837 return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
1838 } catch {}
1840 result = MinValue;
1841 return false;
1844 public static bool TryParseExact (string s, string format,
1845 IFormatProvider provider,
1846 DateTimeStyles style,
1847 out DateTime result)
1849 string[] formats;
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)
1861 try {
1862 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
1864 bool longYear = false;
1865 Exception e = null;
1866 return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
1867 } catch {
1868 result = MinValue;
1869 return false;
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)
1878 int i;
1879 bool incompleteFormat = false;
1880 for (i = 0; i < formats.Length; i++)
1882 DateTime result;
1883 string format = formats[i];
1884 if (format == null || format == String.Empty)
1885 break;
1887 DateTimeOffset dto;
1888 if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
1889 ret = result;
1890 return true;
1894 if (setExceptionOnError)
1895 exception = new FormatException ("Invalid format string");
1896 ret = DateTime.MinValue;
1897 return false;
1900 public TimeSpan Subtract (DateTime value)
1902 return new TimeSpan (ticks.Ticks) - value.ticks;
1905 public DateTime Subtract(TimeSpan value)
1907 TimeSpan newticks;
1909 newticks = (new TimeSpan (ticks.Ticks)) - value;
1910 DateTime ret = new DateTime (true,newticks);
1911 ret.kind = kind;
1912 return ret;
1915 public long ToFileTime()
1917 DateTime universalTime = ToUniversalTime();
1919 if (universalTime.Ticks < w32file_epoch) {
1920 throw new ArgumentOutOfRangeException("file time is not valid");
1923 return(universalTime.Ticks - w32file_epoch);
1926 public long ToFileTimeUtc()
1928 if (Ticks < w32file_epoch) {
1929 throw new ArgumentOutOfRangeException("file time is not valid");
1932 return (Ticks - w32file_epoch);
1935 public string ToLongDateString()
1937 return ToString ("D");
1940 public string ToLongTimeString()
1942 return ToString ("T");
1945 public double ToOADate ()
1947 long t = this.Ticks;
1948 // uninitialized DateTime case
1949 if (t == 0)
1950 return 0;
1951 // we can't reach minimum value
1952 if (t < 31242239136000000)
1953 return OAMinValue + 0.001;
1955 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1956 double result = ts.TotalDays;
1957 // t < 0 (where 599264352000000000 == 0.0d for OA)
1958 if (t < 599264352000000000) {
1959 // negative days (int) but decimals are positive
1960 double d = Math.Ceiling (result);
1961 result = d - 2 - (result - d);
1963 else {
1964 // we can't reach maximum value
1965 if (result >= OAMaxValue)
1966 result = OAMaxValue - 0.00000001d;
1968 return result;
1971 public string ToShortDateString()
1973 return ToString ("d");
1976 public string ToShortTimeString()
1978 return ToString ("t");
1981 public override string ToString ()
1983 return ToString ("G", null);
1986 public string ToString (IFormatProvider provider)
1988 return ToString (null, provider);
1991 public string ToString (string format)
1993 return ToString (format, null);
1996 public string ToString (string format, IFormatProvider provider)
1998 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
2000 if (format == null || format == String.Empty)
2001 format = "G";
2003 bool useutc = false, use_invariant = false;
2005 if (format.Length == 1) {
2006 char fchar = format [0];
2007 format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2008 if (fchar == 'U')
2009 return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
2010 // return ToUniversalTime()._ToString (format, dfi);
2012 if (format == null)
2013 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2016 // Don't convert UTC value. It just adds 'Z' for
2017 // 'u' format, for the same ticks.
2018 return DateTimeUtils.ToString (this, format, dfi);
2021 public DateTime ToLocalTime ()
2023 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2026 public DateTime ToUniversalTime()
2028 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2031 /* OPERATORS */
2033 public static DateTime operator +(DateTime d, TimeSpan t)
2035 DateTime ret = new DateTime (true, d.ticks + t);
2036 ret.kind = d.kind;
2037 return ret;
2040 public static bool operator ==(DateTime d1, DateTime d2)
2042 return (d1.ticks == d2.ticks);
2045 public static bool operator >(DateTime t1,DateTime t2)
2047 return (t1.ticks > t2.ticks);
2050 public static bool operator >=(DateTime t1,DateTime t2)
2052 return (t1.ticks >= t2.ticks);
2055 public static bool operator !=(DateTime d1, DateTime d2)
2057 return (d1.ticks != d2.ticks);
2060 public static bool operator <(DateTime t1, DateTime t2)
2062 return (t1.ticks < t2.ticks );
2065 public static bool operator <=(DateTime t1,DateTime t2)
2067 return (t1.ticks <= t2.ticks);
2070 public static TimeSpan operator -(DateTime d1,DateTime d2)
2072 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2075 public static DateTime operator -(DateTime d,TimeSpan t)
2077 DateTime ret = new DateTime (true, d.ticks - t);
2078 ret.kind = d.kind;
2079 return ret;
2082 bool IConvertible.ToBoolean(IFormatProvider provider)
2084 throw new InvalidCastException();
2087 byte IConvertible.ToByte(IFormatProvider provider)
2089 throw new InvalidCastException();
2093 char IConvertible.ToChar(IFormatProvider provider)
2095 throw new InvalidCastException();
2098 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2100 return this;
2103 decimal IConvertible.ToDecimal(IFormatProvider provider)
2105 throw new InvalidCastException();
2108 double IConvertible.ToDouble(IFormatProvider provider)
2110 throw new InvalidCastException();
2113 Int16 IConvertible.ToInt16(IFormatProvider provider)
2115 throw new InvalidCastException();
2118 Int32 IConvertible.ToInt32(IFormatProvider provider)
2120 throw new InvalidCastException();
2123 Int64 IConvertible.ToInt64(IFormatProvider provider)
2125 throw new InvalidCastException();
2128 SByte IConvertible.ToSByte(IFormatProvider provider)
2130 throw new InvalidCastException();
2133 Single IConvertible.ToSingle(IFormatProvider provider)
2135 throw new InvalidCastException();
2138 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2140 if (targetType == null)
2141 throw new ArgumentNullException ("targetType");
2143 if (targetType == typeof (DateTime))
2144 return this;
2145 else if (targetType == typeof (String))
2146 return this.ToString (provider);
2147 else if (targetType == typeof (Object))
2148 return this;
2149 else
2150 throw new InvalidCastException();
2153 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2155 throw new InvalidCastException();
2158 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2160 throw new InvalidCastException();
2163 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2165 throw new InvalidCastException();
2168 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
2170 long t = ticks.Ticks;
2171 info.AddValue ("ticks", t);
2173 // This is the new .NET format, encodes the kind on the top bits
2174 info.AddValue ("dateData", t | (((uint)kind) << 62));