(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / System / DateTime.cs
blob2acbd6dd683b2b5cb5f00078b232898cefd9f75f
1 //
2 // System.DateTime.cs
3 //
4 // author:
5 // Marcel Narings (marcel@narings.nl)
6 // Martin Baulig (martin@gnome.org)
7 // Atsushi Enomoto (atsushi@ximian.com)
8 //
9 // (C) 2001 Marcel Narings
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System;
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
38 using System.Runtime.InteropServices;
39 using System.Text;
41 namespace System
43 /// <summary>
44 /// The DateTime structure represents dates and time ranging from
45 /// 1-1-0001 12:00:00 AM to 31-12-9999 23:59:00 Common Era.
46 /// </summary>
47 ///
48 [Serializable]
49 [StructLayout (LayoutKind.Auto)]
50 public struct DateTime : IFormattable, IConvertible,
51 #if NET_2_0
52 IComparable, IComparable<DateTime>
53 #else
54 IComparable
55 #endif
57 private TimeSpan ticks;
59 private const int dp400 = 146097;
60 private const int dp100 = 36524;
61 private const int dp4 = 1461;
63 // w32 file time starts counting from 1/1/1601 00:00 GMT
64 // which is the constant ticks from the .NET epoch
65 private const long w32file_epoch = 504911232000000000L;
67 //private const long MAX_VALUE_TICKS = 3155378975400000000L;
68 // -- Microsoft .NET has this value.
69 private const long MAX_VALUE_TICKS = 3155378975999999999L;
72 // The UnixEpoch, it begins on Jan 1, 1970 at 0:0:0, expressed
73 // in Ticks
75 internal const long UnixEpoch = 621355968000000000L;
77 // for OLE Automation dates
78 private const long ticks18991230 = 599264352000000000L;
79 private const double OAMinValue = -657435.0d;
80 private const double OAMaxValue = 2958466.0d;
82 public static readonly DateTime MaxValue = new DateTime (false, MAX_VALUE_TICKS);
83 public static readonly DateTime MinValue = new DateTime (false, 0);
85 private static readonly string[] commonFormats = {
86 // For compatibility with MS's CLR, this format (which
87 // doesn't have a one-letter equivalent) is parsed
88 // too. It's important because it's used in XML
89 // serialization.
91 // Note that those format should be tried only for
92 // invalid patterns;
94 // FIXME: SOME OF those patterns looks tried against
95 // the current culture, since some patterns fail in
96 // some culture.
98 "yyyy-MM-dd",
99 "yyyy-MM-ddTHH:mm:sszzz",
100 "yyyy-MM-ddTHH:mm:ss.fffffff",
101 "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
102 // UTC / allow any separator
103 "yyyy/MM/ddTHH:mm:ssZ",
104 "yyyy/M/dZ",
105 // bug #58938
106 "yyyy/M/d HH:mm:ss",
107 // bug #47720
108 "yyyy/MM/dd HH:mm:ss 'GMT'",
109 // Close to RFC1123, but without 'GMT'
110 "ddd, d MMM yyyy HH:mm:ss",
111 // use UTC ('Z'; not literal "'Z'")
112 // FIXME: 1078(af-ZA) and 1079(ka-GE) reject it
113 "yyyy/MM/dd HH':'mm':'ssZ",
115 // DayOfTheWeek, dd full_month_name yyyy
116 // FIXME: 1054(th-TH) rejects it
117 "dddd, dd MMMM yyyy",
118 // DayOfTheWeek, dd yyyy. This works for every locales.
119 "MMMM dd, yyyy",
120 #if NET_1_1
121 // X509Certificate pattern is accepted by Parse() *in every culture*
122 "yyyyMMddHHmmssZ",
123 #endif
124 // In Parse() the 'r' equivalent pattern is first parsed as universal time
125 "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'",
127 // Full date and time
128 "F", "G", "r", "s", "u", "U",
129 // Full date and time, but no seconds
130 "f", "g",
131 // Only date
132 "d", "D",
133 // Only time
134 "T", "t",
135 // Only date, but no year
136 "m",
137 // Only date, but no day
138 "y"
142 private enum Which
144 Day,
145 DayYear,
146 Month,
147 Year
150 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
151 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
153 private static int AbsoluteDays (int year, int month, int day)
155 int[] days;
156 int temp = 0, m=1 ;
162 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
164 while (m < month)
165 temp += days[m++];
166 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
169 private int FromTicks(Which what)
171 int num400, num100, num4, numyears;
172 int M =1;
174 int[] days = daysmonth;
175 int totaldays = this.ticks.Days;
177 num400 = (totaldays / dp400);
178 totaldays -= num400 * dp400;
180 num100 = (totaldays / dp100);
181 if (num100 == 4) // leap
182 num100 = 3;
183 totaldays -= (num100 * dp100);
185 num4 = totaldays / dp4;
186 totaldays -= (num4 * dp4);
188 numyears = totaldays / 365 ;
190 if (numyears == 4) //leap
191 numyears =3 ;
192 if (what == Which.Year )
193 return num400*400 + num100*100 + num4*4 + numyears + 1;
195 totaldays -= (numyears * 365) ;
196 if (what == Which.DayYear )
197 return totaldays + 1;
199 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
200 days = daysmonthleap;
202 while (totaldays >= days[M])
203 totaldays -= days[M++];
205 if (what == Which.Month )
206 return M;
208 return totaldays +1;
212 // Constructors
214 /// <summary>
215 /// Constructs a DateTime for specified ticks
216 /// </summary>
217 ///
218 public DateTime (long newticks)
219 // `local' must default to false here to avoid
220 // a recursion loop.
221 : this (false, newticks) {}
223 internal DateTime (bool local, long newticks)
225 ticks = new TimeSpan (newticks);
226 if (local) {
227 TimeZone tz = TimeZone.CurrentTimeZone;
229 TimeSpan utcoffset = tz.GetUtcOffset (this);
231 ticks = ticks + utcoffset;
233 if (ticks.Ticks < MinValue.Ticks || ticks.Ticks > MaxValue.Ticks)
234 throw new ArgumentOutOfRangeException ();
237 public DateTime (int year, int month, int day)
238 : this (year, month, day,0,0,0,0) {}
240 public DateTime (int year, int month, int day, int hour, int minute, int second)
241 : this (year, month, day, hour, minute, second, 0) {}
243 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
245 if ( year < 1 || year > 9999 ||
246 month < 1 || month >12 ||
247 day < 1 || day > DaysInMonth(year, month) ||
248 hour < 0 || hour > 23 ||
249 minute < 0 || minute > 59 ||
250 second < 0 || second > 59 ||
251 millisecond < 0 || millisecond > 999)
252 throw new ArgumentOutOfRangeException ("Parameters describe an " +
253 "unrepresentable DateTime.");
255 ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
258 public DateTime (int year, int month, int day, Calendar calendar)
259 : this (year, month, day, 0, 0, 0, 0, calendar) {}
262 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
263 : this (year, month, day, hour, minute, second, 0, calendar) {}
266 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
267 : this (year, month, day, hour, minute, second, millisecond)
269 if (calendar == null)
270 throw new ArgumentNullException();
273 internal DateTime (bool check, TimeSpan value)
275 if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
276 throw new ArgumentOutOfRangeException ();
278 ticks = value;
281 /* Properties */
283 public DateTime Date
285 get
287 return new DateTime (Year, Month, Day);
291 public int Month
293 get
295 return FromTicks(Which.Month);
300 public int Day
302 get
304 return FromTicks(Which.Day);
308 public DayOfWeek DayOfWeek
310 get
312 return ( (DayOfWeek) ((ticks.Days+1) % 7) );
316 public int DayOfYear
318 get
320 return FromTicks(Which.DayYear);
324 public TimeSpan TimeOfDay
326 get
328 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
333 public int Hour
335 get
337 return ticks.Hours;
341 public int Minute
343 get
345 return ticks.Minutes;
349 public int Second
351 get
353 return ticks.Seconds;
357 public int Millisecond
359 get
361 return ticks.Milliseconds;
365 [MethodImplAttribute(MethodImplOptions.InternalCall)]
366 internal static extern long GetNow ();
368 public static DateTime Now
370 get
372 return new DateTime (true, GetNow ());
376 public long Ticks
378 get
380 return ticks.Ticks;
384 public static DateTime Today
386 get {
387 DateTime now = Now;
388 return new DateTime (now.Year, now.Month, now.Day);
392 public static DateTime UtcNow
394 get {
395 return new DateTime (GetNow ());
399 public int Year
401 get
403 return FromTicks(Which.Year);
407 /* methods */
409 public DateTime Add (TimeSpan ts)
411 return AddTicks (ts.Ticks);
414 public DateTime AddDays (double days)
416 return AddMilliseconds (Math.Round (days * 86400000));
419 public DateTime AddTicks (long t)
421 if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
422 throw new ArgumentOutOfRangeException();
424 return new DateTime (t + ticks.Ticks);
427 public DateTime AddHours (double hours)
429 return AddMilliseconds (hours * 3600000);
432 public DateTime AddMilliseconds (double ms)
434 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
435 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
436 throw new ArgumentOutOfRangeException();
438 long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
440 return AddTicks (msticks);
443 // required to match MS implementation for OADate (OLE Automation)
444 private DateTime AddRoundedMilliseconds (double ms)
446 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
447 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
448 throw new ArgumentOutOfRangeException ();
450 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
452 return AddTicks (msticks);
455 public DateTime AddMinutes (double minutes)
457 return AddMilliseconds (minutes * 60000);
460 public DateTime AddMonths (int months)
462 int day, month, year, maxday ;
463 DateTime temp ;
465 day = this.Day;
466 month = this.Month + (months % 12);
467 year = this.Year + months/12 ;
469 if (month < 1)
471 month = 12 + month ;
472 year -- ;
474 else if (month>12)
476 month = month -12;
477 year ++;
479 maxday = DaysInMonth(year, month);
480 if (day > maxday)
481 day = maxday;
483 temp = new DateTime (year, month, day);
484 return temp.Add (this.TimeOfDay);
487 public DateTime AddSeconds (double seconds)
489 return AddMilliseconds (seconds*1000);
492 public DateTime AddYears (int years )
494 return AddMonths(years * 12);
497 public static int Compare (DateTime t1, DateTime t2)
499 if (t1.ticks < t2.ticks)
500 return -1;
501 else if (t1.ticks > t2.ticks)
502 return 1;
503 else
504 return 0;
507 public int CompareTo (object v)
509 if ( v == null)
510 return 1;
512 if (!(v is System.DateTime))
513 throw new ArgumentException (Locale.GetText (
514 "Value is not a System.DateTime"));
516 return Compare (this, (DateTime) v);
519 #if NET_2_0
520 public int CompareTo (DateTime value)
522 return Compare (this, value);
525 public bool Equals (DateTime value)
527 return value.ticks == ticks;
529 #endif
531 public static int DaysInMonth (int year, int month)
533 int[] days ;
535 if (month < 1 || month >12)
536 throw new ArgumentOutOfRangeException ();
538 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
539 return days[month];
542 public override bool Equals (object o)
544 if (!(o is System.DateTime))
545 return false;
547 return ((DateTime) o).ticks == ticks;
550 public static bool Equals (DateTime t1, DateTime t2 )
552 return (t1.ticks == t2.ticks );
555 public static DateTime FromFileTime (long fileTime)
557 if (fileTime < 0)
558 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
560 return new DateTime (true, w32file_epoch + fileTime);
563 #if NET_1_1
564 public static DateTime FromFileTimeUtc (long fileTime)
566 if (fileTime < 0)
567 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
569 return new DateTime (false, w32file_epoch + fileTime);
571 #endif
573 public static DateTime FromOADate (double d)
575 // An OLE Automation date is implemented as a floating-point number
576 // whose value is the number of days from midnight, 30 December 1899.
578 // d must be negative 657435.0 through positive 2958466.0.
579 if ((d <= OAMinValue) || (d >= OAMaxValue))
580 throw new ArgumentException ("d", "[-657435,2958466]");
582 DateTime dt = new DateTime (ticks18991230);
583 if (d < 0.0d) {
584 Double days = Math.Ceiling (d);
585 // integer part is the number of days (negative)
586 dt = dt.AddRoundedMilliseconds (days * 86400000);
587 // but decimals are the number of hours (in days fractions) and positive
588 Double hours = (days - d);
589 dt = dt.AddRoundedMilliseconds (hours * 86400000);
591 else {
592 dt = dt.AddRoundedMilliseconds (d * 86400000);
595 return dt;
598 public string[] GetDateTimeFormats()
600 return GetDateTimeFormats (CultureInfo.CurrentCulture);
603 public string[] GetDateTimeFormats(char format)
605 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
606 throw new FormatException ("Invalid format character.");
607 string[] result = new string[1];
608 result[0] = this.ToString(format.ToString());
609 return result;
612 public string[] GetDateTimeFormats(IFormatProvider provider)
614 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
615 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
616 ArrayList al = new ArrayList ();
617 foreach (char c in "dDgGfFmMrRstTuUyY")
618 al.AddRange (GetDateTimeFormats (c, info));
619 return al.ToArray (typeof (string)) as string [];
622 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
624 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
625 throw new FormatException ("Invalid format character.");
627 // LAMESPEC: There is NO assurance that 'U' ALWAYS
628 // euqals to 'F', but since we have to iterate all
629 // the pattern strings, we cannot just use
630 // ToString("U", provider) here. I believe that the
631 // method's behavior cannot be formalized.
632 bool adjustutc = false;
633 switch (format) {
634 case 'U':
635 // case 'r':
636 // case 'R':
637 // case 'u':
638 adjustutc = true;
639 break;
641 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
642 return GetDateTimeFormats (adjustutc, info.GetAllDateTimePatterns (format), info);
645 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
647 string [] results = new string [patterns.Length];
648 DateTime val = adjustutc ? ToUniversalTime () : this;
649 for (int i = 0; i < results.Length; i++)
650 results [i] = val._ToString (patterns [i], dfi);
651 return results;
654 public override int GetHashCode ()
656 return (int) ticks.Ticks;
659 public TypeCode GetTypeCode ()
661 return TypeCode.DateTime;
664 public static bool IsLeapYear (int year)
666 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
669 public static DateTime Parse (string s)
671 return Parse (s, null);
674 public static DateTime Parse (string s, IFormatProvider fp)
676 return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
679 [MonoTODO ("see the comments inline")]
680 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
682 // This method should try only expected patterns.
683 // Should not try extra patterns.
684 // Right now we also try InvariantCulture, but I
685 // confirmed in some cases this method rejects what
686 // InvariantCulture supports (can be checked against
687 // "th-TH" with Gregorian Calendar). So basically it
688 // should not be done.
689 // I think it should be CurrentCulture to be tested,
690 // but right now we don't support all the supported
691 // patterns for each culture, so try InvariantCulture
692 // as a quick remedy.
693 if (s == null)
694 throw new ArgumentNullException (Locale.GetText ("s is null"));
695 DateTime result;
697 if (fp == null)
698 fp = CultureInfo.CurrentCulture;
699 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
701 bool longYear = false;
702 // Try common formats.
703 if (ParseExact (s, commonFormats, dfi, styles, out result, false, ref longYear))
704 return result;
706 // Try common formats, also with invariant culture
707 if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
708 return result;
710 // Next, try all the patterns
711 string [] patterns = new string [] {"d", "D", "g", "G", "f", "F", "m", "M", "r", "R", "s", "t", "T", "u", "U", "y", "Y"};
713 if (ParseExact (s, patterns, dfi, styles, out result, false, ref longYear))
714 return result;
716 if (longYear) {
717 throw new ArgumentOutOfRangeException ("year",
718 "Valid values are between 1 and 9999 inclusive");
721 throw new FormatException ("String was not recognized as a valid DateTime.");
724 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
726 return ParseExact (s, format, fp, DateTimeStyles.None);
729 internal static int _ParseNumber (string s, int valuePos,
730 int digits,
731 bool leadingzero,
732 bool sloppy_parsing,
733 bool next_not_digit,
734 out int num_parsed)
736 int number = 0, i;
738 if (sloppy_parsing)
739 leadingzero = false;
741 if (!leadingzero) {
742 int real_digits = 0;
743 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
744 if (!Char.IsDigit (s[i]))
745 break;
747 real_digits++;
750 digits = real_digits;
753 if (s.Length - valuePos < digits) {
754 num_parsed = -1;
755 return 0;
758 if (s.Length - valuePos > digits &&
759 next_not_digit &&
760 Char.IsDigit (s[digits + valuePos])) {
761 /* More digits left over */
762 num_parsed = -1;
763 return(0);
766 for (i = valuePos; i < digits + valuePos; i++) {
767 char c = s[i];
768 if (!Char.IsDigit (c)) {
769 num_parsed = -1;
770 return 0;
773 number = number * 10 + (byte) (c - '0');
776 num_parsed = digits;
777 return number;
780 internal static int _ParseEnum (string s, int sPos, string[] values, out int num_parsed)
782 int i;
784 for (i = 0; i < values.Length; i++) {
785 if (s.Length - sPos < values[i].Length)
786 continue;
787 else if (values [i].Length == 0)
788 continue;
789 String tmp = s.Substring (sPos, values[i].Length);
790 if (String.Compare (tmp, values[i], true) == 0) {
791 num_parsed = values[i].Length;
792 return i;
796 num_parsed = -1;
797 return -1;
800 internal static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
802 if (maxlength <= 0)
803 maxlength = value.Length;
805 if (String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
806 num_parsed = maxlength;
807 return true;
810 num_parsed = -1;
811 return false;
814 private static bool _DoParse (string s, string format, bool exact,
815 out DateTime result,
816 DateTimeFormatInfo dfi,
817 DateTimeStyles style,
818 ref bool longYear)
820 bool useutc = false, use_localtime = true;
821 bool use_invariant = false;
822 bool sloppy_parsing = false;
823 int valuePos = 0;
824 if (format.Length == 1)
825 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
826 else if (!exact && format.IndexOf ("GMT") >= 0)
827 useutc = true;
829 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
830 format = format.TrimStart (null);
832 s = s.TrimStart (null); // it could be optimized, but will make little good.
835 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
836 format = format.TrimEnd (null);
837 s = s.TrimEnd (null); // it could be optimized, but will make little good.
840 if (use_invariant)
841 dfi = DateTimeFormatInfo.InvariantInfo;
843 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
844 sloppy_parsing = true;
846 string chars = format;
847 int len = format.Length, pos = 0, num = 0;
849 int day = -1, dayofweek = -1, month = -1, year = -1;
850 int hour = -1, minute = -1, second = -1;
851 double fractionalSeconds = -1;
852 int ampm = -1;
853 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
854 bool next_not_digit;
856 result = new DateTime (0);
857 while (pos+num < len)
859 if (s.Length == valuePos)
860 break;
862 if (chars[pos] == '\'') {
863 num = 1;
864 while (pos+num < len) {
865 if (chars[pos+num] == '\'')
866 break;
868 if (valuePos == s.Length)
869 return false;
870 if (s [valuePos] != chars [pos + num])
871 return false;
872 valuePos++;
874 num++;
876 if (pos+num > len)
877 return false;
879 pos += num + 1;
880 num = 0;
881 continue;
882 } else if (chars[pos] == '"') {
883 num = 1;
884 while (pos+num < len) {
885 if (chars[pos+num] == '"')
886 break;
888 if (valuePos == s.Length)
889 return false;
890 if (s [valuePos] != chars[pos+num])
891 return false;
892 valuePos++;
894 num++;
896 if (pos+num > len)
897 return false;
899 pos += num + 1;
900 num = 0;
901 continue;
902 } else if (chars[pos] == '\\') {
903 if (pos+1 >= len)
904 return false;
906 if (s [valuePos] != chars [pos + num])
907 return false;
908 valuePos++;
909 if (valuePos == s.Length)
910 return false;
912 pos++;
913 continue;
914 } else if (chars[pos] == '%') {
915 pos++;
916 continue;
917 } else if (Char.IsWhiteSpace (s [valuePos])) {
918 valuePos++;
920 if (Char.IsWhiteSpace (chars[pos])) {
921 pos++;
922 continue;
925 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0)
926 return false;
927 int ws = valuePos;
928 while (ws < s.Length) {
929 if (Char.IsWhiteSpace (s [ws]))
930 ws++;
931 else
932 break;
934 valuePos = ws;
938 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
939 num++;
940 continue;
943 int num_parsed = 0;
945 if (pos+num+1 < len) {
946 char next_char = chars[pos+num+1];
948 next_not_digit = !(next_char == 'd' ||
949 next_char == 'M' ||
950 next_char == 'y' ||
951 next_char == 'h' ||
952 next_char == 'H' ||
953 next_char == 'm' ||
954 next_char == 's' ||
955 next_char == 'f' ||
956 next_char == 'z' ||
957 next_char == '"' ||
958 next_char == '\'' ||
959 Char.IsDigit (next_char));
960 } else {
961 next_not_digit = true;
964 switch (chars[pos])
966 case 'd':
967 if (day != -1)
968 return false;
969 if (num == 0)
970 day = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
971 else if (num == 1)
972 day = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
973 else if (num == 2)
974 dayofweek = _ParseEnum (s, valuePos, dfi.AbbreviatedDayNames, out num_parsed);
975 else
977 dayofweek = _ParseEnum (s, valuePos, dfi.DayNames, out num_parsed);
978 num = 3;
980 break;
981 case 'M':
982 if (month != -1)
983 return false;
984 if (num == 0)
985 month = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
986 else if (num == 1)
987 month = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
988 else if (num == 2)
989 month = _ParseEnum (s, valuePos, dfi.AbbreviatedMonthNames , out num_parsed) + 1;
990 else
992 month = _ParseEnum (s, valuePos, dfi.MonthNames, out num_parsed) + 1;
993 num = 3;
995 break;
996 case 'y':
997 if (year != -1)
998 return false;
1000 if (num == 0) {
1001 year = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1002 } else if (num < 3) {
1003 year = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1004 } else {
1005 year = _ParseNumber (s, valuePos, 4, false, sloppy_parsing, next_not_digit, out num_parsed);
1006 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1007 int np = 0;
1008 int ly = _ParseNumber (s, valuePos, 5, false, sloppy_parsing, next_not_digit, out np);
1009 longYear = (ly > 9999);
1011 num = 3;
1014 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1015 if (num_parsed <= 2)
1016 year += (year < 30) ? 2000 : 1900;
1017 break;
1018 case 'h':
1019 if (hour != -1)
1020 return false;
1021 if (num == 0)
1022 hour = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1023 else
1025 hour = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1026 num = 1;
1029 if (hour > 12)
1030 return false;
1031 if (hour == 12)
1032 hour = 0;
1034 break;
1035 case 'H':
1036 if ((hour != -1) || (ampm >= 0))
1037 return false;
1038 if (num == 0)
1039 hour = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1040 else
1042 hour = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1043 num = 1;
1045 if (hour >= 24)
1046 return false;
1048 ampm = -2;
1049 break;
1050 case 'm':
1051 if (minute != -1)
1052 return false;
1053 if (num == 0)
1054 minute = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1055 else
1057 minute = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1058 num = 1;
1060 if (minute >= 60)
1061 return false;
1063 break;
1064 case 's':
1065 if (second != -1)
1066 return false;
1067 if (num == 0)
1068 second = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1069 else
1071 second = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1072 num = 1;
1074 if (second >= 60)
1075 return false;
1077 break;
1078 case 'f':
1079 if (fractionalSeconds != -1)
1080 return false;
1081 num = Math.Min (num, 6);
1082 double decimalNumber = (double) _ParseNumber (s, valuePos, num+1, true, sloppy_parsing, next_not_digit, out num_parsed);
1083 if (num_parsed == -1)
1084 return false;
1086 else
1087 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1088 break;
1089 case 't':
1090 if (ampm != -1)
1091 return false;
1092 if (num == 0)
1094 if (_ParseString (s, valuePos, 1, dfi.AMDesignator, out num_parsed))
1095 ampm = 0;
1096 else if (_ParseString (s, valuePos, 1, dfi.PMDesignator, out num_parsed))
1097 ampm = 1;
1098 else
1099 return false;
1101 else
1103 if (_ParseString (s, valuePos, 0, dfi.AMDesignator, out num_parsed))
1104 ampm = 0;
1105 else if (_ParseString (s, valuePos, 0, dfi.PMDesignator, out num_parsed))
1106 ampm = 1;
1107 else
1108 return false;
1109 num = 1;
1111 break;
1112 case 'z':
1113 if (tzsign != -1)
1114 return false;
1115 if (s [valuePos] == '+')
1116 tzsign = 0;
1117 else if (s [valuePos] == '-')
1118 tzsign = 1;
1119 else
1120 return false;
1121 valuePos++;
1122 if (num == 0)
1123 tzoffset = _ParseNumber (s, valuePos, 2, false, sloppy_parsing, next_not_digit, out num_parsed);
1124 else if (num == 1)
1125 tzoffset = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, false, out num_parsed);
1126 else
1128 tzoffset = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, next_not_digit, out num_parsed);
1129 if (num_parsed < 0)
1130 return false;
1131 valuePos += num_parsed;
1132 if (Char.IsDigit (s [valuePos]))
1133 num_parsed = 0;
1134 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1135 return false;
1136 valuePos += num_parsed;
1137 tzoffmin = _ParseNumber (s, valuePos, 2, true, sloppy_parsing, false, out num_parsed);
1138 if (num_parsed < 0)
1139 return false;
1140 num = 2;
1142 break;
1144 // LAMESPEC: This should be part of UTCpattern
1145 // string and thus should not be considered here.
1147 // Note that 'Z' is not defined as a pattern
1148 // character. Keep it for X509 certificate
1149 // verification. Also, "Z" != "'Z'" under MS.NET
1150 // ("'Z'" is just literal; handled above)
1151 case 'Z':
1152 if (s [valuePos] != 'Z')
1153 return false;
1154 num = 0;
1155 num_parsed = 1;
1156 useutc = true;
1157 break;
1159 case ':':
1160 if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1161 return false;
1162 break;
1163 case '/':
1164 /* Accept any character for
1165 * DateSeparator, except
1166 * TimeSeparator, a digit or a
1167 * letter. Not documented,
1168 * but seems to be MS
1169 * behaviour here. See bug
1170 * 54047.
1172 if (exact && s [valuePos] != '/')
1173 return false;
1175 if (_ParseString (s, valuePos, 0,
1176 dfi.TimeSeparator,
1177 out num_parsed) ||
1178 Char.IsDigit (s [valuePos]) ||
1179 Char.IsLetter (s [valuePos])) {
1180 return(false);
1183 num = 0;
1184 if (num_parsed <= 0) {
1185 num_parsed = 1;
1188 break;
1189 default:
1190 if (s [valuePos] != chars[pos]) {
1191 // FIXME: It is not sure, but
1192 // IsLetter() is introduced
1193 // because we have to reject
1194 // "2002a02b25" but have to
1195 // allow "2002$02$25". The same
1196 // thing applies to '/' case.
1197 if (exact ||
1198 Char.IsDigit (s [valuePos]) ||
1199 Char.IsLetter (s [valuePos]))
1200 return false;
1202 num = 0;
1203 num_parsed = 1;
1204 break;
1207 if (num_parsed < 0)
1208 return false;
1210 valuePos += num_parsed;
1212 if (!exact) {
1213 switch (chars [pos]) {
1214 case 'm':
1215 case 's':
1216 case 'f':
1217 case 'z':
1218 if (s.Length > valuePos && s [valuePos] == 'Z'
1219 && (pos + 1 == chars.Length
1220 || chars [pos + 1] != 'Z')) {
1221 useutc = true;
1222 valuePos++;
1224 break;
1228 pos = pos + num + 1;
1229 num = 0;
1232 if (exact && pos < len)
1233 return false;
1235 if (s.Length != valuePos) // extraneous tail.
1236 return false;
1238 if (hour == -1)
1239 hour = 0;
1240 if (minute == -1)
1241 minute = 0;
1243 if (second == -1)
1244 second = 0;
1245 if (fractionalSeconds == -1)
1246 fractionalSeconds = 0;
1248 // If no date was given
1249 if ((day == -1) && (month == -1) && (year == -1)) {
1250 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1251 day = 1;
1252 month = 1;
1253 year = 1;
1254 } else {
1255 day = Today.Day;
1256 month = Today.Month;
1257 year = Today.Year;
1262 if (day == -1)
1263 day = 1;
1264 if (month == -1)
1265 month = 1;
1266 if (year == -1) {
1267 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1268 year = 1;
1269 else
1270 year = Today.Year;
1273 if (ampm == 1)
1274 hour = hour + 12;
1276 // For anything out of range
1277 // return false
1278 if ( year < 1 || year > 9999 ||
1279 month < 1 || month >12 ||
1280 day < 1 || day > DaysInMonth(year, month) ||
1281 hour < 0 || hour > 23 ||
1282 minute < 0 || minute > 59 ||
1283 second < 0 || second > 59 )
1284 return false;
1286 result = new DateTime (year, month, day, hour, minute, second, 0);
1287 result = result.AddSeconds(fractionalSeconds);
1289 //Console.WriteLine ("**** Parsed as {1} {0} {2}", new object [] {useutc ? "[u]" : "", format, use_localtime ? "[lt]" : ""});
1290 if ((dayofweek != -1) && (dayofweek != (int) result.DayOfWeek))
1291 throw new FormatException (Locale.GetText ("String was not recognized as valid DateTime because the day of week was incorrect."));
1293 // If no timezone was specified, default to the local timezone.
1294 TimeSpan utcoffset;
1296 if (useutc)
1297 utcoffset = new TimeSpan (0, 0, 0);
1298 else if (tzsign == -1) {
1299 TimeZone tz = TimeZone.CurrentTimeZone;
1300 utcoffset = tz.GetUtcOffset (result);
1301 } else {
1302 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1303 use_localtime = false;
1305 if (tzoffmin == -1)
1306 tzoffmin = 0;
1307 if (tzoffset == -1)
1308 tzoffset = 0;
1309 if (tzsign == 1)
1310 tzoffset = -tzoffset;
1312 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1315 long newticks = (result.ticks - utcoffset).Ticks;
1317 result = new DateTime (use_localtime, newticks);
1319 return true;
1323 public static DateTime ParseExact (string s, string format,
1324 IFormatProvider fp, DateTimeStyles style)
1326 string[] formats;
1328 formats = new string [1];
1329 formats[0] = format;
1331 return ParseExact (s, formats, fp, style);
1334 public static DateTime ParseExact (string s, string[] formats,
1335 IFormatProvider fp,
1336 DateTimeStyles style)
1338 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1340 if (s == null)
1341 throw new ArgumentNullException (Locale.GetText ("s is null"));
1342 if (formats == null || formats.Length == 0)
1343 throw new ArgumentNullException (Locale.GetText ("format is null"));
1345 DateTime result;
1346 bool longYear = false;
1347 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1348 throw new FormatException ();
1349 return result;
1352 private static bool ParseExact (string s, string [] formats,
1353 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1354 bool exact, ref bool longYear)
1356 int i;
1357 for (i = 0; i < formats.Length; i++)
1359 DateTime result;
1361 if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
1362 ret = result;
1363 return true;
1366 ret = DateTime.MinValue;
1367 return false;
1370 public TimeSpan Subtract(DateTime dt)
1372 return new TimeSpan(ticks.Ticks) - dt.ticks;
1375 public DateTime Subtract(TimeSpan ts)
1377 TimeSpan newticks;
1379 newticks = (new TimeSpan (ticks.Ticks)) - ts;
1380 return new DateTime(true,newticks);
1383 public long ToFileTime()
1385 DateTime universalTime = ToUniversalTime();
1387 if (universalTime.Ticks < w32file_epoch) {
1388 throw new ArgumentOutOfRangeException("file time is not valid");
1391 return(universalTime.Ticks - w32file_epoch);
1394 #if NET_1_1
1395 public long ToFileTimeUtc()
1397 if (Ticks < w32file_epoch) {
1398 throw new ArgumentOutOfRangeException("file time is not valid");
1401 return (Ticks - w32file_epoch);
1403 #endif
1405 public string ToLongDateString()
1407 return ToString ("D");
1410 public string ToLongTimeString()
1412 return ToString ("T");
1415 public double ToOADate ()
1417 long t = this.Ticks;
1418 // uninitialized DateTime case
1419 if (t == 0)
1420 return 0;
1421 // we can't reach minimum value
1422 if (t < 31242239136000000)
1423 return OAMinValue + 0.001;
1425 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1426 double result = ts.TotalDays;
1427 // t < 0 (where 599264352000000000 == 0.0d for OA)
1428 if (t < 599264352000000000) {
1429 // negative days (int) but decimals are positive
1430 double d = Math.Ceiling (result);
1431 result = d - 2 - (result - d);
1433 else {
1434 // we can't reach maximum value
1435 if (result >= OAMaxValue)
1436 result = OAMaxValue - 0.00000001d;
1438 return result;
1441 public string ToShortDateString()
1443 return ToString ("d");
1446 public string ToShortTimeString()
1448 return ToString ("t");
1451 public override string ToString ()
1453 return ToString ("G", null);
1456 public string ToString (IFormatProvider fp)
1458 return ToString (null, fp);
1461 public string ToString (string format)
1463 return ToString (format, null);
1466 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1468 String pattern;
1470 useutc = false;
1471 use_invariant = false;
1473 switch (format)
1475 case 'd':
1476 pattern = dfi.ShortDatePattern;
1477 break;
1478 case 'D':
1479 pattern = dfi.LongDatePattern;
1480 break;
1481 case 'f':
1482 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1483 break;
1484 case 'F':
1485 pattern = dfi.FullDateTimePattern;
1486 break;
1487 case 'g':
1488 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1489 break;
1490 case 'G':
1491 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1492 break;
1493 case 'm':
1494 case 'M':
1495 pattern = dfi.MonthDayPattern;
1496 break;
1497 case 'r':
1498 case 'R':
1499 pattern = dfi.RFC1123Pattern;
1500 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1501 // uncommented by AE 27/may/2004
1502 // useutc = true;
1503 use_invariant = true;
1504 break;
1505 case 's':
1506 pattern = dfi.SortableDateTimePattern;
1507 break;
1508 case 't':
1509 pattern = dfi.ShortTimePattern;
1510 break;
1511 case 'T':
1512 pattern = dfi.LongTimePattern;
1513 break;
1514 case 'u':
1515 pattern = dfi.UniversalSortableDateTimePattern;
1516 useutc = true;
1517 break;
1518 case 'U':
1519 // pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
1520 pattern = dfi.FullDateTimePattern;
1521 useutc = true;
1522 break;
1523 case 'y':
1524 case 'Y':
1525 pattern = dfi.YearMonthPattern;
1526 break;
1527 default:
1528 pattern = null;
1529 break;
1532 return pattern;
1535 internal string _ToString (string format, DateTimeFormatInfo dfi)
1537 // the length of the format is usually a good guess of the number
1538 // of chars in the result. Might save us a few bytes sometimes
1539 // Add + 10 for cases like mmmm dddd
1540 StringBuilder result = new StringBuilder (format.Length + 10);
1542 // For some cases, the output should not use culture dependent calendar
1543 DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
1544 if (format == inv.RFC1123Pattern)
1545 dfi = inv;
1546 else if (format == inv.UniversalSortableDateTimePattern)
1547 dfi = inv;
1549 int i = 0;
1551 while (i < format.Length) {
1552 int tokLen;
1553 char ch = format [i];
1555 switch (ch) {
1558 // Time Formats
1560 case 'h':
1561 // hour, [1, 12]
1562 tokLen = CountRepeat (format, i, ch);
1564 int hr = this.Hour % 12;
1565 if (hr == 0)
1566 hr = 12;
1568 ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
1569 break;
1570 case 'H':
1571 // hour, [0, 23]
1572 tokLen = CountRepeat (format, i, ch);
1573 ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
1574 break;
1575 case 'm':
1576 // minute, [0, 59]
1577 tokLen = CountRepeat (format, i, ch);
1578 ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
1579 break;
1580 case 's':
1581 // second [0, 29]
1582 tokLen = CountRepeat (format, i, ch);
1583 ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
1584 break;
1585 case 'f':
1586 // fraction of second, to same number of
1587 // digits as there are f's
1589 tokLen = CountRepeat (format, i, ch);
1590 if (tokLen > 7)
1591 throw new FormatException ("Invalid Format String");
1593 int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
1594 ZeroPad (result, dec, tokLen);
1596 break;
1597 case 't':
1598 // AM/PM. t == first char, tt+ == full
1599 tokLen = CountRepeat (format, i, ch);
1600 string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
1602 if (tokLen == 1) {
1603 if (desig.Length >= 1)
1604 result.Append (desig [0]);
1606 else
1607 result.Append (desig);
1609 break;
1610 case 'z':
1611 // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
1612 tokLen = CountRepeat (format, i, ch);
1613 TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
1615 if (offset.Ticks >= 0)
1616 result.Append ('+');
1617 else
1618 result.Append ('-');
1620 switch (tokLen) {
1621 case 1:
1622 result.Append (Math.Abs (offset.Hours));
1623 break;
1624 case 2:
1625 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1626 break;
1627 default:
1628 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1629 result.Append (':');
1630 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
1631 break;
1633 break;
1635 // Date tokens
1637 case 'd':
1638 // day. d(d?) = day of month (leading 0 if two d's)
1639 // ddd = three leter day of week
1640 // dddd+ full day-of-week
1641 tokLen = CountRepeat (format, i, ch);
1643 if (tokLen <= 2)
1644 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
1645 else if (tokLen == 3)
1646 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
1647 else
1648 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
1650 break;
1651 case 'M':
1652 // Month.m(m?) = month # (with leading 0 if two mm)
1653 // mmm = 3 letter name
1654 // mmmm+ = full name
1655 tokLen = CountRepeat (format, i, ch);
1656 int month = dfi.Calendar.GetMonth(this);
1657 if (tokLen <= 2)
1658 ZeroPad (result, month, tokLen);
1659 else if (tokLen == 3)
1660 result.Append (dfi.GetAbbreviatedMonthName (month));
1661 else
1662 result.Append (dfi.GetMonthName (month));
1664 break;
1665 case 'y':
1666 // Year. y(y?) = two digit year, with leading 0 if yy
1667 // yyy+ full year, if yyy and yr < 1000, displayed as three digits
1668 tokLen = CountRepeat (format, i, ch);
1670 if (tokLen <= 2)
1671 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
1672 else
1673 ZeroPad (result, dfi.Calendar.GetYear (this), (tokLen == 3 ? 3 : 4));
1675 break;
1676 case 'g':
1677 // Era name
1678 tokLen = CountRepeat (format, i, ch);
1679 result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
1680 break;
1683 // Other
1685 case ':':
1686 result.Append (dfi.TimeSeparator);
1687 tokLen = 1;
1688 break;
1689 case '/':
1690 result.Append (dfi.DateSeparator);
1691 tokLen = 1;
1692 break;
1693 case '\'': case '"':
1694 tokLen = ParseQuotedString (format, i, result);
1695 break;
1696 case '%':
1697 if (i >= format.Length - 1)
1698 throw new FormatException ("% at end of date time string");
1699 if (format [i + 1] == '%')
1700 throw new FormatException ("%% in date string");
1702 // Look for the next char
1703 tokLen = 1;
1704 break;
1705 case '\\':
1706 // C-Style escape
1707 if (i >= format.Length - 1)
1708 throw new FormatException ("\\ at end of date time string");
1710 result.Append (format [i + 1]);
1711 tokLen = 2;
1713 break;
1714 default:
1715 // catch all
1716 result.Append (ch);
1717 tokLen = 1;
1718 break;
1720 i += tokLen;
1722 return result.ToString ();
1725 static int CountRepeat (string fmt, int p, char c)
1727 int l = fmt.Length;
1728 int i = p + 1;
1729 while ((i < l) && (fmt [i] == c))
1730 i++;
1732 return i - p;
1735 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
1737 // pos == position of " or '
1739 int len = fmt.Length;
1740 int start = pos;
1741 char quoteChar = fmt [pos++];
1743 while (pos < len) {
1744 char ch = fmt [pos++];
1746 if (ch == quoteChar)
1747 return pos - start;
1749 if (ch == '\\') {
1750 // C-Style escape
1751 if (pos >= len)
1752 throw new FormatException("Un-ended quote");
1754 output.Append (fmt [pos++]);
1755 } else {
1756 output.Append (ch);
1760 throw new FormatException("Un-ended quote");
1763 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
1765 // more than enough for an int
1766 char* buffer = stackalloc char [16];
1767 int pos = 16;
1769 do {
1770 buffer [-- pos] = (char) ('0' + digits % 10);
1771 digits /= 10;
1772 len --;
1773 } while (digits > 0);
1775 while (len -- > 0)
1776 buffer [-- pos] = '0';
1778 output.Append (new string (buffer, pos, 16 - pos));
1781 public string ToString (string format, IFormatProvider fp)
1784 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
1786 if (format == null)
1787 format = "G";
1789 bool useutc = false, use_invariant = false;
1791 if (format.Length == 1) {
1792 char fchar = format [0];
1793 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1796 // Don't convert UTC value. It just adds 'Z' for
1797 // 'u' format, for the same ticks.
1798 return this._ToString (format, dfi);
1801 public DateTime ToLocalTime()
1803 TimeZone tz = TimeZone.CurrentTimeZone;
1805 TimeSpan offset = tz.GetUtcOffset (this);
1807 if (offset.Ticks > 0) {
1808 if (DateTime.MaxValue - offset < this)
1809 return DateTime.MaxValue;
1810 } else if (offset.Ticks < 0) {
1811 // MS.NET fails to check validity here
1812 // - it may throw ArgumentOutOfRangeException
1814 if (DateTime.MinValue - offset > this)
1815 return DateTime.MinValue;
1819 DateTime lt = new DateTime(true, ticks+offset);
1820 TimeSpan ltoffset = tz.GetUtcOffset(lt);
1821 if(ltoffset != offset)
1822 lt = lt.Add(ltoffset.Subtract(offset));
1824 return lt;
1827 public DateTime ToUniversalTime()
1829 TimeZone tz = TimeZone.CurrentTimeZone;
1831 TimeSpan offset = tz.GetUtcOffset (this);
1833 if (offset.Ticks < 0) {
1834 if (DateTime.MaxValue + offset < this)
1835 return DateTime.MaxValue;
1836 } else if (offset.Ticks > 0) {
1837 if (DateTime.MinValue + offset > this)
1838 return DateTime.MinValue;
1841 return new DateTime (false, ticks - offset);
1844 /* OPERATORS */
1846 public static DateTime operator +(DateTime d, TimeSpan t)
1848 return new DateTime (true, d.ticks + t);
1851 public static bool operator ==(DateTime d1, DateTime d2)
1853 return (d1.ticks == d2.ticks);
1856 public static bool operator >(DateTime t1,DateTime t2)
1858 return (t1.ticks > t2.ticks);
1861 public static bool operator >=(DateTime t1,DateTime t2)
1863 return (t1.ticks >= t2.ticks);
1866 public static bool operator !=(DateTime d1, DateTime d2)
1868 return (d1.ticks != d2.ticks);
1871 public static bool operator <(DateTime t1, DateTime t2)
1873 return (t1.ticks < t2.ticks );
1876 public static bool operator <=(DateTime t1,DateTime t2)
1878 return (t1.ticks <= t2.ticks);
1881 public static TimeSpan operator -(DateTime d1,DateTime d2)
1883 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
1886 public static DateTime operator -(DateTime d,TimeSpan t)
1888 return new DateTime (true, d.ticks - t);
1891 bool IConvertible.ToBoolean(IFormatProvider provider)
1893 throw new InvalidCastException();
1896 byte IConvertible.ToByte(IFormatProvider provider)
1898 throw new InvalidCastException();
1902 char IConvertible.ToChar(IFormatProvider provider)
1904 throw new InvalidCastException();
1907 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
1909 return this;
1912 decimal IConvertible.ToDecimal(IFormatProvider provider)
1914 throw new InvalidCastException();
1917 double IConvertible.ToDouble(IFormatProvider provider)
1919 throw new InvalidCastException();
1922 Int16 IConvertible.ToInt16(IFormatProvider provider)
1924 throw new InvalidCastException();
1927 Int32 IConvertible.ToInt32(IFormatProvider provider)
1929 throw new InvalidCastException();
1932 Int64 IConvertible.ToInt64(IFormatProvider provider)
1934 throw new InvalidCastException();
1937 SByte IConvertible.ToSByte(IFormatProvider provider)
1939 throw new InvalidCastException();
1942 Single IConvertible.ToSingle(IFormatProvider provider)
1944 throw new InvalidCastException();
1947 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1949 if (conversionType == null)
1950 throw new ArgumentNullException ("conversionType");
1952 if (conversionType == typeof (DateTime))
1953 return this;
1954 else if (conversionType == typeof (String))
1955 return this.ToString (provider);
1956 else if (conversionType == typeof (Object))
1957 return this;
1958 else
1959 throw new InvalidCastException();
1962 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
1964 throw new InvalidCastException();
1967 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
1969 throw new InvalidCastException();
1972 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
1974 throw new InvalidCastException();
1979 namespace System
1981 public enum DayOfWeek
1983 Sunday,
1984 Monday,
1985 Tuesday,
1986 Wednesday,
1987 Thursday,
1988 Friday,
1989 Saturday