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-2005 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 private static readonly string[] commonFormats = {
85 // For compatibility with MS's CLR, this format (which
86 // doesn't have a one-letter equivalent) is parsed
87 // too. It's important because it's used in XML
90 // Note that those format should be tried only for
93 // FIXME: SOME OF those patterns looks tried against
94 // the current culture, since some patterns fail in
98 "yyyy-MM-ddTHH:mm:sszzz",
99 "yyyy-MM-ddTHH:mm:ss.fffffff",
100 "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
102 "yyyy-M-d H:m:s.fffffff",
103 // UTC / allow any separator
104 "yyyy/MM/ddTHH:mm:ssZ",
109 "yyyy/MM/dd HH:mm:ss 'GMT'",
112 // Close to RFC1123, but without 'GMT'
113 "ddd, d MMM yyyy HH:mm:ss",
114 // use UTC ('Z'; not literal "'Z'")
115 // FIXME: 1078(af-ZA) and 1079(ka-GE) reject it
116 "yyyy/MM/dd HH':'mm':'ssZ",
119 "M/d/yyyy HH':'mm':'ss tt",
121 // another funky COM dependent one
124 // DayOfTheWeek, dd full_month_name yyyy
125 // FIXME: 1054(th-TH) rejects them
126 "dddd, dd MMMM yyyy",
127 "dddd, dd MMMM yyyy HH:mm",
128 "dddd, dd MMMM yyyy HH:mm:ss",
131 // DayOfTheWeek, dd yyyy. This works for every locales.
134 // X509Certificate pattern is accepted by Parse() *in every culture*
137 // In Parse() the 'r' equivalent pattern is first parsed as universal time
138 "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'",
140 // Additionally there seems language-specific format
141 // patterns that however works in all language
143 // For example, the pattern below is for Japanese.
144 "yyyy'\u5E74'MM'\u6708'dd'\u65E5' HH'\u6642'mm'\u5206'ss'\u79D2'",
147 // Full date and time
148 "F", "G", "r", "s", "u", "U",
149 // Full date and time, but no seconds
155 // Only date, but no year
157 // Only date, but no day
170 private static readonly int[] daysmonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
171 private static readonly int[] daysmonthleap = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
173 private static int AbsoluteDays (int year, int month, int day)
178 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
182 return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
185 private int FromTicks(Which what)
187 int num400, num100, num4, numyears;
190 int[] days = daysmonth;
191 int totaldays = this.ticks.Days;
193 num400 = (totaldays / dp400);
194 totaldays -= num400 * dp400;
196 num100 = (totaldays / dp100);
197 if (num100 == 4) // leap
199 totaldays -= (num100 * dp100);
201 num4 = totaldays / dp4;
202 totaldays -= (num4 * dp4);
204 numyears = totaldays / 365 ;
206 if (numyears == 4) //leap
208 if (what == Which.Year )
209 return num400*400 + num100*100 + num4*4 + numyears + 1;
211 totaldays -= (numyears * 365) ;
212 if (what == Which.DayYear )
213 return totaldays + 1;
215 if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
216 days = daysmonthleap;
218 while (totaldays >= days[M])
219 totaldays -= days[M++];
221 if (what == Which.Month )
231 /// Constructs a DateTime for specified ticks
234 public DateTime (long ticks)
236 this.ticks = new TimeSpan (ticks);
237 if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
238 string msg = Locale.GetText ("Value {0} is outside the valid range [{1},{2}].",
239 ticks, MinValue.Ticks, MaxValue.Ticks);
240 throw new ArgumentOutOfRangeException ("ticks", msg);
243 kind = DateTimeKind.Unspecified;
247 public DateTime (int year, int month, int day)
248 : this (year, month, day,0,0,0,0) {}
250 public DateTime (int year, int month, int day, int hour, int minute, int second)
251 : this (year, month, day, hour, minute, second, 0) {}
253 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond)
255 if ( year < 1 || year > 9999 ||
256 month < 1 || month >12 ||
257 day < 1 || day > DaysInMonth(year, month) ||
258 hour < 0 || hour > 23 ||
259 minute < 0 || minute > 59 ||
260 second < 0 || second > 59 ||
261 millisecond < 0 || millisecond > 999)
262 throw new ArgumentOutOfRangeException ("Parameters describe an " +
263 "unrepresentable DateTime.");
265 ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
268 kind = DateTimeKind.Unspecified;
272 [MonoTODO ("Calendar is unused")]
273 public DateTime (int year, int month, int day, Calendar calendar)
274 : this (year, month, day, 0, 0, 0, 0, calendar)
278 [MonoTODO ("Calendar is unused")]
279 public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
280 : this (year, month, day, hour, minute, second, 0, calendar)
284 [MonoTODO ("Calendar is unused")]
285 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
286 : this (year, month, day, hour, minute, second, millisecond)
288 if (calendar == null)
289 throw new ArgumentNullException ("calendar");
290 // FIXME: we're not using the calendar anywhere???
293 internal DateTime (bool check, TimeSpan value)
295 if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
296 throw new ArgumentOutOfRangeException ();
301 kind = DateTimeKind.Unspecified;
306 public DateTime (long ticks, DateTimeKind kind) : this (ticks)
308 CheckDateTimeKind (kind);
312 public DateTime (int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
313 : this (year, month, day, hour, minute, second)
315 CheckDateTimeKind (kind);
319 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
320 : this (year, month, day, hour, minute, second, millisecond)
322 CheckDateTimeKind (kind);
326 public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
327 : this (year, month, day, hour, minute, second, millisecond, calendar)
329 CheckDateTimeKind (kind);
340 return new DateTime (Year, Month, Day);
348 return FromTicks(Which.Month);
357 return FromTicks(Which.Day);
361 public DayOfWeek DayOfWeek
365 return ( (DayOfWeek) ((ticks.Days+1) % 7) );
373 return FromTicks(Which.DayYear);
377 public TimeSpan TimeOfDay
381 return new TimeSpan(ticks.Ticks % TimeSpan.TicksPerDay );
398 return ticks.Minutes;
406 return ticks.Seconds;
410 public int Millisecond
414 return ticks.Milliseconds;
418 [MethodImplAttribute(MethodImplOptions.InternalCall)]
419 internal static extern long GetNow ();
421 public static DateTime Now
425 return new DateTime (GetNow ()).ToLocalTime ();
437 public static DateTime Today
441 DateTime today = new DateTime (now.Year, now.Month, now.Day);
443 today.kind = now.kind;
449 public static DateTime UtcNow
453 return new DateTime (GetNow (), DateTimeKind.Utc);
455 return new DateTime (GetNow ());
464 return FromTicks(Which.Year);
469 public DateTimeKind Kind {
478 public DateTime Add (TimeSpan ts)
480 return AddTicks (ts.Ticks);
483 public DateTime AddDays (double days)
485 return AddMilliseconds (Math.Round (days * 86400000));
488 public DateTime AddTicks (long t)
490 if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
491 throw new ArgumentOutOfRangeException();
493 return new DateTime (t + ticks.Ticks);
496 public DateTime AddHours (double hours)
498 return AddMilliseconds (hours * 3600000);
501 public DateTime AddMilliseconds (double ms)
503 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
504 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
505 throw new ArgumentOutOfRangeException();
507 long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
509 return AddTicks (msticks);
512 // required to match MS implementation for OADate (OLE Automation)
513 private DateTime AddRoundedMilliseconds (double ms)
515 if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
516 (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
517 throw new ArgumentOutOfRangeException ();
519 long msticks = (long) (ms += ms > 0 ? 0.5 : -0.5) * TimeSpan.TicksPerMillisecond;
521 return AddTicks (msticks);
524 public DateTime AddMinutes (double minutes)
526 return AddMilliseconds (minutes * 60000);
529 public DateTime AddMonths (int months)
531 int day, month, year, maxday ;
535 month = this.Month + (months % 12);
536 year = this.Year + months/12 ;
548 maxday = DaysInMonth(year, month);
552 temp = new DateTime (year, month, day);
553 return temp.Add (this.TimeOfDay);
556 public DateTime AddSeconds (double seconds)
558 return AddMilliseconds (seconds*1000);
561 public DateTime AddYears (int years )
563 return AddMonths(years * 12);
566 public static int Compare (DateTime t1, DateTime t2)
568 if (t1.ticks < t2.ticks)
570 else if (t1.ticks > t2.ticks)
576 public int CompareTo (object v)
581 if (!(v is System.DateTime))
582 throw new ArgumentException (Locale.GetText (
583 "Value is not a System.DateTime"));
585 return Compare (this, (DateTime) v);
589 public bool IsDaylightSavingTime ()
591 if (kind == DateTimeKind.Utc)
593 return TimeZone.CurrentTimeZone.IsDaylightSavingTime (this);
596 public int CompareTo (DateTime value)
598 return Compare (this, value);
601 public bool Equals (DateTime value)
603 return value.ticks == ticks;
606 public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
608 return new DateTime (value.Ticks, kind);
612 public static int DaysInMonth (int year, int month)
616 if (month < 1 || month >12)
617 throw new ArgumentOutOfRangeException ();
619 days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
623 public override bool Equals (object o)
625 if (!(o is System.DateTime))
628 return ((DateTime) o).ticks == ticks;
631 public static bool Equals (DateTime t1, DateTime t2 )
633 return (t1.ticks == t2.ticks );
636 public static DateTime FromFileTime (long fileTime)
639 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
641 return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
645 public static DateTime FromFileTimeUtc (long fileTime)
648 throw new ArgumentOutOfRangeException ("fileTime", "< 0");
650 return new DateTime (w32file_epoch + fileTime);
654 public static DateTime FromOADate (double d)
656 // An OLE Automation date is implemented as a floating-point number
657 // whose value is the number of days from midnight, 30 December 1899.
659 // d must be negative 657435.0 through positive 2958466.0.
660 if ((d <= OAMinValue) || (d >= OAMaxValue))
661 throw new ArgumentException ("d", "[-657435,2958466]");
663 DateTime dt = new DateTime (ticks18991230);
665 Double days = Math.Ceiling (d);
666 // integer part is the number of days (negative)
667 dt = dt.AddRoundedMilliseconds (days * 86400000);
668 // but decimals are the number of hours (in days fractions) and positive
669 Double hours = (days - d);
670 dt = dt.AddRoundedMilliseconds (hours * 86400000);
673 dt = dt.AddRoundedMilliseconds (d * 86400000);
679 public string[] GetDateTimeFormats()
681 return GetDateTimeFormats (CultureInfo.CurrentCulture);
684 public string[] GetDateTimeFormats(char format)
686 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
687 throw new FormatException ("Invalid format character.");
688 string[] result = new string[1];
689 result[0] = this.ToString(format.ToString());
693 public string[] GetDateTimeFormats(IFormatProvider provider)
695 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
696 // return GetDateTimeFormats (info.GetAllDateTimePatterns ());
697 ArrayList al = new ArrayList ();
698 foreach (char c in "dDgGfFmMrRstTuUyY")
699 al.AddRange (GetDateTimeFormats (c, info));
700 return al.ToArray (typeof (string)) as string [];
703 public string[] GetDateTimeFormats(char format,IFormatProvider provider )
705 if ("dDgGfFmMrRstTuUyY".IndexOf (format) < 0)
706 throw new FormatException ("Invalid format character.");
708 // LAMESPEC: There is NO assurance that 'U' ALWAYS
709 // euqals to 'F', but since we have to iterate all
710 // the pattern strings, we cannot just use
711 // ToString("U", provider) here. I believe that the
712 // method's behavior cannot be formalized.
713 bool adjustutc = false;
722 DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
723 return GetDateTimeFormats (adjustutc, info.GetAllDateTimePatterns (format), info);
726 private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
728 string [] results = new string [patterns.Length];
729 DateTime val = adjustutc ? ToUniversalTime () : this;
730 for (int i = 0; i < results.Length; i++)
731 results [i] = val._ToString (patterns [i], dfi);
736 private void CheckDateTimeKind (DateTimeKind kind) {
737 if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
738 throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
742 public override int GetHashCode ()
744 return (int) ticks.Ticks;
747 public TypeCode GetTypeCode ()
749 return TypeCode.DateTime;
752 public static bool IsLeapYear (int year)
754 return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
757 public static DateTime Parse (string s)
759 return Parse (s, null);
762 public static DateTime Parse (string s, IFormatProvider fp)
764 return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
767 [MonoTODO ("see the comments inline")]
768 public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
770 // This method should try only expected patterns.
771 // Should not try extra patterns.
772 // Right now we also try InvariantCulture, but I
773 // confirmed in some cases this method rejects what
774 // InvariantCulture supports (can be checked against
775 // "th-TH" with Gregorian Calendar). So basically it
776 // should not be done.
777 // I think it should be CurrentCulture to be tested,
778 // but right now we don't support all the supported
779 // patterns for each culture, so try InvariantCulture
780 // as a quick remedy.
782 throw new ArgumentNullException (Locale.GetText ("s is null"));
786 fp = CultureInfo.CurrentCulture;
787 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
789 bool longYear = false;
791 // Try all the patterns
792 if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
795 // Try common formats.
796 // if (ParseExact (s, commonFormats, dfi, styles, out result, false, ref longYear))
799 // Try common formats with invariant culture
800 if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
804 throw new ArgumentOutOfRangeException ("year",
805 "Valid values are between 1 and 9999 inclusive");
808 throw new FormatException ("String was not recognized as a valid DateTime.");
811 public static DateTime ParseExact (string s, string format, IFormatProvider fp)
813 return ParseExact (s, format, fp, DateTimeStyles.None);
816 internal static int _ParseNumber (string s, int valuePos,
830 for (i = valuePos; i < s.Length && i < digits + valuePos; i++) {
831 if (!Char.IsDigit (s[i]))
837 digits = real_digits;
839 if (digits < min_digits) {
844 if (s.Length - valuePos < digits) {
849 for (i = valuePos; i < digits + valuePos; i++) {
851 if (!Char.IsDigit (c)) {
856 number = number * 10 + (byte) (c - '0');
863 internal static int _ParseEnum (string s, int sPos, string[] values, out int num_parsed)
867 for (i = 0; i < values.Length; i++) {
868 if (s.Length - sPos < values[i].Length)
870 else if (values [i].Length == 0)
872 String tmp = s.Substring (sPos, values[i].Length);
873 if (String.Compare (tmp, values[i], true) == 0) {
874 num_parsed = values[i].Length;
883 internal static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
886 maxlength = value.Length;
888 if (String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
889 num_parsed = maxlength;
897 private static bool _DoParse (string s, string format, bool exact,
899 DateTimeFormatInfo dfi,
900 DateTimeStyles style,
903 bool useutc = false, use_localtime = true;
904 bool use_invariant = false;
905 bool sloppy_parsing = false;
907 if (format.Length == 1)
908 format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
909 else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
912 if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
913 format = format.TrimStart (null);
915 s = s.TrimStart (null); // it could be optimized, but will make little good.
918 if ((style & DateTimeStyles.AllowTrailingWhite) != 0) {
919 format = format.TrimEnd (null);
920 s = s.TrimEnd (null); // it could be optimized, but will make little good.
924 dfi = DateTimeFormatInfo.InvariantInfo;
926 if ((style & DateTimeStyles.AllowInnerWhite) != 0)
927 sloppy_parsing = true;
929 string chars = format;
930 int len = format.Length, pos = 0, num = 0;
932 int day = -1, dayofweek = -1, month = -1, year = -1;
933 int hour = -1, minute = -1, second = -1;
934 double fractionalSeconds = -1;
936 int tzsign = -1, tzoffset = -1, tzoffmin = -1;
938 result = new DateTime (0);
939 while (pos+num < len)
941 if (s.Length == valuePos)
944 if (chars[pos] == '\'') {
946 while (pos+num < len) {
947 if (chars[pos+num] == '\'')
950 if (valuePos == s.Length)
952 if (s [valuePos] != chars [pos + num])
964 } else if (chars[pos] == '"') {
966 while (pos+num < len) {
967 if (chars[pos+num] == '"')
970 if (valuePos == s.Length)
972 if (s [valuePos] != chars[pos+num])
984 } else if (chars[pos] == '\\') {
990 if (s [valuePos] != chars [pos])
995 } else if (chars[pos] == '%') {
998 } else if (Char.IsWhiteSpace (s [valuePos]) ||
999 s [valuePos] == ',' && Char.IsWhiteSpace (chars [pos])) {
1002 if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
1003 if (!Char.IsWhiteSpace (chars[pos]))
1010 while (ws < s.Length) {
1011 if (Char.IsWhiteSpace (s [ws]) || s [ws] == ',')
1018 while (ws < chars.Length) {
1019 if (Char.IsWhiteSpace (chars [ws]) || chars [ws] == ',')
1028 if ((pos+num+1 < len) && (chars[pos+num+1] == chars[pos+num])) {
1042 day = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1044 day = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1046 dayofweek = _ParseEnum (s, valuePos, dfi.AbbreviatedDayNames, out num_parsed);
1049 dayofweek = _ParseEnum (s, valuePos, dfi.DayNames, out num_parsed);
1057 month = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1059 month = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1061 month = _ParseEnum (s, valuePos, dfi.AbbreviatedMonthNames , out num_parsed) + 1;
1064 month = _ParseEnum (s, valuePos, dfi.MonthNames, out num_parsed) + 1;
1073 year = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1074 } else if (num < 3) {
1075 year = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1077 year = _ParseNumber (s, valuePos,4, 4, false, sloppy_parsing, out num_parsed);
1078 if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
1080 int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
1081 longYear = (ly > 9999);
1086 //FIXME: We should do use dfi.Calendat.TwoDigitYearMax
1087 if (num_parsed <= 2)
1088 year += (year < 30) ? 2000 : 1900;
1094 hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1097 hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1108 if ((hour != -1) || (ampm >= 0))
1111 hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
1114 hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
1126 minute = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1129 minute = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1140 second = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1143 second = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1151 if (fractionalSeconds != -1)
1153 num = Math.Min (num, 6);
1154 double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, true, sloppy_parsing, out num_parsed);
1155 if (num_parsed == -1)
1159 fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
1166 if (_ParseString (s, valuePos, 1, dfi.AMDesignator, out num_parsed))
1168 else if (_ParseString (s, valuePos, 1, dfi.PMDesignator, out num_parsed))
1175 if (_ParseString (s, valuePos, 0, dfi.AMDesignator, out num_parsed))
1177 else if (_ParseString (s, valuePos, 0, dfi.PMDesignator, out num_parsed))
1187 if (s [valuePos] == '+')
1189 else if (s [valuePos] == '-')
1195 tzoffset = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
1197 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1200 tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1203 valuePos += num_parsed;
1204 if (Char.IsDigit (s [valuePos]))
1206 else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1208 valuePos += num_parsed;
1209 tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
1216 // LAMESPEC: This should be part of UTCpattern
1217 // string and thus should not be considered here.
1219 // Note that 'Z' is not defined as a pattern
1220 // character. Keep it for X509 certificate
1221 // verification. Also, "Z" != "'Z'" under MS.NET
1222 // ("'Z'" is just literal; handled above)
1224 if (s [valuePos] != 'Z')
1232 if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
1236 /* Accept any character for
1237 * DateSeparator, except
1238 * TimeSeparator, a digit or a
1239 * letter. Not documented,
1240 * but seems to be MS
1241 * behaviour here. See bug
1244 if (exact && s [valuePos] != '/')
1247 if (_ParseString (s, valuePos, 0,
1250 Char.IsDigit (s [valuePos]) ||
1251 Char.IsLetter (s [valuePos])) {
1256 if (num_parsed <= 0) {
1262 if (s [valuePos] != chars[pos]) {
1263 // FIXME: It is not sure, but
1264 // IsLetter() is introduced
1265 // because we have to reject
1266 // "2002a02b25" but have to
1267 // allow "2002$02$25". The same
1268 // thing applies to '/' case.
1270 Char.IsDigit (s [valuePos]) ||
1271 Char.IsLetter (s [valuePos]))
1282 valuePos += num_parsed;
1285 switch (chars [pos]) {
1290 if (s.Length > valuePos && s [valuePos] == 'Z'
1291 && (pos + 1 == chars.Length
1292 || chars [pos + 1] != 'Z')) {
1300 pos = pos + num + 1;
1304 // possible empty value. Regarded as no match.
1311 if (s.Length != valuePos) // extraneous tail.
1321 if (fractionalSeconds == -1)
1322 fractionalSeconds = 0;
1324 // If no date was given
1325 if ((day == -1) && (month == -1) && (year == -1)) {
1326 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
1332 month = Today.Month;
1343 if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
1352 // For anything out of range
1354 if ( year < 1 || year > 9999 ||
1355 month < 1 || month >12 ||
1356 day < 1 || day > DaysInMonth(year, month) ||
1357 hour < 0 || hour > 23 ||
1358 minute < 0 || minute > 59 ||
1359 second < 0 || second > 59 )
1362 result = new DateTime (year, month, day, hour, minute, second, 0);
1363 result = result.AddSeconds(fractionalSeconds);
1365 if ((dayofweek != -1) && (dayofweek != (int) result.DayOfWeek))
1366 throw new FormatException (Locale.GetText ("String was not recognized as valid DateTime because the day of week was incorrect."));
1368 // If no timezone was specified, default to the local timezone.
1372 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1373 use_localtime = false;
1374 utcoffset = new TimeSpan (0, 0, 0);
1375 } else if (tzsign == -1) {
1376 TimeZone tz = TimeZone.CurrentTimeZone;
1377 utcoffset = tz.GetUtcOffset (result);
1379 if ((style & DateTimeStyles.AdjustToUniversal) != 0)
1380 use_localtime = false;
1387 tzoffset = -tzoffset
1389 utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
1392 long newticks = (result.ticks - utcoffset).Ticks;
1394 result = new DateTime (false, new TimeSpan (newticks));
1396 result = result.ToLocalTime ();
1402 public static DateTime ParseExact (string s, string format,
1403 IFormatProvider fp, DateTimeStyles style)
1407 formats = new string [1];
1408 formats[0] = format;
1410 return ParseExact (s, formats, fp, style);
1413 public static DateTime ParseExact (string s, string[] formats,
1415 DateTimeStyles style)
1417 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1420 throw new ArgumentNullException (Locale.GetText ("s is null"));
1421 if (formats == null || formats.Length == 0)
1422 throw new ArgumentNullException (Locale.GetText ("format is null"));
1425 bool longYear = false;
1426 if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
1427 throw new FormatException ();
1432 public static bool TryParse (string s, out DateTime result)
1443 public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
1446 result = Parse (s, provider, styles);
1454 public static bool TryParseExact (string s, string format,
1456 DateTimeStyles style,
1457 out DateTime result)
1461 formats = new string [1];
1462 formats[0] = format;
1464 return TryParseExact (s, formats, fp, style, out result);
1467 public static bool TryParseExact (string s, string[] formats,
1469 DateTimeStyles style,
1470 out DateTime result)
1472 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
1474 bool longYear = false;
1475 return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
1479 private static bool ParseExact (string s, string [] formats,
1480 DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
1481 bool exact, ref bool longYear)
1484 for (i = 0; i < formats.Length; i++)
1488 if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
1493 ret = DateTime.MinValue;
1497 public TimeSpan Subtract(DateTime dt)
1499 return new TimeSpan(ticks.Ticks) - dt.ticks;
1502 public DateTime Subtract(TimeSpan ts)
1506 newticks = (new TimeSpan (ticks.Ticks)) - ts;
1507 return new DateTime(true,newticks);
1510 public long ToFileTime()
1512 DateTime universalTime = ToUniversalTime();
1514 if (universalTime.Ticks < w32file_epoch) {
1515 throw new ArgumentOutOfRangeException("file time is not valid");
1518 return(universalTime.Ticks - w32file_epoch);
1522 public long ToFileTimeUtc()
1524 if (Ticks < w32file_epoch) {
1525 throw new ArgumentOutOfRangeException("file time is not valid");
1528 return (Ticks - w32file_epoch);
1532 public string ToLongDateString()
1534 return ToString ("D");
1537 public string ToLongTimeString()
1539 return ToString ("T");
1542 public double ToOADate ()
1544 long t = this.Ticks;
1545 // uninitialized DateTime case
1548 // we can't reach minimum value
1549 if (t < 31242239136000000)
1550 return OAMinValue + 0.001;
1552 TimeSpan ts = new TimeSpan (this.Ticks - ticks18991230);
1553 double result = ts.TotalDays;
1554 // t < 0 (where 599264352000000000 == 0.0d for OA)
1555 if (t < 599264352000000000) {
1556 // negative days (int) but decimals are positive
1557 double d = Math.Ceiling (result);
1558 result = d - 2 - (result - d);
1561 // we can't reach maximum value
1562 if (result >= OAMaxValue)
1563 result = OAMaxValue - 0.00000001d;
1568 public string ToShortDateString()
1570 return ToString ("d");
1573 public string ToShortTimeString()
1575 return ToString ("t");
1578 public override string ToString ()
1580 return ToString ("G", null);
1583 public string ToString (IFormatProvider fp)
1585 return ToString (null, fp);
1588 public string ToString (string format)
1590 return ToString (format, null);
1593 internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
1598 use_invariant = false;
1603 pattern = dfi.ShortDatePattern;
1606 pattern = dfi.LongDatePattern;
1609 pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
1612 pattern = dfi.FullDateTimePattern;
1615 pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
1618 pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
1622 pattern = dfi.MonthDayPattern;
1626 pattern = dfi.RFC1123Pattern;
1627 // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
1628 // uncommented by AE 27/may/2004
1630 use_invariant = true;
1633 pattern = dfi.SortableDateTimePattern;
1636 pattern = dfi.ShortTimePattern;
1639 pattern = dfi.LongTimePattern;
1642 pattern = dfi.UniversalSortableDateTimePattern;
1646 // pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
1647 pattern = dfi.FullDateTimePattern;
1652 pattern = dfi.YearMonthPattern;
1662 internal string _ToString (string format, DateTimeFormatInfo dfi)
1664 // the length of the format is usually a good guess of the number
1665 // of chars in the result. Might save us a few bytes sometimes
1666 // Add + 10 for cases like mmmm dddd
1667 StringBuilder result = new StringBuilder (format.Length + 10);
1669 // For some cases, the output should not use culture dependent calendar
1670 DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
1671 if (format == inv.RFC1123Pattern)
1673 else if (format == inv.UniversalSortableDateTimePattern)
1678 while (i < format.Length) {
1680 char ch = format [i];
1689 tokLen = CountRepeat (format, i, ch);
1691 int hr = this.Hour % 12;
1695 ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
1699 tokLen = CountRepeat (format, i, ch);
1700 ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
1704 tokLen = CountRepeat (format, i, ch);
1705 ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
1709 tokLen = CountRepeat (format, i, ch);
1710 ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
1713 // fraction of second, to same number of
1714 // digits as there are f's
1716 tokLen = CountRepeat (format, i, ch);
1718 throw new FormatException ("Invalid Format String");
1720 int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
1721 ZeroPad (result, dec, tokLen);
1725 // AM/PM. t == first char, tt+ == full
1726 tokLen = CountRepeat (format, i, ch);
1727 string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
1730 if (desig.Length >= 1)
1731 result.Append (desig [0]);
1734 result.Append (desig);
1738 // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
1739 tokLen = CountRepeat (format, i, ch);
1740 TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
1742 if (offset.Ticks >= 0)
1743 result.Append ('+');
1745 result.Append ('-');
1749 result.Append (Math.Abs (offset.Hours));
1752 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1755 result.Append (Math.Abs (offset.Hours).ToString ("00"));
1756 result.Append (':');
1757 result.Append (Math.Abs (offset.Minutes).ToString ("00"));
1765 // day. d(d?) = day of month (leading 0 if two d's)
1766 // ddd = three leter day of week
1767 // dddd+ full day-of-week
1768 tokLen = CountRepeat (format, i, ch);
1771 ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
1772 else if (tokLen == 3)
1773 result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
1775 result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
1779 // Month.m(m?) = month # (with leading 0 if two mm)
1780 // mmm = 3 letter name
1781 // mmmm+ = full name
1782 tokLen = CountRepeat (format, i, ch);
1783 int month = dfi.Calendar.GetMonth(this);
1785 ZeroPad (result, month, tokLen);
1786 else if (tokLen == 3)
1787 result.Append (dfi.GetAbbreviatedMonthName (month));
1789 result.Append (dfi.GetMonthName (month));
1793 // Year. y(y?) = two digit year, with leading 0 if yy
1794 // yyy+ full year, if yyy and yr < 1000, displayed as three digits
1795 tokLen = CountRepeat (format, i, ch);
1798 ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
1800 ZeroPad (result, dfi.Calendar.GetYear (this), (tokLen == 3 ? 3 : 4));
1805 tokLen = CountRepeat (format, i, ch);
1806 result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
1813 result.Append (dfi.TimeSeparator);
1817 result.Append (dfi.DateSeparator);
1820 case '\'': case '"':
1821 tokLen = ParseQuotedString (format, i, result);
1824 if (i >= format.Length - 1)
1825 throw new FormatException ("% at end of date time string");
1826 if (format [i + 1] == '%')
1827 throw new FormatException ("%% in date string");
1829 // Look for the next char
1834 if (i >= format.Length - 1)
1835 throw new FormatException ("\\ at end of date time string");
1837 result.Append (format [i + 1]);
1849 return result.ToString ();
1852 static int CountRepeat (string fmt, int p, char c)
1856 while ((i < l) && (fmt [i] == c))
1862 static int ParseQuotedString (string fmt, int pos, StringBuilder output)
1864 // pos == position of " or '
1866 int len = fmt.Length;
1868 char quoteChar = fmt [pos++];
1871 char ch = fmt [pos++];
1873 if (ch == quoteChar)
1879 throw new FormatException("Un-ended quote");
1881 output.Append (fmt [pos++]);
1887 throw new FormatException("Un-ended quote");
1890 static unsafe void ZeroPad (StringBuilder output, int digits, int len)
1892 // more than enough for an int
1893 char* buffer = stackalloc char [16];
1897 buffer [-- pos] = (char) ('0' + digits % 10);
1900 } while (digits > 0);
1903 buffer [-- pos] = '0';
1905 output.Append (new string (buffer, pos, 16 - pos));
1908 public string ToString (string format, IFormatProvider fp)
1911 DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
1916 bool useutc = false, use_invariant = false;
1918 if (format.Length == 1) {
1919 char fchar = format [0];
1920 format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
1923 // Don't convert UTC value. It just adds 'Z' for
1924 // 'u' format, for the same ticks.
1925 return this._ToString (format, dfi);
1928 public DateTime ToLocalTime ()
1930 return TimeZone.CurrentTimeZone.ToLocalTime (this);
1933 public DateTime ToUniversalTime()
1935 return TimeZone.CurrentTimeZone.ToUniversalTime (this);
1940 public static DateTime operator +(DateTime d, TimeSpan t)
1942 return new DateTime (true, d.ticks + t);
1945 public static bool operator ==(DateTime d1, DateTime d2)
1947 return (d1.ticks == d2.ticks);
1950 public static bool operator >(DateTime t1,DateTime t2)
1952 return (t1.ticks > t2.ticks);
1955 public static bool operator >=(DateTime t1,DateTime t2)
1957 return (t1.ticks >= t2.ticks);
1960 public static bool operator !=(DateTime d1, DateTime d2)
1962 return (d1.ticks != d2.ticks);
1965 public static bool operator <(DateTime t1, DateTime t2)
1967 return (t1.ticks < t2.ticks );
1970 public static bool operator <=(DateTime t1,DateTime t2)
1972 return (t1.ticks <= t2.ticks);
1975 public static TimeSpan operator -(DateTime d1,DateTime d2)
1977 return new TimeSpan((d1.ticks - d2.ticks).Ticks);
1980 public static DateTime operator -(DateTime d,TimeSpan t)
1982 return new DateTime (true, d.ticks - t);
1985 bool IConvertible.ToBoolean(IFormatProvider provider)
1987 throw new InvalidCastException();
1990 byte IConvertible.ToByte(IFormatProvider provider)
1992 throw new InvalidCastException();
1996 char IConvertible.ToChar(IFormatProvider provider)
1998 throw new InvalidCastException();
2001 System.DateTime IConvertible.ToDateTime(IFormatProvider provider)
2006 decimal IConvertible.ToDecimal(IFormatProvider provider)
2008 throw new InvalidCastException();
2011 double IConvertible.ToDouble(IFormatProvider provider)
2013 throw new InvalidCastException();
2016 Int16 IConvertible.ToInt16(IFormatProvider provider)
2018 throw new InvalidCastException();
2021 Int32 IConvertible.ToInt32(IFormatProvider provider)
2023 throw new InvalidCastException();
2026 Int64 IConvertible.ToInt64(IFormatProvider provider)
2028 throw new InvalidCastException();
2031 SByte IConvertible.ToSByte(IFormatProvider provider)
2033 throw new InvalidCastException();
2036 Single IConvertible.ToSingle(IFormatProvider provider)
2038 throw new InvalidCastException();
2041 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2043 if (conversionType == null)
2044 throw new ArgumentNullException ("conversionType");
2046 if (conversionType == typeof (DateTime))
2048 else if (conversionType == typeof (String))
2049 return this.ToString (provider);
2050 else if (conversionType == typeof (Object))
2053 throw new InvalidCastException();
2056 UInt16 IConvertible.ToUInt16(IFormatProvider provider)
2058 throw new InvalidCastException();
2061 UInt32 IConvertible.ToUInt32(IFormatProvider provider)
2063 throw new InvalidCastException();
2066 UInt64 IConvertible.ToUInt64(IFormatProvider provider)
2068 throw new InvalidCastException();