5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
9 // (C) 2001 Marcel Narings
10 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
41 /// The DateTime structure represents dates and time ranging from
42 /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
46 [StructLayout (LayoutKind.Auto)]
47 public struct DateTime : IFormattable, IConvertible, IComparable
49 , IComparable<DateTime>, IEquatable <DateTime>
52 private TimeSpan ticks;
58 private const int dp400 = 146097;
59 private const int dp100 = 36524;
60 private const int dp4 = 1461;
62 // w32 file time starts counting from 1/1/1601 00:00 GMT
63 // which is the constant ticks from the .NET epoch
64 private const long w32file_epoch = 504911232000000000L;
66 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
67 // -- Microsoft .NET has this value.
68 private const long MAX_VALUE_TICKS = 3155378975999999999L;
71 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
74 internal const long UnixEpoch = 621355968000000000L;
76 // for OLE Automation dates
77 private const long ticks18991230 = 599264352000000000L;
78 private const double OAMinValue = -657435.0d;
79 private const double OAMaxValue = 2958466.0d;
81 public static readonly DateTime MaxValue = new DateTime (false, new TimeSpan (MAX_VALUE_TICKS));
82 public static readonly DateTime MinValue = new DateTime (false, new TimeSpan (0));
84 // DateTime.Parse patterns
85 // Patterns are divided to date and time patterns. The algorithm will
86 // try combinations of these patterns. The algorithm also looks for
87 // day of the week, AM/PM GMT and Z independently of the patterns.
88 private static readonly string[] ParseTimeFormats = new string [] {
95 "H tt", // Specifies AM to disallow '8'.
96 "H'\u6642'm'\u5206's'\u79D2'",
99 // DateTime.Parse date patterns extend ParseExact patterns as follows:
100 // MMM - month short name or month full name
101 // MMMM - month number or short name or month full name
103 // Parse behaves differently according to the ShorDatePattern of the
104 // DateTimeFormatInfo. The following define the date patterns for
105 // different orders of day, month and year in ShorDatePattern.
106 // Note that the year cannot go between the day and the month.
107 private static readonly string[] ParseYearDayMonthFormats = new string [] {
110 "yyyy'\u5E74'M'\u6708'd'\u65E5",
127 private static readonly string[] ParseYearMonthDayFormats = new string [] {
130 "yyyy'\u5E74'M'\u6708'd'\u65E5",
144 private static readonly string[] ParseDayMonthYearFormats = new string [] {
147 "yyyy'\u5E74'M'\u6708'd'\u65E5",
164 private static readonly string[] ParseMonthDayYearFormats = new string [] {
167 "yyyy'\u5E74'M'\u6708'd'\u65E5",
184 // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
185 // Note that these patterns cannot be followed by the time.
186 private static readonly string[] MonthDayShortFormats = new string [] {
191 private static readonly string[] DayMonthShortFormats = new string [] {
195 #else // In .Net 1.0 Feb 03 is always Feb 3rd (and not Feb 2003)
209 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
210 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
212 private static int AbsoluteDays (int year, int month, int day)
217 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
221 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
224 private int FromTicks(Which what)
226 int num400, num100, num4, numyears;
229 int[] days = daysmonth;
230 int totaldays = this.ticks.Days;
232 num400 = (totaldays / dp400);
233 totaldays -= num400 * dp400;
235 num100 = (totaldays / dp100);
236 if (num100 == 4) // leap
238 totaldays -= (num100 * dp100);
240 num4 = totaldays / dp4;
241 totaldays -= (num4 * dp4);
243 numyears = totaldays / 365 ;
245 if (numyears == 4) //leap
247 if (what == Which.Year )
248 return num400*400 + num100*100 + num4*4 + numyears + 1;
250 totaldays -= (numyears * 365) ;
251 if (what == Which.DayYear )
252 return totaldays + 1;
254 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
255 days = daysmonthleap;
257 while (totaldays >= days[M])
258 totaldays -= days[M++];
260 if (what == Which.Month )
270 /// Constructs a DateTime for specified ticks
273 public DateTime (long ticks)
275 this.ticks = new TimeSpan (ticks);
276 if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
277 string msg = Locale.GetText ("Value {0} is outside the valid range [{1},{2}].",
278 ticks, MinValue.Ticks, MaxValue.Ticks);
279 throw new ArgumentOutOfRangeException ("ticks", msg);
282 kind = DateTimeKind.Unspecified;
286 public DateTime (int year, int month, int day)
287 : this (year, month, day,0,0,0,0) {}
289 public DateTime (int year, int month, int day, int hour, int minute, int second)
290 : this (year, month, day, hour, minute, second, 0) {}
292 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
294 if ( year < 1 || year > 9999 ||
295 month < 1 || month >12 ||
296 day < 1 || day > DaysInMonth(year, month) ||
297 hour < 0 || hour > 23 ||
298 minute < 0 || minute > 59 ||
299 second < 0 || second > 59 ||
300 millisecond < 0 || millisecond > 999)
301 throw new ArgumentOutOfRangeException ("Parameters describe an " +
302 "unrepresentable DateTime.");
304 ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
307 kind = DateTimeKind.Unspecified;
311 public DateTime (int year, int month, int day, Calendar calendar)
312 : this (year, month, day, 0, 0, 0, 0, calendar)
316 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
317 : this (year, month, day, hour, minute, second, 0, calendar)
321 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
323 if (calendar == null)
324 throw new ArgumentNullException ("calendar");
325 ticks = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).ticks;
327 kind = DateTimeKind.Unspecified;
331 internal DateTime (bool check, TimeSpan value)
333 if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
334 throw new ArgumentOutOfRangeException ();
339 kind = DateTimeKind.Unspecified;
344 public DateTime (long ticks, DateTimeKind kind) : this (ticks)
346 CheckDateTimeKind (kind);
350 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
351 : this (year, month, day, hour, minute, second)
353 CheckDateTimeKind (kind);
357 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
358 : this (year, month, day, hour, minute, second, millisecond)
360 CheckDateTimeKind (kind);
364 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
365 : this (year, month, day, hour, minute, second, millisecond, calendar)
367 CheckDateTimeKind (kind);
378 DateTime ret = new DateTime (Year, Month, Day);
390 return FromTicks(Which.Month);
398 return FromTicks(Which.Day);
402 public DayOfWeek DayOfWeek
406 return ( (DayOfWeek) ((ticks.Days+1) % 7) );
414 return FromTicks(Which.DayYear);
418 public TimeSpan TimeOfDay
422 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
439 return ticks.Minutes;
447 return ticks.Seconds;
451 public int Millisecond
455 return ticks.Milliseconds;
459 [MethodImplAttribute(MethodImplOptions.InternalCall)]
460 internal static extern long GetNow ();
463 // To reduce the time consumed by DateTime.Now, we keep
464 // the difference to map the system time into a local
465 // time into `to_local_time_span', we record the timestamp
466 // for this in `last_now'
468 static object to_local_time_span_object;
469 static long last_now;
471 public static DateTime Now
475 long now = GetNow ();
476 DateTime dt = new DateTime (now);
478 if ((now - last_now) > TimeSpan.TicksPerMinute){
479 to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
484 // This is boxed, so we avoid locking.
485 DateTime ret = dt + (TimeSpan) to_local_time_span_object;
487 ret.kind = DateTimeKind.Local;
501 public static DateTime Today
505 DateTime today = new DateTime (now.Year, now.Month, now.Day);
507 today.kind = now.kind;
513 public static DateTime UtcNow
517 return new DateTime (GetNow (), DateTimeKind.Utc);
519 return new DateTime (GetNow ());
528 return FromTicks(Which.Year);
533 public DateTimeKind Kind {
542 public DateTime Add (TimeSpan ts)
544 DateTime ret = AddTicks (ts.Ticks);
551 public DateTime AddDays (double days)
553 return AddMilliseconds (Math.Round (days * 86400000));
556 public DateTime AddTicks (long t)
558 if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
559 throw new ArgumentOutOfRangeException();
561 DateTime ret = new DateTime (t + ticks.Ticks);
568 public DateTime AddHours (double hours)
570 return AddMilliseconds (hours * 3600000);
573 public DateTime AddMilliseconds (double ms)
575 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
576 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
577 throw new ArgumentOutOfRangeException();
579 long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
581 return AddTicks (msticks);
584 // required to match MS implementation for OADate (OLE Automation)
585 private DateTime AddRoundedMilliseconds (double ms)
587 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
588 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
589 throw new ArgumentOutOfRangeException ();
591 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
593 return AddTicks (msticks);
596 public DateTime AddMinutes (double minutes)
598 return AddMilliseconds (minutes * 60000);
601 public DateTime AddMonths (int months)
603 int day, month, year, maxday ;
607 month = this.Month + (months % 12);
608 year = this.Year + months/12 ;
620 maxday = DaysInMonth(year, month);
624 temp = new DateTime (year, month, day);
628 return temp.Add (this.TimeOfDay);
631 public DateTime AddSeconds (double seconds)
633 return AddMilliseconds (seconds*1000);
636 public DateTime AddYears (int years )
638 return AddMonths(years * 12);
641 public static int Compare (DateTime t1, DateTime t2)
643 if (t1.ticks < t2.ticks)
645 else if (t1.ticks > t2.ticks)
651 public int CompareTo (object v)
656 if (!(v is System.DateTime))
657 throw new ArgumentException (Locale.GetText (
658 "Value is not a System.DateTime"));
660 return Compare (this, (DateTime) v);
664 public bool IsDaylightSavingTime ()
666 if (kind == DateTimeKind.Utc)
668 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
671 public int CompareTo (DateTime value)
673 return Compare (this, value);
676 public bool Equals (DateTime value)
678 return value.ticks == ticks;
681 public long ToBinary ()
684 case DateTimeKind.Utc:
685 return Ticks | 0x4000000000000000;
686 case DateTimeKind.Local:
687 return (long) ((ulong)Ticks | 0x8000000000000000);
693 public static DateTime FromBinary (long dateData)
695 switch ((ulong)dateData >> 62) {
697 return new DateTime (dateData ^ 0x4000000000000000, DateTimeKind.Utc);
699 return new DateTime (dateData, DateTimeKind.Unspecified);
701 return new DateTime (dateData & 0x3fffffffffffffff, DateTimeKind.Local);
705 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
707 return new DateTime (value.Ticks, kind);
712 internal long ToBinary ()
717 internal static DateTime FromBinary (long dateData)
719 return new DateTime (dateData & 0x3fffffffffffffff);
723 public static int DaysInMonth (int year, int month)
727 if (month < 1 || month >12)
728 throw new ArgumentOutOfRangeException ();
730 if (year < 1 || year > 9999)
731 throw new ArgumentOutOfRangeException ();
733 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
737 public override bool Equals (object o)
739 if (!(o is System.DateTime))
742 return ((DateTime) o).ticks == ticks;
745 public static bool Equals (DateTime t1, DateTime t2 )
747 return (t1.ticks == t2.ticks );
750 public static DateTime FromFileTime (long fileTime)
753 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
755 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
759 public static DateTime FromFileTimeUtc (long fileTime)
762 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
764 return new DateTime (w32file_epoch + fileTime);
768 public static DateTime FromOADate (double d)
770 // An OLE Automation date is implemented as a floating-point number
771 // whose value is the number of days from midnight, 30 December 1899.
773 // d must be negative 657435.0 through positive 2958466.0.
774 if ((d <= OAMinValue) || (d >= OAMaxValue))
775 throw new ArgumentException ("d", "[-657435,2958466]");
777 DateTime dt = new DateTime (ticks18991230);
779 Double days = Math.Ceiling (d);
780 // integer part is the number of days (negative)
781 dt = dt.AddRoundedMilliseconds (days * 86400000);
782 // but decimals are the number of hours (in days fractions) and positive
783 Double hours = (days - d);
784 dt = dt.AddRoundedMilliseconds (hours * 86400000);
787 dt = dt.AddRoundedMilliseconds (d * 86400000);
793 public string[] GetDateTimeFormats()
795 return GetDateTimeFormats (CultureInfo.CurrentCulture);
798 public string[] GetDateTimeFormats(char format)
800 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
801 throw new FormatException ("Invalid format character.");
802 string[] result = new string[1];
803 result[0] = this.ToString(format.ToString());
807 public string[] GetDateTimeFormats(IFormatProvider provider)
809 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
810 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
811 ArrayList al = new ArrayList ();
812 foreach (char c in "dDgGfFmMrRstTuUyY")
813 al.AddRange (GetDateTimeFormats (c, info));
814 return al.ToArray (typeof (string)) as string [];
817 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
819 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
820 throw new FormatException ("Invalid format character.");
822 // LAMESPEC: There is NO assurance that 'U' ALWAYS
823 // euqals to 'F', but since we have to iterate all
824 // the pattern strings, we cannot just use
825 // ToString("U", provider) here. I believe that the
826 // method's behavior cannot be formalized.
827 bool adjustutc = false;
836 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
837 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
840 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
842 string [] results = new string [patterns.Length];
843 DateTime val = adjustutc ? ToUniversalTime () : this;
844 for (int i = 0; i < results.Length; i++)
845 results [i] = val._ToString (patterns [i], dfi);
850 private void CheckDateTimeKind (DateTimeKind kind) {
851 if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
852 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
856 public override int GetHashCode ()
858 return (int) ticks.Ticks;
861 public TypeCode GetTypeCode ()
863 return TypeCode.DateTime;
866 public static bool IsLeapYear (int year)
868 if (year < 1 || year > 9999)
869 throw new ArgumentOutOfRangeException ();
870 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
873 public static DateTime Parse (string s)
875 return Parse (s, null);
878 public static DateTime Parse (string s, IFormatProvider fp)
880 return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
883 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
886 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
888 const string argumentYearRangeExceptionMessage = "Valid values are between 1 and 9999, inclusive.";
892 throw new ArgumentNullException (Locale.GetText ("s is null"));
894 fp = CultureInfo.CurrentCulture;
895 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
897 bool longYear = false;
899 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
900 string[] allDateFormats = YearMonthDayFormats (dfi);
901 for (int i = 0; i < allDateFormats.Length; i++) {
902 string firstPart = allDateFormats [i];
903 bool incompleteFormat = false;
904 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
906 if (!incompleteFormat)
909 for (int j = 0; j < ParseTimeFormats.Length; j++) {
910 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
914 string[] monthDayFormats = IsDayBeforeMonth (dfi) ? DayMonthShortFormats : MonthDayShortFormats;
915 for (int i = 0; i < monthDayFormats.Length; i++) {
916 bool incompleteFormat = false;
917 if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
920 for (int j = 0; j < ParseTimeFormats.Length; j++) {
921 string firstPart = ParseTimeFormats [j];
922 bool incompleteFormat = false;
923 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
925 if (!incompleteFormat)
928 for (int i = 0; i < monthDayFormats.Length; i++) {
929 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
932 for (int i = 0; i < allDateFormats.Length; i++) {
933 string dateFormat = allDateFormats [i];
934 if (dateFormat[dateFormat.Length - 1] == 'T')
935 continue; // T formats must be before the time part
936 if (_DoParse (s, firstPart, dateFormat, false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
941 // Try as a last resort all the patterns
942 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
946 // .NET does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
947 throw new FormatException (formatExceptionMessage);
950 throw new ArgumentOutOfRangeException ("year",
951 argumentYearRangeExceptionMessage);
954 throw new FormatException (formatExceptionMessage);
958 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
960 return ParseExact (s, format, fp, DateTimeStyles.None);
963 private static bool IsDayBeforeMonth (DateTimeFormatInfo dfi)
965 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
966 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
967 if (dayIndex == -1 || monthIndex == -1)
968 throw new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
970 return dayIndex < monthIndex;
973 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
975 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
976 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
977 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
978 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
979 throw new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
981 if (yearIndex < monthIndex)
982 if (monthIndex < dayIndex)
983 return ParseYearMonthDayFormats;
984 else if (yearIndex < dayIndex)
985 return ParseYearDayMonthFormats;
987 // The year cannot be between the date and the month
988 throw new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
989 else if (dayIndex < monthIndex)
990 return ParseDayMonthYearFormats;
991 else if (dayIndex < yearIndex)
992 return ParseMonthDayYearFormats;
994 // The year cannot be between the month and the date
995 throw new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
998 private static int _ParseNumber (string s, int valuePos,
1002 bool sloppy_parsing,
1008 leadingzero = false;
1011 int real_digits = 0;
1012 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
1013 if (!Char.IsDigit (s[i]))
1019 digits = real_digits;
1021 if (digits < min_digits) {
1026 if (s.Length - valuePos < digits) {
1031 for (i = valuePos; i < digits + valuePos; i++) {
1033 if (!Char.IsDigit (c)) {
1038 number = number * 10 + (byte) (c - '0');
1041 num_parsed = digits;
1045 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1047 // FIXME: I know this is somehow lame code. Probably
1048 // it should iterate all the enum value and return
1049 // the longest match. However right now I don't see
1050 // anything but "1" and "10" - "12" that might match
1051 // two or more values. (They are only abbrev month
1052 // names, so do reverse order search). See bug #80094.
1053 for (int i = values.Length - 1; i >= 0; i--) {
1054 if (!exact && invValues [i].Length > values[i].Length) {
1055 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1057 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1061 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1063 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1072 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1075 maxlength = value.Length;
1077 if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1078 num_parsed = maxlength;
1086 // Note that in case of Parse (exact == false) we check both for AM/PM
1087 // and the culture spcific AM/PM strings.
1088 private static bool _ParseAmPm(string s,
1091 DateTimeFormatInfo dfi,
1100 if (!IsLetter (s, valuePos)) {
1101 if (dfi.AMDesignator != "")
1108 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1109 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1110 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1112 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1113 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1114 if (exact || num_parsed != 0)
1122 // Note that in case of Parse (exact == false) we check both for ':'
1123 // and the culture spcific TimeSperator
1124 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1126 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1127 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1130 // Accept any character for DateSeparator, except TimeSeparator,
1131 // a digit or a letter.
1132 // Not documented, but seems to be MS behaviour here. See bug 54047.
1133 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1136 if (exact && s [sPos] != '/')
1139 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1140 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1147 private static bool IsLetter (string s, int pos)
1149 return pos < s.Length && Char.IsLetter (s [pos]);
1152 // To implement better DateTime.Parse we use two format strings one
1153 // for Date and one for Time. This allows us to define two different
1154 // arrays of formats for Time and Dates and to combine them more or less
1155 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1156 private static bool _DoParse (string s,
1160 out DateTime result,
1161 DateTimeFormatInfo dfi,
1162 DateTimeStyles style,
1163 bool firstPartIsDate,
1164 ref bool incompleteFormat,
1168 DateTimeKind explicit_kind = DateTimeKind.Unspecified;
1170 bool useutc = false, use_localtime = true;
1171 bool use_invariant = false;
1172 bool sloppy_parsing = false;
1173 bool afterTimePart = firstPartIsDate && secondPart == "";
1174 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1175 incompleteFormat = false;
1177 string format = firstPart;
1178 bool afterTFormat = false;
1179 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1180 if (format.Length == 1) {
1182 use_localtime = false;
1183 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1185 else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
1188 if ((style & DateTimeStyles.AssumeUniversal) != 0)
1192 result = new DateTime (0);
1196 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1197 format = format.TrimStart (null);
1199 s = s.TrimStart (null); // it could be optimized, but will make little good.
1202 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1203 format = format.TrimEnd (null);
1204 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1210 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1211 sloppy_parsing = true;
1213 string chars = format;
1214 int len = format.Length, pos = 0, num = 0;
1218 int day = -1, dayofweek = -1, month = -1, year = -1;
1219 int hour = -1, minute = -1, second = -1;
1220 double fractionalSeconds = -1;
1222 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1223 bool isFirstPart = true;
1227 if (valuePos == s.Length)
1231 if (flexibleTwoPartsParsing && pos + num == 0)
1233 bool isLetter = IsLetter(s, valuePos);
1234 if (afterTimePart && isLetter) {
1235 if (s [valuePos] == 'Z')
1238 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1239 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1240 valuePos += num_parsed;
1245 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1246 if (IsLetter (s, valuePos + num_parsed))
1248 else if (num_parsed > 0) {
1249 valuePos += num_parsed;
1254 if (!afterTFormat && dayofweek == -1 && isLetter) {
1255 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1256 if (dayofweek == -1)
1257 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1258 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1259 valuePos += num_parsed;
1266 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1273 if (pos + num >= len)
1275 if (flexibleTwoPartsParsing && num == 0) {
1276 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1277 if (!isFirstPart && format == "")
1282 format = secondPart;
1287 isFirstPart = false;
1288 if (!firstPartIsDate || format == "")
1289 afterTimePart = true;
1295 bool leading_zeros = true;
1297 if (chars[pos] == '\'') {
1299 while (pos+num < len) {
1300 if (chars[pos+num] == '\'')
1303 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1313 } else if (chars[pos] == '"') {
1315 while (pos+num < len) {
1316 if (chars[pos+num] == '"')
1319 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1329 } else if (chars[pos] == '\\') {
1334 if (s [valuePos] != chars [pos])
1340 } else if (chars[pos] == '%') {
1343 } else if (char.IsWhiteSpace (s [valuePos]) ||
1344 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1347 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1348 if (!Char.IsWhiteSpace (chars[pos]))
1355 while (ws < s.Length) {
1356 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1363 while (ws < chars.Length) {
1364 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1370 // A whitespace may match a '/' in the pattern.
1371 if (!exact && pos < chars.Length && chars[pos] == '/')
1372 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1377 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1385 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1388 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1390 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1392 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1394 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1400 if (flexibleTwoPartsParsing) {
1402 if (num == 0 || num == 3)
1403 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1404 if (num > 1 && num_parsed == -1)
1405 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1406 if (num > 1 && num_parsed == -1)
1407 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1412 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1414 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1416 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1418 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1425 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1426 } else if (num < 3) {
1427 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1429 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1430 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1432 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1433 longYear = (ly > 9999);
1438 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1439 if (num_parsed <= 2)
1440 year += (year < 30) ? 2000 : 1900;
1446 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1448 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1457 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1460 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1462 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1473 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1475 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1485 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1487 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1495 leading_zeros = false;
1499 if (num > 6 || fractionalSeconds != -1)
1501 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1502 if (num_parsed == -1)
1504 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1507 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1514 if (s [valuePos] == '+')
1516 else if (s [valuePos] == '-')
1523 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1525 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1527 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1528 valuePos += num_parsed;
1533 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1534 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1535 valuePos += num_parsed;
1536 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1540 else if (!flexibleTwoPartsParsing)
1548 if (s [valuePos] == 'Z') {
1550 explicit_kind = DateTimeKind.Utc;
1552 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1555 if (s [valuePos] == '+')
1557 else if (s [valuePos] == '-')
1562 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1563 valuePos += num_parsed;
1567 if (Char.IsDigit (s [valuePos]))
1569 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1571 valuePos += num_parsed;
1573 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1575 explicit_kind = DateTimeKind.Local;
1581 // LAMESPEC: This should be part of UTCpattern
1582 // string and thus should not be considered here.
1584 // Note that 'Z' is not defined as a pattern
1585 // character. Keep it for X509 certificate
1586 // verification. Also, "Z" != "'Z'" under MS.NET
1587 // ("'Z'" is just literal; handled above)
1589 if (s [valuePos] != 'Z')
1597 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1601 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1607 if (s [valuePos] != chars [pos])
1618 valuePos += num_parsed;
1620 if (!exact && !flexibleTwoPartsParsing) {
1621 switch (chars [pos]) {
1629 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1630 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1638 pos = pos + num + 1;
1645 if (s.Length > valuePos) // extraneous tail.
1650 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1652 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1654 incompleteFormat = true;
1665 if (fractionalSeconds == -1)
1666 fractionalSeconds = 0;
1668 // If no date was given
1669 if ((day == -1) && (month == -1) && (year == -1)) {
1670 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1675 day = DateTime.Today.Day;
1676 month = DateTime.Today.Month;
1677 year = DateTime.Today.Year;
1686 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1689 year = DateTime.Today.Year;
1692 if (ampm == 0 && hour == 12)
1695 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1698 // For anything out of range
1700 if (year < 1 || year > 9999 ||
1701 month < 1 || month >12 ||
1702 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1703 hour < 0 || hour > 23 ||
1704 minute < 0 || minute > 59 ||
1705 second < 0 || second > 59)
1708 result = new DateTime (year, month, day, hour, minute, second, 0);
1709 result = result.AddSeconds(fractionalSeconds);
1711 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1714 // If no timezone was specified, default to the local timezone.
1718 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1719 use_localtime = false;
1720 utcoffset = new TimeSpan (0, 0, 0);
1721 } else if (tzsign == -1) {
1722 TimeZone tz = TimeZone.CurrentTimeZone;
1723 utcoffset = tz.GetUtcOffset (result);
1725 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1726 use_localtime = false;
1733 tzoffset = -tzoffset;
1735 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1738 long newticks = (result.ticks - utcoffset).Ticks;
1740 result = new DateTime (false, new TimeSpan (newticks));
1742 if (explicit_kind != DateTimeKind.Unspecified)
1743 result.kind = explicit_kind;
1744 else if (use_localtime)
1745 result = result.ToLocalTime ();
1747 result.kind = DateTimeKind.Utc;
1750 result = result.ToLocalTime ();
1756 public static DateTime ParseExact (string s, string format,
1757 IFormatProvider fp, DateTimeStyles style)
1760 throw new ArgumentNullException ("format");
1762 string [] formats = new string [1];
1763 formats[0] = format;
1765 return ParseExact (s, formats, fp, style);
1768 public static DateTime ParseExact (string s, string[] formats,
1770 DateTimeStyles style)
1772 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1775 throw new ArgumentNullException ("s");
1776 if (formats == null)
1777 throw new ArgumentNullException ("formats");
1778 if (formats.Length == 0)
1779 throw new FormatException ("Format specifier was invalid.");
1782 bool longYear = false;
1783 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1784 throw new FormatException ();
1789 public static bool TryParse (string s, out DateTime result)
1800 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1803 result = Parse (s, provider, styles);
1811 public static bool TryParseExact (string s, string format,
1813 DateTimeStyles style,
1814 out DateTime result)
1818 formats = new string [1];
1819 formats[0] = format;
1821 return TryParseExact (s, formats, fp, style, out result);
1824 public static bool TryParseExact (string s, string[] formats,
1826 DateTimeStyles style,
1827 out DateTime result)
1829 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1831 bool longYear = false;
1832 return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1836 private static bool ParseExact (string s, string [] formats,
1837 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1838 bool exact, ref bool longYear)
1841 bool incompleteFormat = false;
1842 for (i = 0; i < formats.Length; i++)
1845 string format = formats[i];
1846 if (format == null || format == String.Empty)
1847 throw new FormatException ("Invalid Format String");
1849 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1854 ret = DateTime.MinValue;
1858 public TimeSpan Subtract(DateTime dt)
1860 return new TimeSpan(ticks.Ticks) - dt.ticks;
1863 public DateTime Subtract(TimeSpan ts)
1867 newticks = (new TimeSpan (ticks.Ticks)) - ts;
1868 DateTime ret = new DateTime(true,newticks);
1875 public long ToFileTime()
1877 DateTime universalTime = ToUniversalTime();
1879 if (universalTime.Ticks < w32file_epoch) {
1880 throw new ArgumentOutOfRangeException("file time is not valid");
1883 return(universalTime.Ticks - w32file_epoch);
1887 public long ToFileTimeUtc()
1889 if (Ticks < w32file_epoch) {
1890 throw new ArgumentOutOfRangeException("file time is not valid");
1893 return (Ticks - w32file_epoch);
1897 public string ToLongDateString()
1899 return ToString ("D");
1902 public string ToLongTimeString()
1904 return ToString ("T");
1907 public double ToOADate ()
1909 long t = this.Ticks;
1910 // uninitialized DateTime case
1913 // we can't reach minimum value
1914 if (t < 31242239136000000)
1915 return OAMinValue + 0.001;
1917 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1918 double result = ts.TotalDays;
1919 // t < 0 (where 599264352000000000 == 0.0d for OA)
1920 if (t < 599264352000000000) {
1921 // negative days (int) but decimals are positive
1922 double d = Math.Ceiling (result);
1923 result = d - 2 - (result - d);
1926 // we can't reach maximum value
1927 if (result >= OAMaxValue)
1928 result = OAMaxValue - 0.00000001d;
1933 public string ToShortDateString()
1935 return ToString ("d");
1938 public string ToShortTimeString()
1940 return ToString ("t");
1943 public override string ToString ()
1945 return ToString ("G", null);
1948 public string ToString (IFormatProvider fp)
1950 return ToString (null, fp);
1953 public string ToString (string format)
1955 return ToString (format, null);
1958 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1963 use_invariant = false;
1968 pattern = dfi.ShortDatePattern;
1971 pattern = dfi.LongDatePattern;
1974 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1977 pattern = dfi.FullDateTimePattern;
1980 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1983 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1987 pattern = dfi.MonthDayPattern;
1991 pattern = dfi.RoundtripPattern;
1996 pattern = dfi.RFC1123Pattern;
1997 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1998 // uncommented by AE 27/may/2004
2000 use_invariant = true;
2003 pattern = dfi.SortableDateTimePattern;
2006 pattern = dfi.ShortTimePattern;
2009 pattern = dfi.LongTimePattern;
2012 pattern = dfi.UniversalSortableDateTimePattern;
2016 // pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
2017 pattern = dfi.FullDateTimePattern;
2022 pattern = dfi.YearMonthPattern;
2027 // throw new FormatException (String.Format ("Invalid format pattern: '{0}'", format));
2033 internal string _ToString (string format, DateTimeFormatInfo dfi)
2035 // the length of the format is usually a good guess of the number
2036 // of chars in the result. Might save us a few bytes sometimes
2037 // Add + 10 for cases like mmmm dddd
2038 StringBuilder result = new StringBuilder (format.Length + 10);
2040 // For some cases, the output should not use culture dependent calendar
2041 DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
2042 if (format == inv.RFC1123Pattern)
2044 else if (format == inv.UniversalSortableDateTimePattern)
2049 while (i < format.Length) {
2051 bool omitZeros = false;
2052 char ch = format [i];
2061 tokLen = CountRepeat (format, i, ch);
2063 int hr = this.Hour % 12;
2067 ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
2071 tokLen = CountRepeat (format, i, ch);
2072 ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
2076 tokLen = CountRepeat (format, i, ch);
2077 ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
2081 tokLen = CountRepeat (format, i, ch);
2082 ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
2090 // fraction of second, to same number of
2091 // digits as there are f's
2093 tokLen = CountRepeat (format, i, ch);
2095 throw new FormatException ("Invalid Format String");
2097 int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
2098 int startLen = result.Length;
2099 ZeroPad (result, dec, tokLen);
2102 while (result.Length > startLen && result [result.Length - 1] == '0')
2104 // when the value was 0, then trim even preceding '.' (!) It is fixed character.
2105 if (dec == 0 && startLen > 0 && result [startLen - 1] == '.')
2111 // AM/PM. t == first char, tt+ == full
2112 tokLen = CountRepeat (format, i, ch);
2113 string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
2116 if (desig.Length >= 1)
2117 result.Append (desig [0]);
2120 result.Append (desig);
2124 // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
2125 tokLen = CountRepeat (format, i, ch);
2126 TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2128 if (offset.Ticks >= 0)
2129 result.Append ('+');
2131 result.Append ('-');
2135 result.Append (Math.Abs (offset.Hours));
2138 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2141 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2142 result.Append (':');
2143 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2148 case 'K': // 'Z' (UTC) or zzz (Local)
2151 case DateTimeKind.Utc:
2152 result.Append ('Z');
2154 case DateTimeKind.Local:
2155 offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2156 if (offset.Ticks >= 0)
2157 result.Append ('+');
2159 result.Append ('-');
2160 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2161 result.Append (':');
2162 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2171 // day. d(d?) = day of month (leading 0 if two d's)
2172 // ddd = three leter day of week
2173 // dddd+ full day-of-week
2174 tokLen = CountRepeat (format, i, ch);
2177 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
2178 else if (tokLen == 3)
2179 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
2181 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
2185 // Month.m(m?) = month # (with leading 0 if two mm)
2186 // mmm = 3 letter name
2187 // mmmm+ = full name
2188 tokLen = CountRepeat (format, i, ch);
2189 int month = dfi.Calendar.GetMonth(this);
2191 ZeroPad (result, month, tokLen);
2192 else if (tokLen == 3)
2193 result.Append (dfi.GetAbbreviatedMonthName (month));
2195 result.Append (dfi.GetMonthName (month));
2199 // Year. y(y?) = two digit year, with leading 0 if yy
2200 // yyy+ full year with leading zeros if needed.
2201 tokLen = CountRepeat (format, i, ch);
2204 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
2206 ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
2211 tokLen = CountRepeat (format, i, ch);
2212 result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
2219 result.Append (dfi.TimeSeparator);
2223 result.Append (dfi.DateSeparator);
2226 case '\'': case '"':
2227 tokLen = ParseQuotedString (format, i, result);
2230 if (i >= format.Length - 1)
2231 throw new FormatException ("% at end of date time string");
2232 if (format [i + 1] == '%')
2233 throw new FormatException ("%% in date string");
2235 // Look for the next char
2240 if (i >= format.Length - 1)
2241 throw new FormatException ("\\ at end of date time string");
2243 result.Append (format [i + 1]);
2255 return result.ToString ();
2258 static int CountRepeat (string fmt, int p, char c)
2262 while ((i < l) && (fmt [i] == c))
2268 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
2270 // pos == position of " or '
2272 int len = fmt.Length;
2274 char quoteChar = fmt [pos++];
2277 char ch = fmt [pos++];
2279 if (ch == quoteChar)
2285 throw new FormatException("Un-ended quote");
2287 output.Append (fmt [pos++]);
2293 throw new FormatException("Un-ended quote");
2296 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
2298 // more than enough for an int
2299 char* buffer = stackalloc char [16];
2303 buffer [-- pos] = (char) ('0' + digits % 10);
2306 } while (digits > 0);
2309 buffer [-- pos] = '0';
2311 output.Append (new string (buffer, pos, 16 - pos));
2314 public string ToString (string format, IFormatProvider fp)
2316 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
2318 if (format == null || format == String.Empty)
2321 bool useutc = false, use_invariant = false;
2323 if (format.Length == 1) {
2324 char fchar = format [0];
2325 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2327 return ToUniversalTime()._ToString (format, dfi);
2330 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2333 // Don't convert UTC value. It just adds 'Z' for
2334 // 'u' format, for the same ticks.
2335 return this._ToString (format, dfi);
2338 public DateTime ToLocalTime ()
2340 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2343 public DateTime ToUniversalTime()
2345 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2350 public static DateTime operator +(DateTime d, TimeSpan t)
2352 DateTime ret = new DateTime (true, d.ticks + t);
2359 public static bool operator ==(DateTime d1, DateTime d2)
2361 return (d1.ticks == d2.ticks);
2364 public static bool operator >(DateTime t1,DateTime t2)
2366 return (t1.ticks > t2.ticks);
2369 public static bool operator >=(DateTime t1,DateTime t2)
2371 return (t1.ticks >= t2.ticks);
2374 public static bool operator !=(DateTime d1, DateTime d2)
2376 return (d1.ticks != d2.ticks);
2379 public static bool operator <(DateTime t1, DateTime t2)
2381 return (t1.ticks < t2.ticks );
2384 public static bool operator <=(DateTime t1,DateTime t2)
2386 return (t1.ticks <= t2.ticks);
2389 public static TimeSpan operator -(DateTime d1,DateTime d2)
2391 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2394 public static DateTime operator -(DateTime d,TimeSpan t)
2396 DateTime ret = new DateTime (true, d.ticks - t);
2403 bool IConvertible.ToBoolean(IFormatProvider provider)
2405 throw new InvalidCastException();
2408 byte IConvertible.ToByte(IFormatProvider provider)
2410 throw new InvalidCastException();
2414 char IConvertible.ToChar(IFormatProvider provider)
2416 throw new InvalidCastException();
2419 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2424 decimal IConvertible.ToDecimal(IFormatProvider provider)
2426 throw new InvalidCastException();
2429 double IConvertible.ToDouble(IFormatProvider provider)
2431 throw new InvalidCastException();
2434 Int16 IConvertible.ToInt16(IFormatProvider provider)
2436 throw new InvalidCastException();
2439 Int32 IConvertible.ToInt32(IFormatProvider provider)
2441 throw new InvalidCastException();
2444 Int64 IConvertible.ToInt64(IFormatProvider provider)
2446 throw new InvalidCastException();
2449 SByte IConvertible.ToSByte(IFormatProvider provider)
2451 throw new InvalidCastException();
2454 Single IConvertible.ToSingle(IFormatProvider provider)
2456 throw new InvalidCastException();
2459 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2461 if (conversionType == null)
2462 throw new ArgumentNullException ("conversionType");
2464 if (conversionType == typeof (DateTime))
2466 else if (conversionType == typeof (String))
2467 return this.ToString (provider);
2468 else if (conversionType == typeof (Object))
2471 throw new InvalidCastException();
2474 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2476 throw new InvalidCastException();
2479 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2481 throw new InvalidCastException();
2484 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2486 throw new InvalidCastException();