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) ToUniversalTime ().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.Utc).ToLocalTime ();
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') {
1551 explicit_kind = DateTimeKind.Utc;
1553 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1556 if (s [valuePos] == '+')
1558 else if (s [valuePos] == '-')
1563 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1564 valuePos += num_parsed;
1568 if (Char.IsDigit (s [valuePos]))
1570 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1572 valuePos += num_parsed;
1574 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1576 explicit_kind = DateTimeKind.Local;
1582 // LAMESPEC: This should be part of UTCpattern
1583 // string and thus should not be considered here.
1585 // Note that 'Z' is not defined as a pattern
1586 // character. Keep it for X509 certificate
1587 // verification. Also, "Z" != "'Z'" under MS.NET
1588 // ("'Z'" is just literal; handled above)
1590 if (s [valuePos] != 'Z')
1598 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1602 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1608 if (s [valuePos] != chars [pos])
1619 valuePos += num_parsed;
1621 if (!exact && !flexibleTwoPartsParsing) {
1622 switch (chars [pos]) {
1630 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1631 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1639 pos = pos + num + 1;
1644 while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
1650 if (s.Length > valuePos) // extraneous tail.
1655 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1657 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1659 incompleteFormat = true;
1670 if (fractionalSeconds == -1)
1671 fractionalSeconds = 0;
1673 // If no date was given
1674 if ((day == -1) && (month == -1) && (year == -1)) {
1675 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1680 day = DateTime.Today.Day;
1681 month = DateTime.Today.Month;
1682 year = DateTime.Today.Year;
1691 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1694 year = DateTime.Today.Year;
1697 if (ampm == 0 && hour == 12)
1700 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1703 // For anything out of range
1705 if (year < 1 || year > 9999 ||
1706 month < 1 || month >12 ||
1707 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1708 hour < 0 || hour > 23 ||
1709 minute < 0 || minute > 59 ||
1710 second < 0 || second > 59)
1713 result = new DateTime (year, month, day, hour, minute, second, 0);
1714 result = result.AddSeconds(fractionalSeconds);
1716 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1719 // If no timezone was specified, default to the local timezone.
1723 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1724 use_localtime = false;
1725 utcoffset = new TimeSpan (0, 0, 0);
1726 } else if (tzsign == -1) {
1727 TimeZone tz = TimeZone.CurrentTimeZone;
1728 utcoffset = tz.GetUtcOffset (result);
1730 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1731 use_localtime = false;
1738 tzoffset = -tzoffset;
1740 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1743 long newticks = (result.ticks - utcoffset).Ticks;
1745 result = new DateTime (false, new TimeSpan (newticks));
1747 if (explicit_kind != DateTimeKind.Unspecified)
1748 result.kind = explicit_kind;
1749 else if (use_localtime)
1750 result = result.ToLocalTime ();
1752 result.kind = DateTimeKind.Utc;
1755 result = result.ToLocalTime ();
1761 public static DateTime ParseExact (string s, string format,
1762 IFormatProvider fp, DateTimeStyles style)
1765 throw new ArgumentNullException ("format");
1767 string [] formats = new string [1];
1768 formats[0] = format;
1770 return ParseExact (s, formats, fp, style);
1773 public static DateTime ParseExact (string s, string[] formats,
1775 DateTimeStyles style)
1777 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1780 throw new ArgumentNullException ("s");
1781 if (formats == null)
1782 throw new ArgumentNullException ("formats");
1783 if (formats.Length == 0)
1784 throw new FormatException ("Format specifier was invalid.");
1787 bool longYear = false;
1788 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1789 throw new FormatException ();
1794 public static bool TryParse (string s, out DateTime result)
1805 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1808 result = Parse (s, provider, styles);
1816 public static bool TryParseExact (string s, string format,
1818 DateTimeStyles style,
1819 out DateTime result)
1823 formats = new string [1];
1824 formats[0] = format;
1826 return TryParseExact (s, formats, fp, style, out result);
1829 public static bool TryParseExact (string s, string[] formats,
1831 DateTimeStyles style,
1832 out DateTime result)
1834 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1836 bool longYear = false;
1837 return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1841 private static bool ParseExact (string s, string [] formats,
1842 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1843 bool exact, ref bool longYear)
1846 bool incompleteFormat = false;
1847 for (i = 0; i < formats.Length; i++)
1850 string format = formats[i];
1851 if (format == null || format == String.Empty)
1852 throw new FormatException ("Invalid Format String");
1854 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1859 ret = DateTime.MinValue;
1863 public TimeSpan Subtract(DateTime dt)
1865 return new TimeSpan(ticks.Ticks) - dt.ticks;
1868 public DateTime Subtract(TimeSpan ts)
1872 newticks = (new TimeSpan (ticks.Ticks)) - ts;
1873 DateTime ret = new DateTime(true,newticks);
1880 public long ToFileTime()
1882 DateTime universalTime = ToUniversalTime();
1884 if (universalTime.Ticks < w32file_epoch) {
1885 throw new ArgumentOutOfRangeException("file time is not valid");
1888 return(universalTime.Ticks - w32file_epoch);
1892 public long ToFileTimeUtc()
1894 if (Ticks < w32file_epoch) {
1895 throw new ArgumentOutOfRangeException("file time is not valid");
1898 return (Ticks - w32file_epoch);
1902 public string ToLongDateString()
1904 return ToString ("D");
1907 public string ToLongTimeString()
1909 return ToString ("T");
1912 public double ToOADate ()
1914 long t = this.Ticks;
1915 // uninitialized DateTime case
1918 // we can't reach minimum value
1919 if (t < 31242239136000000)
1920 return OAMinValue + 0.001;
1922 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1923 double result = ts.TotalDays;
1924 // t < 0 (where 599264352000000000 == 0.0d for OA)
1925 if (t < 599264352000000000) {
1926 // negative days (int) but decimals are positive
1927 double d = Math.Ceiling (result);
1928 result = d - 2 - (result - d);
1931 // we can't reach maximum value
1932 if (result >= OAMaxValue)
1933 result = OAMaxValue - 0.00000001d;
1938 public string ToShortDateString()
1940 return ToString ("d");
1943 public string ToShortTimeString()
1945 return ToString ("t");
1948 public override string ToString ()
1950 return ToString ("G", null);
1953 public string ToString (IFormatProvider fp)
1955 return ToString (null, fp);
1958 public string ToString (string format)
1960 return ToString (format, null);
1963 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1968 use_invariant = false;
1973 pattern = dfi.ShortDatePattern;
1976 pattern = dfi.LongDatePattern;
1979 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1982 pattern = dfi.FullDateTimePattern;
1985 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1988 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1992 pattern = dfi.MonthDayPattern;
1996 pattern = dfi.RoundtripPattern;
2001 pattern = dfi.RFC1123Pattern;
2002 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
2003 // uncommented by AE 27/may/2004
2005 use_invariant = true;
2008 pattern = dfi.SortableDateTimePattern;
2011 pattern = dfi.ShortTimePattern;
2014 pattern = dfi.LongTimePattern;
2017 pattern = dfi.UniversalSortableDateTimePattern;
2021 // pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
2022 pattern = dfi.FullDateTimePattern;
2027 pattern = dfi.YearMonthPattern;
2032 // throw new FormatException (String.Format ("Invalid format pattern: '{0}'", format));
2038 internal string _ToString (string format, DateTimeFormatInfo dfi)
2040 // the length of the format is usually a good guess of the number
2041 // of chars in the result. Might save us a few bytes sometimes
2042 // Add + 10 for cases like mmmm dddd
2043 StringBuilder result = new StringBuilder (format.Length + 10);
2045 // For some cases, the output should not use culture dependent calendar
2046 DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
2047 if (format == inv.RFC1123Pattern)
2049 else if (format == inv.UniversalSortableDateTimePattern)
2054 while (i < format.Length) {
2056 bool omitZeros = false;
2057 char ch = format [i];
2066 tokLen = CountRepeat (format, i, ch);
2068 int hr = this.Hour % 12;
2072 ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
2076 tokLen = CountRepeat (format, i, ch);
2077 ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
2081 tokLen = CountRepeat (format, i, ch);
2082 ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
2086 tokLen = CountRepeat (format, i, ch);
2087 ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
2095 // fraction of second, to same number of
2096 // digits as there are f's
2098 tokLen = CountRepeat (format, i, ch);
2100 throw new FormatException ("Invalid Format String");
2102 int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
2103 int startLen = result.Length;
2104 ZeroPad (result, dec, tokLen);
2107 while (result.Length > startLen && result [result.Length - 1] == '0')
2109 // when the value was 0, then trim even preceding '.' (!) It is fixed character.
2110 if (dec == 0 && startLen > 0 && result [startLen - 1] == '.')
2116 // AM/PM. t == first char, tt+ == full
2117 tokLen = CountRepeat (format, i, ch);
2118 string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
2121 if (desig.Length >= 1)
2122 result.Append (desig [0]);
2125 result.Append (desig);
2129 // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
2130 tokLen = CountRepeat (format, i, ch);
2131 TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2133 if (offset.Ticks >= 0)
2134 result.Append ('+');
2136 result.Append ('-');
2140 result.Append (Math.Abs (offset.Hours));
2143 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2146 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2147 result.Append (':');
2148 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2153 case 'K': // 'Z' (UTC) or zzz (Local)
2156 case DateTimeKind.Utc:
2157 result.Append ('Z');
2159 case DateTimeKind.Local:
2160 offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2161 if (offset.Ticks >= 0)
2162 result.Append ('+');
2164 result.Append ('-');
2165 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2166 result.Append (':');
2167 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2176 // day. d(d?) = day of month (leading 0 if two d's)
2177 // ddd = three leter day of week
2178 // dddd+ full day-of-week
2179 tokLen = CountRepeat (format, i, ch);
2182 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
2183 else if (tokLen == 3)
2184 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
2186 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
2190 // Month.m(m?) = month # (with leading 0 if two mm)
2191 // mmm = 3 letter name
2192 // mmmm+ = full name
2193 tokLen = CountRepeat (format, i, ch);
2194 int month = dfi.Calendar.GetMonth(this);
2196 ZeroPad (result, month, tokLen);
2197 else if (tokLen == 3)
2198 result.Append (dfi.GetAbbreviatedMonthName (month));
2200 result.Append (dfi.GetMonthName (month));
2204 // Year. y(y?) = two digit year, with leading 0 if yy
2205 // yyy+ full year with leading zeros if needed.
2206 tokLen = CountRepeat (format, i, ch);
2209 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
2211 ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
2216 tokLen = CountRepeat (format, i, ch);
2217 result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
2224 result.Append (dfi.TimeSeparator);
2228 result.Append (dfi.DateSeparator);
2231 case '\'': case '"':
2232 tokLen = ParseQuotedString (format, i, result);
2235 if (i >= format.Length - 1)
2236 throw new FormatException ("% at end of date time string");
2237 if (format [i + 1] == '%')
2238 throw new FormatException ("%% in date string");
2240 // Look for the next char
2245 if (i >= format.Length - 1)
2246 throw new FormatException ("\\ at end of date time string");
2248 result.Append (format [i + 1]);
2260 return result.ToString ();
2263 static int CountRepeat (string fmt, int p, char c)
2267 while ((i < l) && (fmt [i] == c))
2273 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
2275 // pos == position of " or '
2277 int len = fmt.Length;
2279 char quoteChar = fmt [pos++];
2282 char ch = fmt [pos++];
2284 if (ch == quoteChar)
2290 throw new FormatException("Un-ended quote");
2292 output.Append (fmt [pos++]);
2298 throw new FormatException("Un-ended quote");
2301 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
2303 // more than enough for an int
2304 char* buffer = stackalloc char [16];
2308 buffer [-- pos] = (char) ('0' + digits % 10);
2311 } while (digits > 0);
2314 buffer [-- pos] = '0';
2316 output.Append (new string (buffer, pos, 16 - pos));
2319 public string ToString (string format, IFormatProvider fp)
2321 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
2323 if (format == null || format == String.Empty)
2326 bool useutc = false, use_invariant = false;
2328 if (format.Length == 1) {
2329 char fchar = format [0];
2330 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2332 return ToUniversalTime()._ToString (format, dfi);
2335 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2338 // Don't convert UTC value. It just adds 'Z' for
2339 // 'u' format, for the same ticks.
2340 return this._ToString (format, dfi);
2343 public DateTime ToLocalTime ()
2345 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2348 public DateTime ToUniversalTime()
2350 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2355 public static DateTime operator +(DateTime d, TimeSpan t)
2357 DateTime ret = new DateTime (true, d.ticks + t);
2364 public static bool operator ==(DateTime d1, DateTime d2)
2366 return (d1.ticks == d2.ticks);
2369 public static bool operator >(DateTime t1,DateTime t2)
2371 return (t1.ticks > t2.ticks);
2374 public static bool operator >=(DateTime t1,DateTime t2)
2376 return (t1.ticks >= t2.ticks);
2379 public static bool operator !=(DateTime d1, DateTime d2)
2381 return (d1.ticks != d2.ticks);
2384 public static bool operator <(DateTime t1, DateTime t2)
2386 return (t1.ticks < t2.ticks );
2389 public static bool operator <=(DateTime t1,DateTime t2)
2391 return (t1.ticks <= t2.ticks);
2394 public static TimeSpan operator -(DateTime d1,DateTime d2)
2396 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2399 public static DateTime operator -(DateTime d,TimeSpan t)
2401 DateTime ret = new DateTime (true, d.ticks - t);
2408 bool IConvertible.ToBoolean(IFormatProvider provider)
2410 throw new InvalidCastException();
2413 byte IConvertible.ToByte(IFormatProvider provider)
2415 throw new InvalidCastException();
2419 char IConvertible.ToChar(IFormatProvider provider)
2421 throw new InvalidCastException();
2424 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2429 decimal IConvertible.ToDecimal(IFormatProvider provider)
2431 throw new InvalidCastException();
2434 double IConvertible.ToDouble(IFormatProvider provider)
2436 throw new InvalidCastException();
2439 Int16 IConvertible.ToInt16(IFormatProvider provider)
2441 throw new InvalidCastException();
2444 Int32 IConvertible.ToInt32(IFormatProvider provider)
2446 throw new InvalidCastException();
2449 Int64 IConvertible.ToInt64(IFormatProvider provider)
2451 throw new InvalidCastException();
2454 SByte IConvertible.ToSByte(IFormatProvider provider)
2456 throw new InvalidCastException();
2459 Single IConvertible.ToSingle(IFormatProvider provider)
2461 throw new InvalidCastException();
2464 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2466 if (conversionType == null)
2467 throw new ArgumentNullException ("conversionType");
2469 if (conversionType == typeof (DateTime))
2471 else if (conversionType == typeof (String))
2472 return this.ToString (provider);
2473 else if (conversionType == typeof (Object))
2476 throw new InvalidCastException();
2479 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2481 throw new InvalidCastException();
2484 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2486 throw new InvalidCastException();
2489 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2491 throw new InvalidCastException();