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 << 2) >> 2), DateTimeKind.Local);
705 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
707 return new DateTime (value.Ticks, kind);
711 public static int DaysInMonth (int year, int month)
715 if (month < 1 || month >12)
716 throw new ArgumentOutOfRangeException ();
718 if (year < 1 || year > 9999)
719 throw new ArgumentOutOfRangeException ();
721 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
725 public override bool Equals (object o)
727 if (!(o is System.DateTime))
730 return ((DateTime) o).ticks == ticks;
733 public static bool Equals (DateTime t1, DateTime t2 )
735 return (t1.ticks == t2.ticks );
738 public static DateTime FromFileTime (long fileTime)
741 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
743 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
747 public static DateTime FromFileTimeUtc (long fileTime)
750 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
752 return new DateTime (w32file_epoch + fileTime);
756 public static DateTime FromOADate (double d)
758 // An OLE Automation date is implemented as a floating-point number
759 // whose value is the number of days from midnight, 30 December 1899.
761 // d must be negative 657435.0 through positive 2958466.0.
762 if ((d <= OAMinValue) || (d >= OAMaxValue))
763 throw new ArgumentException ("d", "[-657435,2958466]");
765 DateTime dt = new DateTime (ticks18991230);
767 Double days = Math.Ceiling (d);
768 // integer part is the number of days (negative)
769 dt = dt.AddRoundedMilliseconds (days * 86400000);
770 // but decimals are the number of hours (in days fractions) and positive
771 Double hours = (days - d);
772 dt = dt.AddRoundedMilliseconds (hours * 86400000);
775 dt = dt.AddRoundedMilliseconds (d * 86400000);
781 public string[] GetDateTimeFormats()
783 return GetDateTimeFormats (CultureInfo.CurrentCulture);
786 public string[] GetDateTimeFormats(char format)
788 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
789 throw new FormatException ("Invalid format character.");
790 string[] result = new string[1];
791 result[0] = this.ToString(format.ToString());
795 public string[] GetDateTimeFormats(IFormatProvider provider)
797 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
798 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
799 ArrayList al = new ArrayList ();
800 foreach (char c in "dDgGfFmMrRstTuUyY")
801 al.AddRange (GetDateTimeFormats (c, info));
802 return al.ToArray (typeof (string)) as string [];
805 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
807 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
808 throw new FormatException ("Invalid format character.");
810 // LAMESPEC: There is NO assurance that 'U' ALWAYS
811 // euqals to 'F', but since we have to iterate all
812 // the pattern strings, we cannot just use
813 // ToString("U", provider) here. I believe that the
814 // method's behavior cannot be formalized.
815 bool adjustutc = false;
824 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
825 return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
828 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
830 string [] results = new string [patterns.Length];
831 DateTime val = adjustutc ? ToUniversalTime () : this;
832 for (int i = 0; i < results.Length; i++)
833 results [i] = val._ToString (patterns [i], dfi);
838 private void CheckDateTimeKind (DateTimeKind kind) {
839 if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
840 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
844 public override int GetHashCode ()
846 return (int) ticks.Ticks;
849 public TypeCode GetTypeCode ()
851 return TypeCode.DateTime;
854 public static bool IsLeapYear (int year)
856 if (year < 1 || year > 9999)
857 throw new ArgumentOutOfRangeException ();
858 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
861 public static DateTime Parse (string s)
863 return Parse (s, null);
866 public static DateTime Parse (string s, IFormatProvider fp)
868 return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
871 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
874 const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
875 const string argumentYearRangeExceptionMessage = "Valid values are between 1 and 9999, inclusive.";
878 throw new ArgumentNullException (Locale.GetText ("s is null"));
880 fp = CultureInfo.CurrentCulture;
881 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
883 bool longYear = false;
885 // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
886 string[] allDateFormats = YearMonthDayFormats (dfi);
887 for (int i = 0; i < allDateFormats.Length; i++) {
888 string firstPart = allDateFormats [i];
889 bool incompleteFormat = false;
890 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
892 if (!incompleteFormat)
895 for (int j = 0; j < ParseTimeFormats.Length; j++) {
896 if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
900 string[] monthDayFormats = IsDayBeforeMonth (dfi) ? DayMonthShortFormats : MonthDayShortFormats;
901 for (int i = 0; i < monthDayFormats.Length; i++) {
902 bool incompleteFormat = false;
903 if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
906 for (int j = 0; j < ParseTimeFormats.Length; j++) {
907 string firstPart = ParseTimeFormats [j];
908 bool incompleteFormat = false;
909 if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
911 if (!incompleteFormat)
914 for (int i = 0; i < monthDayFormats.Length; i++) {
915 if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
918 for (int i = 0; i < allDateFormats.Length; i++) {
919 string dateFormat = allDateFormats [i];
920 if (dateFormat[dateFormat.Length - 1] == 'T')
921 continue; // T formats must be before the time part
922 if (_DoParse (s, firstPart, dateFormat, false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
927 // Try as a last resort all the patterns
928 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
932 // .NET does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
933 throw new FormatException (formatExceptionMessage);
936 throw new ArgumentOutOfRangeException ("year",
937 argumentYearRangeExceptionMessage);
940 throw new FormatException (formatExceptionMessage);
944 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
946 return ParseExact (s, format, fp, DateTimeStyles.None);
949 private static bool IsDayBeforeMonth (DateTimeFormatInfo dfi)
951 int dayIndex = dfi.MonthDayPattern.IndexOf('d');
952 int monthIndex = dfi.MonthDayPattern.IndexOf('M');
953 if (dayIndex == -1 || monthIndex == -1)
954 throw new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
956 return dayIndex < monthIndex;
959 private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
961 int dayIndex = dfi.ShortDatePattern.IndexOf('d');
962 int monthIndex = dfi.ShortDatePattern.IndexOf('M');
963 int yearIndex = dfi.ShortDatePattern.IndexOf('y');
964 if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1)
965 throw new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
967 if (yearIndex < monthIndex)
968 if (monthIndex < dayIndex)
969 return ParseYearMonthDayFormats;
970 else if (yearIndex < dayIndex)
971 return ParseYearDayMonthFormats;
973 // The year cannot be between the date and the month
974 throw new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
975 else if (dayIndex < monthIndex)
976 return ParseDayMonthYearFormats;
977 else if (dayIndex < yearIndex)
978 return ParseMonthDayYearFormats;
980 // The year cannot be between the month and the date
981 throw new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
984 private static int _ParseNumber (string s, int valuePos,
998 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
999 if (!Char.IsDigit (s[i]))
1005 digits = real_digits;
1007 if (digits < min_digits) {
1012 if (s.Length - valuePos < digits) {
1017 for (i = valuePos; i < digits + valuePos; i++) {
1019 if (!Char.IsDigit (c)) {
1024 number = number * 10 + (byte) (c - '0');
1027 num_parsed = digits;
1031 private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
1033 // FIXME: I know this is somehow lame code. Probably
1034 // it should iterate all the enum value and return
1035 // the longest match. However right now I don't see
1036 // anything but "1" and "10" - "12" that might match
1037 // two or more values. (They are only abbrev month
1038 // names, so do reverse order search). See bug #80094.
1039 for (int i = values.Length - 1; i >= 0; i--) {
1040 if (!exact && invValues [i].Length > values[i].Length) {
1041 if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1043 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1047 if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
1049 if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
1058 private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
1061 maxlength = value.Length;
1063 if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
1064 num_parsed = maxlength;
1072 // Note that in case of Parse (exact == false) we check both for AM/PM
1073 // and the culture spcific AM/PM strings.
1074 private static bool _ParseAmPm(string s,
1077 DateTimeFormatInfo dfi,
1086 if (!IsLetter (s, valuePos)) {
1087 if (dfi.AMDesignator != "")
1094 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1095 if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
1096 dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
1098 else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
1099 _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
1100 if (exact || num_parsed != 0)
1108 // Note that in case of Parse (exact == false) we check both for ':'
1109 // and the culture spcific TimeSperator
1110 private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1112 return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
1113 !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
1116 // Accept any character for DateSeparator, except TimeSeparator,
1117 // a digit or a letter.
1118 // Not documented, but seems to be MS behaviour here. See bug 54047.
1119 private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
1122 if (exact && s [sPos] != '/')
1125 if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
1126 Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
1133 private static bool IsLetter (string s, int pos)
1135 return pos < s.Length && Char.IsLetter (s [pos]);
1138 // To implement better DateTime.Parse we use two format strings one
1139 // for Date and one for Time. This allows us to define two different
1140 // arrays of formats for Time and Dates and to combine them more or less
1141 // efficiently. When this mode is used flexibleTwoPartsParsing is true.
1142 private static bool _DoParse (string s,
1146 out DateTime result,
1147 DateTimeFormatInfo dfi,
1148 DateTimeStyles style,
1149 bool firstPartIsDate,
1150 ref bool incompleteFormat,
1154 DateTimeKind explicit_kind = DateTimeKind.Unspecified;
1156 bool useutc = false, use_localtime = true;
1157 bool use_invariant = false;
1158 bool sloppy_parsing = false;
1159 bool afterTimePart = firstPartIsDate && secondPart == "";
1160 bool flexibleTwoPartsParsing = !exact && secondPart != null;
1161 incompleteFormat = false;
1163 string format = firstPart;
1164 bool afterTFormat = false;
1165 DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
1166 if (format.Length == 1) {
1168 use_localtime = false;
1169 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
1171 else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
1174 result = new DateTime (0);
1178 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
1179 format = format.TrimStart (null);
1181 s = s.TrimStart (null); // it could be optimized, but will make little good.
1184 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
1185 format = format.TrimEnd (null);
1186 s = s.TrimEnd (null); // it could be optimized, but will make little good.
1192 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
1193 sloppy_parsing = true;
1195 string chars = format;
1196 int len = format.Length, pos = 0, num = 0;
1200 int day = -1, dayofweek = -1, month = -1, year = -1;
1201 int hour = -1, minute = -1, second = -1;
1202 double fractionalSeconds = -1;
1204 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
1205 bool isFirstPart = true;
1209 if (valuePos == s.Length)
1213 if (flexibleTwoPartsParsing && pos + num == 0)
1215 bool isLetter = IsLetter(s, valuePos);
1216 if (afterTimePart && isLetter) {
1217 if (s [valuePos] == 'Z')
1220 _ParseString (s, valuePos, 0, "GMT", out num_parsed);
1221 if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
1222 valuePos += num_parsed;
1227 if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
1228 if (IsLetter (s, valuePos + num_parsed))
1230 else if (num_parsed > 0) {
1231 valuePos += num_parsed;
1236 if (!afterTFormat && dayofweek == -1 && isLetter) {
1237 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1238 if (dayofweek == -1)
1239 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1240 if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
1241 valuePos += num_parsed;
1248 if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
1255 if (pos + num >= len)
1257 if (flexibleTwoPartsParsing && num == 0) {
1258 afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
1259 if (!isFirstPart && format == "")
1264 format = secondPart;
1269 isFirstPart = false;
1270 if (!firstPartIsDate || format == "")
1271 afterTimePart = true;
1277 bool leading_zeros = true;
1279 if (chars[pos] == '\'') {
1281 while (pos+num < len) {
1282 if (chars[pos+num] == '\'')
1285 if (valuePos == s.Length || s [valuePos] != chars [pos + num])
1295 } else if (chars[pos] == '"') {
1297 while (pos+num < len) {
1298 if (chars[pos+num] == '"')
1301 if (valuePos == s.Length || s [valuePos] != chars[pos+num])
1311 } else if (chars[pos] == '\\') {
1316 if (s [valuePos] != chars [pos])
1322 } else if (chars[pos] == '%') {
1325 } else if (char.IsWhiteSpace (s [valuePos]) ||
1326 s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
1329 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1330 if (!Char.IsWhiteSpace (chars[pos]))
1337 while (ws < s.Length) {
1338 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1345 while (ws < chars.Length) {
1346 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1352 // A whitespace may match a '/' in the pattern.
1353 if (!exact && pos < chars.Length && chars[pos] == '/')
1354 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1359 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1367 if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
1370 day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1372 day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1374 dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
1376 dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
1382 if (flexibleTwoPartsParsing) {
1384 if (num == 0 || num == 3)
1385 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1386 if (num > 1 && num_parsed == -1)
1387 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1388 if (num > 1 && num_parsed == -1)
1389 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1394 month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1396 month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1398 month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
1400 month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
1407 year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1408 } else if (num < 3) {
1409 year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1411 year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
1412 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1414 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1415 longYear = (ly > 9999);
1420 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1421 if (num_parsed <= 2)
1422 year += (year < 30) ? 2000 : 1900;
1428 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1430 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1439 if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
1442 hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1444 hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1455 minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1457 minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1467 second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1469 second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1477 leading_zeros = false;
1481 if (num > 6 || fractionalSeconds != -1)
1483 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
1484 if (num_parsed == -1)
1486 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1489 if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
1496 if (s [valuePos] == '+')
1498 else if (s [valuePos] == '-')
1505 tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
1507 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1509 tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1510 valuePos += num_parsed;
1515 if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
1516 _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
1517 valuePos += num_parsed;
1518 tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
1522 else if (!flexibleTwoPartsParsing)
1530 if (s [valuePos] == 'Z') {
1532 explicit_kind = DateTimeKind.Utc;
1534 else if (s [valuePos] == '+' || s [valuePos] == '-') {
1537 if (s [valuePos] == '+')
1539 else if (s [valuePos] == '-')
1544 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1545 valuePos += num_parsed;
1549 if (Char.IsDigit (s [valuePos]))
1551 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1553 valuePos += num_parsed;
1555 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1557 explicit_kind = DateTimeKind.Local;
1563 // LAMESPEC: This should be part of UTCpattern
1564 // string and thus should not be considered here.
1566 // Note that 'Z' is not defined as a pattern
1567 // character. Keep it for X509 certificate
1568 // verification. Also, "Z" != "'Z'" under MS.NET
1569 // ("'Z'" is just literal; handled above)
1571 if (s [valuePos] != 'Z')
1579 if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
1583 if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
1589 if (s [valuePos] != chars [pos])
1600 valuePos += num_parsed;
1602 if (!exact && !flexibleTwoPartsParsing) {
1603 switch (chars [pos]) {
1611 if (s.Length > valuePos && s [valuePos] == 'Z' &&
1612 (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
1620 pos = pos + num + 1;
1627 if (s.Length > valuePos) // extraneous tail.
1632 if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
1634 if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
1636 incompleteFormat = true;
1647 if (fractionalSeconds == -1)
1648 fractionalSeconds = 0;
1650 // If no date was given
1651 if ((day == -1) && (month == -1) && (year == -1)) {
1652 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1657 day = DateTime.Today.Day;
1658 month = DateTime.Today.Month;
1659 year = DateTime.Today.Year;
1668 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1671 year = DateTime.Today.Year;
1674 if (ampm == 0 && hour == 12)
1677 if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
1680 // For anything out of range
1682 if (year < 1 || year > 9999 ||
1683 month < 1 || month >12 ||
1684 day < 1 || day > DateTime.DaysInMonth(year, month) ||
1685 hour < 0 || hour > 23 ||
1686 minute < 0 || minute > 59 ||
1687 second < 0 || second > 59)
1690 result = new DateTime (year, month, day, hour, minute, second, 0);
1691 result = result.AddSeconds(fractionalSeconds);
1693 if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
1696 // If no timezone was specified, default to the local timezone.
1700 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1701 use_localtime = false;
1702 utcoffset = new TimeSpan (0, 0, 0);
1703 } else if (tzsign == -1) {
1704 TimeZone tz = TimeZone.CurrentTimeZone;
1705 utcoffset = tz.GetUtcOffset (result);
1707 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1708 use_localtime = false;
1715 tzoffset = -tzoffset;
1717 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1720 long newticks = (result.ticks - utcoffset).Ticks;
1722 result = new DateTime (false, new TimeSpan (newticks));
1724 if (explicit_kind != DateTimeKind.Unspecified)
1725 result.kind = explicit_kind;
1726 else if (use_localtime)
1727 result = result.ToLocalTime ();
1729 result.kind = DateTimeKind.Utc;
1732 result = result.ToLocalTime ();
1738 public static DateTime ParseExact (string s, string format,
1739 IFormatProvider fp, DateTimeStyles style)
1742 throw new ArgumentNullException ("format");
1744 string [] formats = new string [1];
1745 formats[0] = format;
1747 return ParseExact (s, formats, fp, style);
1750 public static DateTime ParseExact (string s, string[] formats,
1752 DateTimeStyles style)
1754 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1757 throw new ArgumentNullException ("s");
1758 if (formats == null)
1759 throw new ArgumentNullException ("formats");
1760 if (formats.Length == 0)
1761 throw new FormatException ("Format specifier was invalid.");
1764 bool longYear = false;
1765 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1766 throw new FormatException ();
1771 public static bool TryParse (string s, out DateTime result)
1782 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1785 result = Parse (s, provider, styles);
1793 public static bool TryParseExact (string s, string format,
1795 DateTimeStyles style,
1796 out DateTime result)
1800 formats = new string [1];
1801 formats[0] = format;
1803 return TryParseExact (s, formats, fp, style, out result);
1806 public static bool TryParseExact (string s, string[] formats,
1808 DateTimeStyles style,
1809 out DateTime result)
1811 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1813 bool longYear = false;
1814 return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1818 private static bool ParseExact (string s, string [] formats,
1819 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1820 bool exact, ref bool longYear)
1823 bool incompleteFormat = false;
1824 for (i = 0; i < formats.Length; i++)
1827 string format = formats[i];
1828 if (format == null || format == String.Empty)
1829 throw new FormatException ("Invalid Format String");
1831 if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
1836 ret = DateTime.MinValue;
1840 public TimeSpan Subtract(DateTime dt)
1842 return new TimeSpan(ticks.Ticks) - dt.ticks;
1845 public DateTime Subtract(TimeSpan ts)
1849 newticks = (new TimeSpan (ticks.Ticks)) - ts;
1850 DateTime ret = new DateTime(true,newticks);
1857 public long ToFileTime()
1859 DateTime universalTime = ToUniversalTime();
1861 if (universalTime.Ticks < w32file_epoch) {
1862 throw new ArgumentOutOfRangeException("file time is not valid");
1865 return(universalTime.Ticks - w32file_epoch);
1869 public long ToFileTimeUtc()
1871 if (Ticks < w32file_epoch) {
1872 throw new ArgumentOutOfRangeException("file time is not valid");
1875 return (Ticks - w32file_epoch);
1879 public string ToLongDateString()
1881 return ToString ("D");
1884 public string ToLongTimeString()
1886 return ToString ("T");
1889 public double ToOADate ()
1891 long t = this.Ticks;
1892 // uninitialized DateTime case
1895 // we can't reach minimum value
1896 if (t < 31242239136000000)
1897 return OAMinValue + 0.001;
1899 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1900 double result = ts.TotalDays;
1901 // t < 0 (where 599264352000000000 == 0.0d for OA)
1902 if (t < 599264352000000000) {
1903 // negative days (int) but decimals are positive
1904 double d = Math.Ceiling (result);
1905 result = d - 2 - (result - d);
1908 // we can't reach maximum value
1909 if (result >= OAMaxValue)
1910 result = OAMaxValue - 0.00000001d;
1915 public string ToShortDateString()
1917 return ToString ("d");
1920 public string ToShortTimeString()
1922 return ToString ("t");
1925 public override string ToString ()
1927 return ToString ("G", null);
1930 public string ToString (IFormatProvider fp)
1932 return ToString (null, fp);
1935 public string ToString (string format)
1937 return ToString (format, null);
1940 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1945 use_invariant = false;
1950 pattern = dfi.ShortDatePattern;
1953 pattern = dfi.LongDatePattern;
1956 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1959 pattern = dfi.FullDateTimePattern;
1962 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1965 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1969 pattern = dfi.MonthDayPattern;
1973 pattern = dfi.RoundtripPattern;
1978 pattern = dfi.RFC1123Pattern;
1979 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1980 // uncommented by AE 27/may/2004
1982 use_invariant = true;
1985 pattern = dfi.SortableDateTimePattern;
1988 pattern = dfi.ShortTimePattern;
1991 pattern = dfi.LongTimePattern;
1994 pattern = dfi.UniversalSortableDateTimePattern;
1998 // pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
1999 pattern = dfi.FullDateTimePattern;
2004 pattern = dfi.YearMonthPattern;
2009 // throw new FormatException (String.Format ("Invalid format pattern: '{0}'", format));
2015 internal string _ToString (string format, DateTimeFormatInfo dfi)
2017 // the length of the format is usually a good guess of the number
2018 // of chars in the result. Might save us a few bytes sometimes
2019 // Add + 10 for cases like mmmm dddd
2020 StringBuilder result = new StringBuilder (format.Length + 10);
2022 // For some cases, the output should not use culture dependent calendar
2023 DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
2024 if (format == inv.RFC1123Pattern)
2026 else if (format == inv.UniversalSortableDateTimePattern)
2031 while (i < format.Length) {
2033 bool omitZeros = false;
2034 char ch = format [i];
2043 tokLen = CountRepeat (format, i, ch);
2045 int hr = this.Hour % 12;
2049 ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
2053 tokLen = CountRepeat (format, i, ch);
2054 ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
2058 tokLen = CountRepeat (format, i, ch);
2059 ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
2063 tokLen = CountRepeat (format, i, ch);
2064 ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
2072 // fraction of second, to same number of
2073 // digits as there are f's
2075 tokLen = CountRepeat (format, i, ch);
2077 throw new FormatException ("Invalid Format String");
2079 int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
2080 int startLen = result.Length;
2081 ZeroPad (result, dec, tokLen);
2084 while (result.Length > startLen && result [result.Length - 1] == '0')
2086 // when the value was 0, then trim even preceding '.' (!) It is fixed character.
2087 if (dec == 0 && startLen > 0 && result [startLen - 1] == '.')
2093 // AM/PM. t == first char, tt+ == full
2094 tokLen = CountRepeat (format, i, ch);
2095 string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
2098 if (desig.Length >= 1)
2099 result.Append (desig [0]);
2102 result.Append (desig);
2106 // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
2107 tokLen = CountRepeat (format, i, ch);
2108 TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2110 if (offset.Ticks >= 0)
2111 result.Append ('+');
2113 result.Append ('-');
2117 result.Append (Math.Abs (offset.Hours));
2120 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2123 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2124 result.Append (':');
2125 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2130 case 'K': // 'Z' (UTC) or zzz (Local)
2133 case DateTimeKind.Utc:
2134 result.Append ('Z');
2136 case DateTimeKind.Local:
2137 offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
2138 if (offset.Ticks >= 0)
2139 result.Append ('+');
2141 result.Append ('-');
2142 result.Append (Math.Abs (offset.Hours).ToString ("00"));
2143 result.Append (':');
2144 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
2153 // day. d(d?) = day of month (leading 0 if two d's)
2154 // ddd = three leter day of week
2155 // dddd+ full day-of-week
2156 tokLen = CountRepeat (format, i, ch);
2159 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
2160 else if (tokLen == 3)
2161 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
2163 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
2167 // Month.m(m?) = month # (with leading 0 if two mm)
2168 // mmm = 3 letter name
2169 // mmmm+ = full name
2170 tokLen = CountRepeat (format, i, ch);
2171 int month = dfi.Calendar.GetMonth(this);
2173 ZeroPad (result, month, tokLen);
2174 else if (tokLen == 3)
2175 result.Append (dfi.GetAbbreviatedMonthName (month));
2177 result.Append (dfi.GetMonthName (month));
2181 // Year. y(y?) = two digit year, with leading 0 if yy
2182 // yyy+ full year with leading zeros if needed.
2183 tokLen = CountRepeat (format, i, ch);
2186 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
2188 ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
2193 tokLen = CountRepeat (format, i, ch);
2194 result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
2201 result.Append (dfi.TimeSeparator);
2205 result.Append (dfi.DateSeparator);
2208 case '\'': case '"':
2209 tokLen = ParseQuotedString (format, i, result);
2212 if (i >= format.Length - 1)
2213 throw new FormatException ("% at end of date time string");
2214 if (format [i + 1] == '%')
2215 throw new FormatException ("%% in date string");
2217 // Look for the next char
2222 if (i >= format.Length - 1)
2223 throw new FormatException ("\\ at end of date time string");
2225 result.Append (format [i + 1]);
2237 return result.ToString ();
2240 static int CountRepeat (string fmt, int p, char c)
2244 while ((i < l) && (fmt [i] == c))
2250 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
2252 // pos == position of " or '
2254 int len = fmt.Length;
2256 char quoteChar = fmt [pos++];
2259 char ch = fmt [pos++];
2261 if (ch == quoteChar)
2267 throw new FormatException("Un-ended quote");
2269 output.Append (fmt [pos++]);
2275 throw new FormatException("Un-ended quote");
2278 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
2280 // more than enough for an int
2281 char* buffer = stackalloc char [16];
2285 buffer [-- pos] = (char) ('0' + digits % 10);
2288 } while (digits > 0);
2291 buffer [-- pos] = '0';
2293 output.Append (new string (buffer, pos, 16 - pos));
2296 public string ToString (string format, IFormatProvider fp)
2298 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
2300 if (format == null || format == String.Empty)
2303 bool useutc = false, use_invariant = false;
2305 if (format.Length == 1) {
2306 char fchar = format [0];
2307 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
2309 return ToUniversalTime()._ToString (format, dfi);
2312 throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
2315 // Don't convert UTC value. It just adds 'Z' for
2316 // 'u' format, for the same ticks.
2317 return this._ToString (format, dfi);
2320 public DateTime ToLocalTime ()
2322 return TimeZone.CurrentTimeZone.ToLocalTime (this);
2325 public DateTime ToUniversalTime()
2327 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
2332 public static DateTime operator +(DateTime d, TimeSpan t)
2334 DateTime ret = new DateTime (true, d.ticks + t);
2341 public static bool operator ==(DateTime d1, DateTime d2)
2343 return (d1.ticks == d2.ticks);
2346 public static bool operator >(DateTime t1,DateTime t2)
2348 return (t1.ticks > t2.ticks);
2351 public static bool operator >=(DateTime t1,DateTime t2)
2353 return (t1.ticks >= t2.ticks);
2356 public static bool operator !=(DateTime d1, DateTime d2)
2358 return (d1.ticks != d2.ticks);
2361 public static bool operator <(DateTime t1, DateTime t2)
2363 return (t1.ticks < t2.ticks );
2366 public static bool operator <=(DateTime t1,DateTime t2)
2368 return (t1.ticks <= t2.ticks);
2371 public static TimeSpan operator -(DateTime d1,DateTime d2)
2373 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
2376 public static DateTime operator -(DateTime d,TimeSpan t)
2378 DateTime ret = new DateTime (true, d.ticks - t);
2385 bool IConvertible.ToBoolean(IFormatProvider provider)
2387 throw new InvalidCastException();
2390 byte IConvertible.ToByte(IFormatProvider provider)
2392 throw new InvalidCastException();
2396 char IConvertible.ToChar(IFormatProvider provider)
2398 throw new InvalidCastException();
2401 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2406 decimal IConvertible.ToDecimal(IFormatProvider provider)
2408 throw new InvalidCastException();
2411 double IConvertible.ToDouble(IFormatProvider provider)
2413 throw new InvalidCastException();
2416 Int16 IConvertible.ToInt16(IFormatProvider provider)
2418 throw new InvalidCastException();
2421 Int32 IConvertible.ToInt32(IFormatProvider provider)
2423 throw new InvalidCastException();
2426 Int64 IConvertible.ToInt64(IFormatProvider provider)
2428 throw new InvalidCastException();
2431 SByte IConvertible.ToSByte(IFormatProvider provider)
2433 throw new InvalidCastException();
2436 Single IConvertible.ToSingle(IFormatProvider provider)
2438 throw new InvalidCastException();
2441 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2443 if (conversionType == null)
2444 throw new ArgumentNullException ("conversionType");
2446 if (conversionType == typeof (DateTime))
2448 else if (conversionType == typeof (String))
2449 return this.ToString (provider);
2450 else if (conversionType == typeof (Object))
2453 throw new InvalidCastException();
2456 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2458 throw new InvalidCastException();
2461 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2463 throw new InvalidCastException();
2466 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2468 throw new InvalidCastException();