// Atsushi Enomoto (atsushi@ximian.com)
//
// (C) 2001 Marcel Narings
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
+using System.Runtime.Serialization;
namespace System
{
///
[Serializable]
[StructLayout (LayoutKind.Auto)]
- public struct DateTime : IFormattable, IConvertible, IComparable
-#if NET_2_0
- , IComparable<DateTime>, IEquatable <DateTime>
-#endif
+ public struct DateTime : IFormattable, IConvertible, IComparable, ISerializable, IComparable<DateTime>, IEquatable <DateTime>
{
+#if MONOTOUCH
+ static DateTime () {
+ if (MonoTouchAOTHelper.FalseFlag) {
+ var comparer = new System.Collections.Generic.GenericComparer <DateTime> ();
+ var eqcomparer = new System.Collections.Generic.GenericEqualityComparer <DateTime> ();
+ }
+ }
+#endif
private TimeSpan ticks;
-#if NET_2_0
DateTimeKind kind;
-#endif
private const int dp400 = 146097;
private const int dp100 = 36524;
public static readonly DateTime MaxValue = new DateTime (false, new TimeSpan (MAX_VALUE_TICKS));
public static readonly DateTime MinValue = new DateTime (false, new TimeSpan (0));
- private static readonly string[] commonFormats = {
- // For compatibility with MS's CLR, this format (which
- // doesn't have a one-letter equivalent) is parsed
- // too. It's important because it's used in XML
- // serialization.
-
- // Note that those format should be tried only for
- // invalid patterns;
-
- // FIXME: SOME OF those patterns looks tried against
- // the current culture, since some patterns fail in
- // some culture.
-
- "yyyy-MM-dd",
- "yyyy-MM-ddTHH:mm:sszzz",
- "yyyy-MM-ddTHH:mm:ss.fffffff",
- "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
- // bug #78618
- "yyyy-M-d H:m:s.fffffff",
- // UTC / allow any separator
- "yyyy/MM/ddTHH:mm:ssZ",
- "yyyy/M/dZ",
- // bug #58938
- "yyyy/M/d HH:mm:ss",
- // bug #47720
- "yyyy/MM/dd HH:mm:ss 'GMT'",
- // bug #53023
- "MM/dd/yyyy",
- // Close to RFC1123, but without 'GMT'
- "ddd, d MMM yyyy HH:mm:ss",
- // use UTC ('Z'; not literal "'Z'")
- // FIXME: 1078(af-ZA) and 1079(ka-GE) reject it
- "yyyy/MM/dd HH':'mm':'ssZ",
-
- // bug #60912
- "M/d/yyyy HH':'mm':'ss tt",
- "H':'mm':'ss tt",
- // another funky COM dependent one
- "dd-MMM-yy",
-
- // DayOfTheWeek, dd full_month_name yyyy
- // FIXME: 1054(th-TH) rejects them
- "dddd, dd MMMM yyyy",
- "dddd, dd MMMM yyyy HH:mm",
- "dddd, dd MMMM yyyy HH:mm:ss",
-
- "yyyy MMMM",
- // DayOfTheWeek, dd yyyy. This works for every locales.
- "MMMM dd, yyyy",
-#if NET_1_1
- // X509Certificate pattern is accepted by Parse() *in every culture*
- "yyyyMMddHHmmssZ",
-#endif
- // In Parse() the 'r' equivalent pattern is first parsed as universal time
- "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'",
-
- // Additionally there seems language-specific format
- // patterns that however works in all language
- // environment.
- // For example, the pattern below is for Japanese.
- "yyyy'\u5E74'MM'\u6708'dd'\u65E5' HH'\u6642'mm'\u5206'ss'\u79D2'",
-
-/*
- // Full date and time
- "F", "G", "r", "s", "u", "U",
- // Full date and time, but no seconds
- "f", "g",
- // Only date
- "d", "D",
- // Only time
- "T", "t",
- // Only date, but no year
- "m",
- // Only date, but no day
- "y"
-*/
+ // DateTime.Parse patterns
+ // Patterns are divided to date and time patterns. The algorithm will
+ // try combinations of these patterns. The algorithm also looks for
+ // day of the week, AM/PM GMT and Z independently of the patterns.
+ private static readonly string[] ParseTimeFormats = new string [] {
+ "H:m:s.fffffffzzz",
+ "H:m:s.fffffff",
+ "H:m:s tt zzz",
+ "H:m:szzz",
+ "H:m:s",
+ "H:mzzz",
+ "H:m",
+ "H tt", // Specifies AM to disallow '8'.
+ "H'\u6642'm'\u5206's'\u79D2'",
+ };
+
+ // DateTime.Parse date patterns extend ParseExact patterns as follows:
+ // MMM - month short name or month full name
+ // MMMM - month number or short name or month full name
+
+ // Parse behaves differently according to the ShorDatePattern of the
+ // DateTimeFormatInfo. The following define the date patterns for
+ // different orders of day, month and year in ShorDatePattern.
+ // Note that the year cannot go between the day and the month.
+ private static readonly string[] ParseYearDayMonthFormats = new string [] {
+ "yyyy/M/dT",
+ "M/yyyy/dT",
+ "yyyy'\u5E74'M'\u6708'd'\u65E5",
+
+
+ "yyyy/d/MMMM",
+ "yyyy/MMM/d",
+ "d/MMMM/yyyy",
+ "MMM/d/yyyy",
+ "d/yyyy/MMMM",
+ "MMM/yyyy/d",
+
+ "yy/d/M",
+ };
+
+ private static readonly string[] ParseYearMonthDayFormats = new string [] {
+ "yyyy/M/dT",
+ "M/yyyy/dT",
+ "yyyy'\u5E74'M'\u6708'd'\u65E5",
+
+ "yyyy/MMMM/d",
+ "yyyy/d/MMM",
+ "MMMM/d/yyyy",
+ "d/MMM/yyyy",
+ "MMMM/yyyy/d",
+ "d/yyyy/MMM",
+
+ "yy/MMMM/d",
+ "yy/d/MMM",
+ "MMM/yy/d",
+ };
+
+ private static readonly string[] ParseDayMonthYearFormats = new string [] {
+ "yyyy/M/dT",
+ "M/yyyy/dT",
+ "yyyy'\u5E74'M'\u6708'd'\u65E5",
+
+ "yyyy/MMMM/d",
+ "yyyy/d/MMM",
+ "d/MMMM/yyyy",
+ "MMM/d/yyyy",
+ "MMMM/yyyy/d",
+ "d/yyyy/MMM",
+
+ "d/MMMM/yy",
+ "yy/MMM/d",
+ "d/yy/MMM",
+ "yy/d/MMM",
+ "MMM/d/yy",
+ "MMM/yy/d",
+ };
+
+ private static readonly string[] ParseMonthDayYearFormats = new string [] {
+ "yyyy/M/dT",
+ "M/yyyy/dT",
+ "yyyy'\u5E74'M'\u6708'd'\u65E5",
+
+ "yyyy/MMMM/d",
+ "yyyy/d/MMM",
+ "MMMM/d/yyyy",
+ "d/MMM/yyyy",
+ "MMMM/yyyy/d",
+ "d/yyyy/MMM",
+
+ "MMMM/d/yy",
+ "MMM/yy/d",
+ "d/MMM/yy",
+ "yy/MMM/d",
+ "d/yy/MMM",
+ "yy/d/MMM",
+ };
+
+ // Patterns influenced by the MonthDayPattern in DateTimeFormatInfo.
+ // Note that these patterns cannot be followed by the time.
+ private static readonly string[] MonthDayShortFormats = new string [] {
+ "MMMM/d",
+ "d/MMM",
+ "yyyy/MMMM",
+ };
+ private static readonly string[] DayMonthShortFormats = new string [] {
+ "d/MMMM",
+ "MMM/yy",
+ "yyyy/MMMM",
};
private enum Which
if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
days = daysmonthleap;
-
+
while (totaldays >= days[M])
totaldays -= days[M++];
ticks, MinValue.Ticks, MaxValue.Ticks);
throw new ArgumentOutOfRangeException ("ticks", msg);
}
-#if NET_2_0
kind = DateTimeKind.Unspecified;
-#endif
}
public DateTime (int year, int month, int day)
ticks = new TimeSpan (AbsoluteDays(year,month,day), hour, minute, second, millisecond);
-#if NET_2_0
kind = DateTimeKind.Unspecified;
-#endif
}
- [MonoTODO ("Calendar is unused")]
public DateTime (int year, int month, int day, Calendar calendar)
: this (year, month, day, 0, 0, 0, 0, calendar)
{
}
- [MonoTODO ("Calendar is unused")]
public DateTime (int year, int month, int day, int hour, int minute, int second, Calendar calendar)
: this (year, month, day, hour, minute, second, 0, calendar)
{
}
- [MonoTODO ("Calendar is unused")]
public DateTime (int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
- : this (year, month, day, hour, minute, second, millisecond)
{
if (calendar == null)
throw new ArgumentNullException ("calendar");
- // FIXME: we're not using the calendar anywhere???
+ ticks = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).ticks;
+ kind = DateTimeKind.Unspecified;
}
internal DateTime (bool check, TimeSpan value)
{
if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
- throw new ArgumentOutOfRangeException ();
+ throw new ArgumentOutOfRangeException ();
ticks = value;
-#if NET_2_0
kind = DateTimeKind.Unspecified;
-#endif
}
-#if NET_2_0
public DateTime (long ticks, DateTimeKind kind) : this (ticks)
{
CheckDateTimeKind (kind);
{
CheckDateTimeKind (kind);
this.kind = kind;
- }
-#endif
+ }
+ //
+ // Not visible, but can be invoked during deserialization
+ //
+ DateTime (SerializationInfo info, StreamingContext context)
+ {
+ if (info.HasKey ("dateData")){
+ long dateData = info.GetInt64 ("dateData");
+ kind = (DateTimeKind) (dateData >> 62);
+ ticks = new TimeSpan (dateData & 0x3fffffffffffffff);
+ } else if (info.HasKey ("ticks")){
+ ticks = new TimeSpan (info.GetInt64 ("ticks"));
+ kind = DateTimeKind.Unspecified;
+ } else {
+ kind = DateTimeKind.Unspecified;
+ ticks = new TimeSpan (0);
+ }
+ }
+
+
/* Properties */
public DateTime Date
{
get
{
- return new DateTime (Year, Month, Day);
+ DateTime ret = new DateTime (Year, Month, Day);
+ ret.kind = kind;
+ return ret;
}
}
-
+
public int Month
{
get
}
}
-
public int Day
{
get
}
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern long GetTimeMonotonic ();
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern long GetNow ();
+ //
+ // To reduce the time consumed by DateTime.Now, we keep
+ // the difference to map the system time into a local
+ // time into `to_local_time_span', we record the timestamp
+ // for this in `last_now'
+ //
+ static object to_local_time_span_object;
+ static long last_now;
+
public static DateTime Now
{
get
{
- return new DateTime (GetNow ()).ToLocalTime ();
+ long now = GetNow ();
+ DateTime dt = new DateTime (now);
+
+ if ((now - last_now) > TimeSpan.TicksPerMinute){
+ to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
+ last_now = now;
+
+ }
+
+ // This is boxed, so we avoid locking.
+ DateTime ret = dt + (TimeSpan) to_local_time_span_object;
+ ret.kind = DateTimeKind.Local;
+ return ret;
}
}
get {
DateTime now = Now;
DateTime today = new DateTime (now.Year, now.Month, now.Day);
-#if NET_2_0
today.kind = now.kind;
-#endif
return today;
}
}
public static DateTime UtcNow
{
get {
-#if NET_2_0
return new DateTime (GetNow (), DateTimeKind.Utc);
-#else
- return new DateTime (GetNow ());
-#endif
}
}
}
}
-#if NET_2_0
public DateTimeKind Kind {
get {
return kind;
}
}
-#endif
/* methods */
- public DateTime Add (TimeSpan ts)
+ public DateTime Add (TimeSpan value)
{
- return AddTicks (ts.Ticks);
+ DateTime ret = AddTicks (value.Ticks);
+ ret.kind = kind;
+ return ret;
}
- public DateTime AddDays (double days)
+ public DateTime AddDays (double value)
{
- return AddMilliseconds (Math.Round (days * 86400000));
+ return AddMilliseconds (Math.Round (value * 86400000));
}
- public DateTime AddTicks (long t)
+ public DateTime AddTicks (long value)
{
- if ((t + ticks.Ticks) > MAX_VALUE_TICKS || (t + ticks.Ticks) < 0) {
+ if ((value + ticks.Ticks) > MAX_VALUE_TICKS || (value + ticks.Ticks) < 0) {
throw new ArgumentOutOfRangeException();
}
- return new DateTime (t + ticks.Ticks);
+ DateTime ret = new DateTime (value + ticks.Ticks);
+ ret.kind = kind;
+ return ret;
}
- public DateTime AddHours (double hours)
+ public DateTime AddHours (double value)
{
- return AddMilliseconds (hours * 3600000);
+ return AddMilliseconds (value * 3600000);
}
- public DateTime AddMilliseconds (double ms)
+ public DateTime AddMilliseconds (double value)
{
- if ((ms * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
- (ms * TimeSpan.TicksPerMillisecond) < long.MinValue) {
+ if ((value * TimeSpan.TicksPerMillisecond) > long.MaxValue ||
+ (value * TimeSpan.TicksPerMillisecond) < long.MinValue) {
throw new ArgumentOutOfRangeException();
}
- long msticks = (long) (ms * TimeSpan.TicksPerMillisecond);
+ long msticks = (long) Math.Round (value * TimeSpan.TicksPerMillisecond);
return AddTicks (msticks);
}
return AddTicks (msticks);
}
- public DateTime AddMinutes (double minutes)
+ public DateTime AddMinutes (double value)
{
- return AddMilliseconds (minutes * 60000);
+ return AddMilliseconds (value * 60000);
}
public DateTime AddMonths (int months)
day = maxday;
temp = new DateTime (year, month, day);
+ temp.kind = kind;
return temp.Add (this.TimeOfDay);
}
- public DateTime AddSeconds (double seconds)
+ public DateTime AddSeconds (double value)
{
- return AddMilliseconds (seconds*1000);
+ return AddMilliseconds (value * 1000);
}
- public DateTime AddYears (int years )
+ public DateTime AddYears (int value)
{
- return AddMonths(years * 12);
+ return AddMonths (value * 12);
}
public static int Compare (DateTime t1, DateTime t2)
return 0;
}
- public int CompareTo (object v)
+ public int CompareTo (object value)
{
- if ( v == null)
+ if (value == null)
return 1;
- if (!(v is System.DateTime))
+ if (!(value is System.DateTime))
throw new ArgumentException (Locale.GetText (
"Value is not a System.DateTime"));
- return Compare (this, (DateTime) v);
+ return Compare (this, (DateTime) value);
}
-#if NET_2_0
public bool IsDaylightSavingTime ()
{
if (kind == DateTimeKind.Utc)
return value.ticks == ticks;
}
+ public long ToBinary ()
+ {
+ switch (kind) {
+ case DateTimeKind.Utc:
+ return Ticks | 0x4000000000000000;
+ case DateTimeKind.Local:
+ return (long) ((ulong) ToUniversalTime ().Ticks | 0x8000000000000000);
+ default:
+ return Ticks;
+ }
+ }
+
+ public static DateTime FromBinary (long dateData)
+ {
+ switch ((ulong)dateData >> 62) {
+ case 1:
+ return new DateTime (dateData ^ 0x4000000000000000, DateTimeKind.Utc);
+ case 0:
+ return new DateTime (dateData, DateTimeKind.Unspecified);
+ default:
+ return new DateTime (dateData & 0x3fffffffffffffff, DateTimeKind.Utc).ToLocalTime ();
+ }
+ }
+
public static DateTime SpecifyKind (DateTime value, DateTimeKind kind)
{
return new DateTime (value.Ticks, kind);
}
-#endif
public static int DaysInMonth (int year, int month)
{
if (month < 1 || month >12)
throw new ArgumentOutOfRangeException ();
+ if (year < 1 || year > 9999)
+ throw new ArgumentOutOfRangeException ();
+
days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
return days[month];
}
- public override bool Equals (object o)
+ public override bool Equals (object value)
{
- if (!(o is System.DateTime))
+ if (!(value is System.DateTime))
return false;
- return ((DateTime) o).ticks == ticks;
+ return ((DateTime) value).ticks == ticks;
}
public static bool Equals (DateTime t1, DateTime t2 )
return new DateTime (w32file_epoch + fileTime).ToLocalTime ();
}
-#if NET_1_1
public static DateTime FromFileTimeUtc (long fileTime)
{
if (fileTime < 0)
return new DateTime (w32file_epoch + fileTime);
}
-#endif
public static DateTime FromOADate (double d)
{
break;
}
DateTimeFormatInfo info = (DateTimeFormatInfo) provider.GetFormat (typeof(DateTimeFormatInfo));
- return GetDateTimeFormats (adjustutc, info.GetAllDateTimePatterns (format), info);
+ return GetDateTimeFormats (adjustutc, info.GetAllRawDateTimePatterns (format), info);
}
private string [] GetDateTimeFormats (bool adjustutc, string [] patterns, DateTimeFormatInfo dfi)
string [] results = new string [patterns.Length];
DateTime val = adjustutc ? ToUniversalTime () : this;
for (int i = 0; i < results.Length; i++)
- results [i] = val._ToString (patterns [i], dfi);
+ results [i] = DateTimeUtils.ToString (val, patterns [i], dfi);
return results;
}
-#if NET_2_0
private void CheckDateTimeKind (DateTimeKind kind) {
if ((kind != DateTimeKind.Unspecified) && (kind != DateTimeKind.Utc) && (kind != DateTimeKind.Local))
throw new ArgumentException ("Invalid DateTimeKind value.", "kind");
}
-#endif
public override int GetHashCode ()
{
public static bool IsLeapYear (int year)
{
+ if (year < 1 || year > 9999)
+ throw new ArgumentOutOfRangeException ();
return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}
return Parse (s, null);
}
- public static DateTime Parse (string s, IFormatProvider fp)
+ public static DateTime Parse (string s, IFormatProvider provider)
{
- return Parse (s, fp, DateTimeStyles.AllowWhiteSpaces);
+ return Parse (s, provider, DateTimeStyles.AllowWhiteSpaces);
}
- [MonoTODO ("see the comments inline")]
- public static DateTime Parse (string s, IFormatProvider fp, DateTimeStyles styles)
+ public static DateTime Parse (string s, IFormatProvider provider, DateTimeStyles styles)
{
- // This method should try only expected patterns.
- // Should not try extra patterns.
- // Right now we also try InvariantCulture, but I
- // confirmed in some cases this method rejects what
- // InvariantCulture supports (can be checked against
- // "th-TH" with Gregorian Calendar). So basically it
- // should not be done.
- // I think it should be CurrentCulture to be tested,
- // but right now we don't support all the supported
- // patterns for each culture, so try InvariantCulture
- // as a quick remedy.
if (s == null)
- throw new ArgumentNullException (Locale.GetText ("s is null"));
- DateTime result;
+ throw new ArgumentNullException ("s");
- if (fp == null)
- fp = CultureInfo.CurrentCulture;
- DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
+ DateTime res;
+ DateTimeOffset dto;
+ Exception exception = null;
+ if (!CoreParse (s, provider, styles, out res, out dto, true, ref exception))
+ throw exception;
+
+ return res;
+ }
+
+ const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
+
+ internal static bool CoreParse (string s, IFormatProvider provider, DateTimeStyles styles,
+ out DateTime result, out DateTimeOffset dto, bool setExceptionOnError, ref Exception exception)
+ {
+ dto = new DateTimeOffset (0, TimeSpan.Zero);
+ if (s == null || s.Length == 0) {
+ if (setExceptionOnError)
+ exception = new FormatException (formatExceptionMessage);
+ result = MinValue;
+ return false;
+ }
+
+ if (provider == null)
+ provider = CultureInfo.CurrentCulture;
+ DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
+
+ // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
+ string[] allDateFormats = YearMonthDayFormats (dfi, setExceptionOnError, ref exception);
+ if (allDateFormats == null){
+ result = MinValue;
+ return false;
+ }
bool longYear = false;
+ for (int i = 0; i < allDateFormats.Length; i++) {
+ string firstPart = allDateFormats [i];
+ bool incompleteFormat = false;
+ if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return true;
- // Try all the patterns
- if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
- return result;
+ if (!incompleteFormat)
+ continue;
- // Try common formats.
-// if (ParseExact (s, commonFormats, dfi, styles, out result, false, ref longYear))
-// return result;
+ for (int j = 0; j < ParseTimeFormats.Length; j++) {
+ if (_DoParse (s, firstPart, ParseTimeFormats [j], false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return true;
+ }
+ }
- // Try common formats with invariant culture
- if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
- return result;
+ //
+ // Month day formats
+ //
+ int dayIndex = dfi.MonthDayPattern.IndexOf('d');
+ int monthIndex = dfi.MonthDayPattern.IndexOf('M');
+ if (dayIndex == -1 || monthIndex == -1){
+ result = MinValue;
+ if (setExceptionOnError)
+ exception = new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
+ return false;
+ }
+ bool is_day_before_month = dayIndex < monthIndex;
+ string[] monthDayFormats = is_day_before_month ? DayMonthShortFormats : MonthDayShortFormats;
+ for (int i = 0; i < monthDayFormats.Length; i++) {
+ bool incompleteFormat = false;
+ if (_DoParse (s, monthDayFormats[i], "", false, out result, out dto, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return true;
+ }
+
+ for (int j = 0; j < ParseTimeFormats.Length; j++) {
+ string firstPart = ParseTimeFormats [j];
+ bool incompleteFormat = false;
+ if (_DoParse (s, firstPart, "", false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
+ return true;
+ if (!incompleteFormat)
+ continue;
- if (longYear) {
- throw new ArgumentOutOfRangeException ("year",
- "Valid values are between 1 and 9999 inclusive");
+ for (int i = 0; i < monthDayFormats.Length; i++) {
+ if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
+ return true;
+ }
+ for (int i = 0; i < allDateFormats.Length; i++) {
+ string dateFormat = allDateFormats [i];
+ if (dateFormat[dateFormat.Length - 1] == 'T')
+ continue; // T formats must be before the time part
+ if (_DoParse (s, firstPart, dateFormat, false, out result, out dto, dfi, styles, false, ref incompleteFormat, ref longYear))
+ return true;
+ }
}
- throw new FormatException ("String was not recognized as a valid DateTime.");
+ // Try as a last resort all the patterns
+ if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear, setExceptionOnError, ref exception))
+ return true;
+
+ if (!setExceptionOnError)
+ return false;
+
+ // .NET 2.x does not throw an ArgumentOutOfRangeException, but .NET 1.1 does.
+ exception = new FormatException (formatExceptionMessage);
+ return false;
+ }
+
+ public static DateTime ParseExact (string s, string format, IFormatProvider provider)
+ {
+ return ParseExact (s, format, provider, DateTimeStyles.None);
}
- public static DateTime ParseExact (string s, string format, IFormatProvider fp)
+ private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi, bool setExceptionOnError, ref Exception exc)
{
- return ParseExact (s, format, fp, DateTimeStyles.None);
+ int dayIndex = dfi.ShortDatePattern.IndexOf('d');
+ int monthIndex = dfi.ShortDatePattern.IndexOf('M');
+ int yearIndex = dfi.ShortDatePattern.IndexOf('y');
+ if (dayIndex == -1 || monthIndex == -1 || yearIndex == -1){
+ if (setExceptionOnError)
+ exc = new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
+ return null;
+ }
+
+ if (yearIndex < monthIndex)
+ if (monthIndex < dayIndex)
+ return ParseYearMonthDayFormats;
+ else if (yearIndex < dayIndex)
+ return ParseYearDayMonthFormats;
+ else {
+ // The year cannot be between the date and the month
+ if (setExceptionOnError)
+ exc = new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
+ return null;
+ }
+ else if (dayIndex < monthIndex)
+ return ParseDayMonthYearFormats;
+ else if (dayIndex < yearIndex)
+ return ParseMonthDayYearFormats;
+ else {
+ // The year cannot be between the month and the date
+ if (setExceptionOnError)
+ exc = new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
+ return null;
+ }
}
- internal static int _ParseNumber (string s, int valuePos,
- int min_digits,
- int digits,
- bool leadingzero,
- bool sloppy_parsing,
- out int num_parsed)
+ private static int _ParseNumber (string s, int valuePos,
+ int min_digits,
+ int digits,
+ bool leadingzero,
+ bool sloppy_parsing,
+ out int num_parsed)
{
int number = 0, i;
return number;
}
- internal static int _ParseEnum (string s, int sPos, string[] values, out int num_parsed)
- {
- int i;
-
- for (i = 0; i < values.Length; i++) {
- if (s.Length - sPos < values[i].Length)
- continue;
- else if (values [i].Length == 0)
- continue;
- String tmp = s.Substring (sPos, values[i].Length);
- if (String.Compare (tmp, values[i], true) == 0) {
- num_parsed = values[i].Length;
+ private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
+ {
+ // FIXME: I know this is somehow lame code. Probably
+ // it should iterate all the enum value and return
+ // the longest match. However right now I don't see
+ // anything but "1" and "10" - "12" that might match
+ // two or more values. (They are only abbrev month
+ // names, so do reverse order search). See bug #80094.
+ for (int i = values.Length - 1; i >= 0; i--) {
+ if (!exact && invValues [i].Length > values[i].Length) {
+ if (invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
+ return i;
+ if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
+ return i;
+ }
+ else {
+ if (values [i].Length > 0 && _ParseString (s, sPos, 0, values [i], out num_parsed))
+ return i;
+ if (!exact && invValues [i].Length > 0 && _ParseString (s, sPos, 0, invValues [i], out num_parsed))
return i;
}
}
return -1;
}
- internal static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
+ private static bool _ParseString (string s, int sPos, int maxlength, string value, out int num_parsed)
{
if (maxlength <= 0)
maxlength = value.Length;
- if (String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
+ if (sPos + maxlength <= s.Length && String.Compare (s, sPos, value, 0, maxlength, true, CultureInfo.InvariantCulture) == 0) {
num_parsed = maxlength;
return true;
}
return false;
}
- private static bool _DoParse (string s, string format, bool exact,
- out DateTime result,
+ // Note that in case of Parse (exact == false) we check both for AM/PM
+ // and the culture spcific AM/PM strings.
+ private static bool _ParseAmPm(string s,
+ int valuePos,
+ int num,
DateTimeFormatInfo dfi,
- DateTimeStyles style,
- ref bool longYear)
+ bool exact,
+ out int num_parsed,
+ ref int ampm)
+ {
+ num_parsed = -1;
+ if (ampm != -1)
+ return false;
+
+ if (!IsLetter (s, valuePos)) {
+ if (dfi.AMDesignator != "")
+ return false;
+ if (exact)
+ ampm = 0;
+ num_parsed = 0;
+ return true;
+ }
+ DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
+ if (!exact && _ParseString (s, valuePos, num, invInfo.PMDesignator, out num_parsed) ||
+ dfi.PMDesignator != "" && _ParseString(s, valuePos, num, dfi.PMDesignator, out num_parsed))
+ ampm = 1;
+ else if (!exact && _ParseString (s, valuePos, num, invInfo.AMDesignator, out num_parsed) ||
+ _ParseString (s, valuePos, num, dfi.AMDesignator, out num_parsed)) {
+ if (exact || num_parsed != 0)
+ ampm = 0;
+ }
+ else
+ return false;
+ return true;
+ }
+
+ // Note that in case of Parse (exact == false) we check both for ':'
+ // and the culture spcific TimeSperator
+ private static bool _ParseTimeSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
{
- bool useutc = false, use_localtime = true;
+ return _ParseString (s, sPos, 0, dfi.TimeSeparator, out num_parsed) ||
+ !exact && _ParseString (s, sPos, 0, ":", out num_parsed);
+ }
+
+ // Accept any character for DateSeparator, except TimeSeparator,
+ // a digit or a letter.
+ // Not documented, but seems to be MS behaviour here. See bug 54047.
+ private static bool _ParseDateSeparator (string s, int sPos, DateTimeFormatInfo dfi, bool exact, out int num_parsed)
+ {
+ num_parsed = -1;
+ if (exact && s [sPos] != '/')
+ return false;
+
+ if (_ParseTimeSeparator (s, sPos, dfi, exact, out num_parsed) ||
+ Char.IsDigit (s [sPos]) || Char.IsLetter (s [sPos]))
+ return(false);
+
+ num_parsed = 1;
+ return true;
+ }
+
+ private static bool IsLetter (string s, int pos)
+ {
+ return pos < s.Length && Char.IsLetter (s [pos]);
+ }
+
+ // To implement better DateTime.Parse we use two format strings one
+ // for Date and one for Time. This allows us to define two different
+ // arrays of formats for Time and Dates and to combine them more or less
+ // efficiently. When this mode is used flexibleTwoPartsParsing is true.
+ private static bool _DoParse (string s,
+ string firstPart,
+ string secondPart,
+ bool exact,
+ out DateTime result,
+ out DateTimeOffset dto,
+ DateTimeFormatInfo dfi,
+ DateTimeStyles style,
+ bool firstPartIsDate,
+ ref bool incompleteFormat,
+ ref bool longYear)
+ {
+ bool useutc = false;
bool use_invariant = false;
bool sloppy_parsing = false;
+ dto = new DateTimeOffset (0, TimeSpan.Zero);
+ bool flexibleTwoPartsParsing = !exact && secondPart != null;
+ incompleteFormat = false;
int valuePos = 0;
+ string format = firstPart;
+ bool afterTFormat = false;
+ DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
if (format.Length == 1)
- format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
- else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
- useutc = true;
+ format = DateTimeUtils.GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
+
+ result = new DateTime (0);
+ if (format == null)
+ return false;
+ if (s == null)
+ return false;
+
if ((style & DateTimeStyles.AllowLeadingWhite) != 0) {
format = format.TrimStart (null);
}
if (use_invariant)
- dfi = DateTimeFormatInfo.InvariantInfo;
+ dfi = invInfo;
if ((style & DateTimeStyles.AllowInnerWhite) != 0)
sloppy_parsing = true;
string chars = format;
int len = format.Length, pos = 0, num = 0;
+ if (len == 0)
+ return false;
int day = -1, dayofweek = -1, month = -1, year = -1;
int hour = -1, minute = -1, second = -1;
double fractionalSeconds = -1;
int ampm = -1;
int tzsign = -1, tzoffset = -1, tzoffmin = -1;
+ bool isFirstPart = true;
- result = new DateTime (0);
- while (pos+num < len)
+ for (; ; )
{
- if (s.Length == valuePos)
+ if (valuePos == s.Length)
+ break;
+
+ int num_parsed = 0;
+ if (flexibleTwoPartsParsing && pos + num == 0)
+ {
+ bool isLetter = IsLetter(s, valuePos);
+ if (isLetter) {
+ if (s [valuePos] == 'Z')
+ num_parsed = 1;
+ else
+ _ParseString (s, valuePos, 0, "GMT", out num_parsed);
+ if (num_parsed > 0 && !IsLetter (s, valuePos + num_parsed)) {
+ valuePos += num_parsed;
+ useutc = true;
+ continue;
+ }
+ }
+ if (!afterTFormat && _ParseAmPm (s, valuePos, 0, dfi, exact, out num_parsed, ref ampm)) {
+ if (IsLetter (s, valuePos + num_parsed))
+ ampm = -1;
+ else if (num_parsed > 0) {
+ valuePos += num_parsed;
+ continue;
+ }
+ }
+
+ if (!afterTFormat && dayofweek == -1 && isLetter) {
+ dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
+ if (dayofweek == -1)
+ dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
+ if (dayofweek != -1 && !IsLetter (s, valuePos + num_parsed)) {
+ valuePos += num_parsed;
+ continue;
+ }
+ else
+ dayofweek = -1;
+ }
+
+ if (char.IsWhiteSpace (s [valuePos]) || s [valuePos] == ',') {
+ valuePos += 1;
+ continue;
+ }
+ num_parsed = 0;
+ }
+
+ if (pos + num >= len)
+ {
+ if (flexibleTwoPartsParsing && num == 0) {
+ afterTFormat = isFirstPart && firstPart [firstPart.Length - 1] == 'T';
+ if (!isFirstPart && format == "")
+ break;
+
+ pos = 0;
+ if (isFirstPart)
+ format = secondPart;
+ else
+ format = "";
+ chars = format;
+ len = chars.Length;
+ isFirstPart = false;
+ continue;
+ }
break;
+ }
+
+ bool leading_zeros = true;
if (chars[pos] == '\'') {
num = 1;
if (chars[pos+num] == '\'')
break;
- if (valuePos == s.Length)
+ if (valuePos == s.Length || s [valuePos] != chars [pos + num])
return false;
- if (s [valuePos] != chars [pos + num])
- return false;
- valuePos++;
+ valuePos++;
num++;
}
- if (pos+num > len)
- return false;
pos += num + 1;
num = 0;
if (chars[pos+num] == '"')
break;
- if (valuePos == s.Length)
+ if (valuePos == s.Length || s [valuePos] != chars[pos+num])
return false;
- if (s [valuePos] != chars[pos+num])
- return false;
- valuePos++;
+ valuePos++;
num++;
}
- if (pos+num > len)
- return false;
pos += num + 1;
num = 0;
num = 0;
if (pos >= len)
return false;
-
if (s [valuePos] != chars [pos])
return false;
+
valuePos++;
pos++;
continue;
} else if (chars[pos] == '%') {
pos++;
continue;
- } else if (Char.IsWhiteSpace (s [valuePos]) ||
- s [valuePos] == ',' && Char.IsWhiteSpace (chars [pos])) {
+ } else if (char.IsWhiteSpace (s [valuePos]) ||
+ s [valuePos] == ',' && (!exact && chars [pos] == '/' || Char.IsWhiteSpace (chars [pos]))) {
valuePos++;
num = 0;
if (exact && (style & DateTimeStyles.AllowInnerWhite) == 0) {
break;
}
pos = ws;
+ // A whitespace may match a '/' in the pattern.
+ if (!exact && pos < chars.Length && chars[pos] == '/')
+ if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
+ pos++;
continue;
}
continue;
}
-
- int num_parsed = 0;
-
switch (chars[pos])
{
case 'd':
- if (day != -1)
+ if (num < 2 && day != -1 || num >= 2 && dayofweek != -1)
return false;
if (num == 0)
- day = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
+ day = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
- day = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
+ day = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
else if (num == 2)
- dayofweek = _ParseEnum (s, valuePos, dfi.AbbreviatedDayNames, out num_parsed);
+ dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
else
- {
- dayofweek = _ParseEnum (s, valuePos, dfi.DayNames, out num_parsed);
- num = 3;
- }
+ dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, invInfo.RawDayNames, exact, out num_parsed);
break;
case 'M':
if (month != -1)
return false;
+
+ if (flexibleTwoPartsParsing) {
+ num_parsed = -1;
+ if (num == 0 || num == 3)
+ month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
+ if (num > 1 && num_parsed == -1)
+ month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
+ if (num > 1 && num_parsed == -1)
+ month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
+ break;
+ }
+
if (num == 0)
- month = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
+ month = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
- month = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
+ month = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
else if (num == 2)
- month = _ParseEnum (s, valuePos, dfi.AbbreviatedMonthNames , out num_parsed) + 1;
+ month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
else
- {
- month = _ParseEnum (s, valuePos, dfi.MonthNames, out num_parsed) + 1;
- num = 3;
- }
+ month = _ParseEnum (s, valuePos, dfi.RawMonthNames, invInfo.RawMonthNames, exact, out num_parsed) + 1;
break;
case 'y':
if (year != -1)
return false;
if (num == 0) {
- year = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
+ year = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
} else if (num < 3) {
- year = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
+ year = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
} else {
- year = _ParseNumber (s, valuePos,4, 4, false, sloppy_parsing, out num_parsed);
+ year = _ParseNumber (s, valuePos, exact ? 4 : 3, 4, false, sloppy_parsing, out num_parsed);
if ((year >= 1000) && (num_parsed == 4) && (!longYear) && (s.Length > 4 + valuePos)) {
int np = 0;
int ly = _ParseNumber (s, valuePos, 5, 5, false, sloppy_parsing, out np);
if (hour != -1)
return false;
if (num == 0)
- hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
+ hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
- {
- hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
- num = 1;
- }
+ hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
if (hour > 12)
return false;
break;
case 'H':
- if ((hour != -1) || (ampm >= 0))
+ if (hour != -1 || !flexibleTwoPartsParsing && ampm >= 0)
return false;
if (num == 0)
- hour = _ParseNumber (s, valuePos,0, 2, false, sloppy_parsing, out num_parsed);
+ hour = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
- {
- hour = _ParseNumber (s, valuePos,0, 2, true, sloppy_parsing, out num_parsed);
- num = 1;
- }
+ hour = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+
if (hour >= 24)
return false;
if (minute != -1)
return false;
if (num == 0)
- minute = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
+ minute = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
- {
- minute = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
- num = 1;
- }
+ minute = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+
if (minute >= 60)
return false;
if (second != -1)
return false;
if (num == 0)
- second = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
+ second = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else
- {
- second = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
- num = 1;
- }
+ second = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+
if (second >= 60)
return false;
break;
+ case 'F':
+ leading_zeros = false;
+ goto case 'f';
case 'f':
- if (fractionalSeconds != -1)
+ if (num > 6 || fractionalSeconds != -1)
return false;
- num = Math.Min (num, 6);
- double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, true, sloppy_parsing, out num_parsed);
+ double decimalNumber = (double) _ParseNumber (s, valuePos, 0, num+1, leading_zeros, sloppy_parsing, out num_parsed);
if (num_parsed == -1)
return false;
-
- else
- fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
+ fractionalSeconds = decimalNumber / Math.Pow(10.0, num_parsed);
break;
case 't':
- if (ampm != -1)
- return false;
- if (num == 0)
- {
- if (_ParseString (s, valuePos, 1, dfi.AMDesignator, out num_parsed))
- ampm = 0;
- else if (_ParseString (s, valuePos, 1, dfi.PMDesignator, out num_parsed))
- ampm = 1;
- else
+ if (!_ParseAmPm (s, valuePos, num > 0 ? 0 : 1, dfi, exact, out num_parsed, ref ampm))
return false;
- }
- else
- {
- if (_ParseString (s, valuePos, 0, dfi.AMDesignator, out num_parsed))
- ampm = 0;
- else if (_ParseString (s, valuePos, 0, dfi.PMDesignator, out num_parsed))
- ampm = 1;
- else
- return false;
- num = 1;
- }
break;
case 'z':
if (tzsign != -1)
return false;
+
if (s [valuePos] == '+')
tzsign = 0;
else if (s [valuePos] == '-')
else
return false;
valuePos++;
+
if (num == 0)
- tzoffset = _ParseNumber (s, valuePos, 0, 2, false, sloppy_parsing, out num_parsed);
+ tzoffset = _ParseNumber (s, valuePos, 1, 2, false, sloppy_parsing, out num_parsed);
else if (num == 1)
- tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
- else
- {
- tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
+ tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+ else {
+ tzoffset = _ParseNumber (s, valuePos, 1, 2, true, /*sloppy_parsing*/true, out num_parsed);
+ valuePos += num_parsed;
if (num_parsed < 0)
return false;
+
+ num_parsed = 0;
+ if (valuePos < s.Length && Char.IsDigit (s [valuePos]) ||
+ _ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed)) {
+ valuePos += num_parsed;
+ tzoffmin = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+ if (num_parsed < 0)
+ return false;
+ }
+ else if (!flexibleTwoPartsParsing)
+ return false;
+ else
+ num_parsed = 0;
+ }
+ break;
+ case 'K':
+ if (s [valuePos] == 'Z') {
+ valuePos++;
+ useutc = true;
+ }
+ else if (s [valuePos] == '+' || s [valuePos] == '-') {
+ if (tzsign != -1)
+ return false;
+ if (s [valuePos] == '+')
+ tzsign = 0;
+ else if (s [valuePos] == '-')
+ tzsign = 1;
+ valuePos++;
+
+ // zzz
+ tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
valuePos += num_parsed;
+ if (num_parsed < 0)
+ return false;
+
if (Char.IsDigit (s [valuePos]))
num_parsed = 0;
else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
return false;
valuePos += num_parsed;
+
tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
+ num = 2;
if (num_parsed < 0)
return false;
- num = 2;
}
break;
num_parsed = 1;
useutc = true;
break;
+ case 'G':
+ if (s [valuePos] != 'G')
+ return false;
+ if ((pos + 2 < len) && (valuePos + 2 < s.Length) &&
+ (chars [pos + 1] == 'M') && (s[valuePos + 1] == 'M') &&
+ (chars [pos + 2] == 'T') && (s[valuePos + 2] == 'T'))
+ {
+ useutc = true;
+ num = 2;
+ num_parsed = 3;
+ }
+ else {
+ num = 0;
+ num_parsed = 1;
+ }
+ break;
case ':':
- if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
+ if (!_ParseTimeSeparator (s, valuePos, dfi, exact, out num_parsed))
return false;
break;
case '/':
- /* Accept any character for
- * DateSeparator, except
- * TimeSeparator, a digit or a
- * letter. Not documented,
- * but seems to be MS
- * behaviour here. See bug
- * 54047.
- */
- if (exact && s [valuePos] != '/')
+ if (!_ParseDateSeparator (s, valuePos, dfi, exact, out num_parsed))
return false;
- if (_ParseString (s, valuePos, 0,
- dfi.TimeSeparator,
- out num_parsed) ||
- Char.IsDigit (s [valuePos]) ||
- Char.IsLetter (s [valuePos])) {
- return(false);
- }
-
num = 0;
- if (num_parsed <= 0) {
- num_parsed = 1;
- }
-
break;
default:
- if (s [valuePos] != chars[pos]) {
- // FIXME: It is not sure, but
- // IsLetter() is introduced
- // because we have to reject
- // "2002a02b25" but have to
- // allow "2002$02$25". The same
- // thing applies to '/' case.
- if (exact ||
- Char.IsDigit (s [valuePos]) ||
- Char.IsLetter (s [valuePos]))
+ if (s [valuePos] != chars [pos])
return false;
- }
+
num = 0;
num_parsed = 1;
break;
valuePos += num_parsed;
- if (!exact) {
+ if (!exact && !flexibleTwoPartsParsing) {
switch (chars [pos]) {
case 'm':
case 's':
+ case 'F':
case 'f':
case 'z':
- if (s.Length > valuePos && s [valuePos] == 'Z'
- && (pos + 1 == chars.Length
- || chars [pos + 1] != 'Z')) {
+ if (s.Length > valuePos && s [valuePos] == 'Z' &&
+ (pos + 1 == chars.Length || chars [pos + 1] != 'Z')) {
useutc = true;
valuePos++;
}
num = 0;
}
- // possible empty value. Regarded as no match.
- if (pos == 0)
- return false;
+ if (pos + 1 < len && chars [pos] == '.' && chars [pos + 1] == 'F') {
+ pos++;
+ while (pos < len && chars [pos] == 'F') // '.FFF....' can be mapped to nothing. See bug #444103
+ pos++;
+ }
+ while (pos < len && chars [pos] == 'K') // 'K' can be mapped to nothing
+ pos++;
if (pos < len)
return false;
- if (s.Length != valuePos) // extraneous tail.
+ if (s.Length > valuePos) // extraneous tail.
+ {
+ if (valuePos == 0)
+ return false;
+
+ if (Char.IsDigit (s [valuePos]) && Char.IsDigit (s [valuePos - 1]))
+ return false;
+ if (Char.IsLetter (s [valuePos]) && Char.IsLetter (s [valuePos - 1]))
+ return false;
+ incompleteFormat = true;
return false;
+ }
if (hour == -1)
hour = 0;
month = 1;
year = 1;
} else {
- day = Today.Day;
- month = Today.Month;
- year = Today.Year;
+ day = DateTime.Today.Day;
+ month = DateTime.Today.Month;
+ year = DateTime.Today.Year;
}
}
-
if (day == -1)
day = 1;
if (month == -1)
if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
year = 1;
else
- year = Today.Year;
+ year = DateTime.Today.Year;
}
- if (ampm == 1)
+ if (ampm == 0 && hour == 12)
+ hour = 0;
+
+ if (ampm == 1 && (!flexibleTwoPartsParsing || hour < 12))
hour = hour + 12;
// For anything out of range
// return false
- if ( year < 1 || year > 9999 ||
- month < 1 || month >12 ||
- day < 1 || day > DaysInMonth(year, month) ||
- hour < 0 || hour > 23 ||
- minute < 0 || minute > 59 ||
- second < 0 || second > 59 )
+ if (year < 1 || year > 9999 ||
+ month < 1 || month >12 ||
+ day < 1 || day > DateTime.DaysInMonth(year, month) ||
+ hour < 0 || hour > 23 ||
+ minute < 0 || minute > 59 ||
+ second < 0 || second > 59)
return false;
result = new DateTime (year, month, day, hour, minute, second, 0);
result = result.AddSeconds(fractionalSeconds);
- if ((dayofweek != -1) && (dayofweek != (int) result.DayOfWeek))
- throw new FormatException (Locale.GetText ("String was not recognized as valid DateTime because the day of week was incorrect."));
-
- // If no timezone was specified, default to the local timezone.
- TimeSpan utcoffset;
+ if (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
+ return false;
- if (useutc) {
- if ((style & DateTimeStyles.AdjustToUniversal) != 0)
- use_localtime = false;
- utcoffset = new TimeSpan (0, 0, 0);
- } else if (tzsign == -1) {
- TimeZone tz = TimeZone.CurrentTimeZone;
- utcoffset = tz.GetUtcOffset (result);
+ if (tzsign == -1) {
+ if (result != DateTime.MinValue) {
+ try {
+ dto = new DateTimeOffset (result);
+ } catch { } // We handle this error in DateTimeOffset.Parse
+ }
} else {
- if ((style & DateTimeStyles.AdjustToUniversal) != 0)
- use_localtime = false;
-
if (tzoffmin == -1)
tzoffmin = 0;
if (tzoffset == -1)
tzoffset = 0;
- if (tzsign == 1)
- tzoffset = -tzoffset
-;
- utcoffset = new TimeSpan (tzoffset, tzoffmin, 0);
+ if (tzsign == 1) {
+ tzoffset = -tzoffset;
+ tzoffmin = -tzoffmin;
+ }
+ try {
+ dto = new DateTimeOffset (result, new TimeSpan (tzoffset, tzoffmin, 0));
+ } catch {} // We handle this error in DateTimeOffset.Parse
+ }
+ bool adjustToUniversal = (style & DateTimeStyles.AdjustToUniversal) != 0;
+
+ if (tzsign != -1) {
+ long newticks = (result.ticks - dto.Offset).Ticks;
+ if (newticks < 0)
+ newticks += TimeSpan.TicksPerDay;
+ result = new DateTime (false, new TimeSpan (newticks));
+ result.kind = DateTimeKind.Utc;
+ if ((style & DateTimeStyles.RoundtripKind) != 0)
+ result = result.ToLocalTime ();
+ }
+ else if (useutc || ((style & DateTimeStyles.AssumeUniversal) != 0))
+ result.kind = DateTimeKind.Utc;
+ else if ((style & DateTimeStyles.AssumeLocal) != 0)
+ result.kind = DateTimeKind.Local;
+
+ bool adjustToLocal = !adjustToUniversal && (style & DateTimeStyles.RoundtripKind) == 0;
+ if (result.kind != DateTimeKind.Unspecified)
+ {
+ if (adjustToUniversal)
+ result = result.ToUniversalTime ();
+ else if (adjustToLocal)
+ result = result.ToLocalTime ();
}
-
- long newticks = (result.ticks - utcoffset).Ticks;
-
- result = new DateTime (false, new TimeSpan (newticks));
- if (use_localtime)
- result = result.ToLocalTime ();
-
return true;
}
-
public static DateTime ParseExact (string s, string format,
- IFormatProvider fp, DateTimeStyles style)
+ IFormatProvider provider, DateTimeStyles style)
{
- string[] formats;
+ if (format == null)
+ throw new ArgumentNullException ("format");
- formats = new string [1];
+ string [] formats = new string [1];
formats[0] = format;
- return ParseExact (s, formats, fp, style);
+ return ParseExact (s, formats, provider, style);
}
public static DateTime ParseExact (string s, string[] formats,
- IFormatProvider fp,
+ IFormatProvider provider,
DateTimeStyles style)
{
- DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
-
+ DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
+ CheckStyle (style);
if (s == null)
- throw new ArgumentNullException (Locale.GetText ("s is null"));
- if (formats == null || formats.Length == 0)
- throw new ArgumentNullException (Locale.GetText ("format is null"));
+ throw new ArgumentNullException ("s");
+ if (formats == null)
+ throw new ArgumentNullException ("formats");
+ if (formats.Length == 0)
+ throw new FormatException ("Format specifier was invalid.");
DateTime result;
bool longYear = false;
- if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear))
- throw new FormatException ();
+ Exception e = null;
+ if (!ParseExact (s, formats, dfi, style, out result, true, ref longYear, true, ref e))
+ throw e;
return result;
+ }
+
+ private static void CheckStyle (DateTimeStyles style)
+ {
+ if ( (style & DateTimeStyles.RoundtripKind) != 0)
+ {
+ if ((style & DateTimeStyles.AdjustToUniversal) != 0 || (style & DateTimeStyles.AssumeLocal) != 0 ||
+ (style & DateTimeStyles.AssumeUniversal) != 0)
+ throw new ArgumentException ("The DateTimeStyles value RoundtripKind cannot be used with the values AssumeLocal, Asersal or AdjustToUniversal.", "style");
+ }
+ if ((style & DateTimeStyles.AssumeUniversal) != 0 && (style & DateTimeStyles.AssumeLocal) != 0)
+ throw new ArgumentException ("The DateTimeStyles values AssumeLocal and AssumeUniversal cannot be used together.", "style");
}
-#if NET_2_0
public static bool TryParse (string s, out DateTime result)
{
- try {
- result = Parse (s);
- } catch {
- result = MinValue;
- return false;
+ if (s != null){
+ try {
+ Exception exception = null;
+ DateTimeOffset dto;
+
+ return CoreParse (s, null, DateTimeStyles.AllowWhiteSpaces, out result, out dto, false, ref exception);
+ } catch { }
}
- return true;
+ result = MinValue;
+ return false;
}
public static bool TryParse (string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
{
- try {
- result = Parse (s, provider, styles);
- } catch {
- result = MinValue;
- return false;
- }
- return true;
+ if (s != null){
+ try {
+ Exception exception = null;
+ DateTimeOffset dto;
+
+ return CoreParse (s, provider, styles, out result, out dto, false, ref exception);
+ } catch {}
+ }
+ result = MinValue;
+ return false;
}
public static bool TryParseExact (string s, string format,
- IFormatProvider fp,
+ IFormatProvider provider,
DateTimeStyles style,
out DateTime result)
{
string[] formats;
-
formats = new string [1];
formats[0] = format;
- return TryParseExact (s, formats, fp, style, out result);
+ return TryParseExact (s, formats, provider, style, out result);
}
public static bool TryParseExact (string s, string[] formats,
- IFormatProvider fp,
+ IFormatProvider provider,
DateTimeStyles style,
out DateTime result)
{
- DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
+ try {
+ DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
- bool longYear = false;
- return ParseExact (s, formats, dfi, style, out result, true, ref longYear);
+ bool longYear = false;
+ Exception e = null;
+ return ParseExact (s, formats, dfi, style, out result, true, ref longYear, false, ref e);
+ } catch {
+ result = MinValue;
+ return false;
+ }
}
-#endif
private static bool ParseExact (string s, string [] formats,
- DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
- bool exact, ref bool longYear)
+ DateTimeFormatInfo dfi, DateTimeStyles style, out DateTime ret,
+ bool exact, ref bool longYear,
+ bool setExceptionOnError, ref Exception exception)
{
int i;
+ bool incompleteFormat = false;
for (i = 0; i < formats.Length; i++)
{
DateTime result;
+ string format = formats[i];
+ if (format == null || format == String.Empty)
+ break;
- if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
+ DateTimeOffset dto;
+ if (_DoParse (s, formats[i], null, exact, out result, out dto, dfi, style, false, ref incompleteFormat, ref longYear)) {
ret = result;
return true;
}
}
+
+ if (setExceptionOnError)
+ exception = new FormatException ("Invalid format string");
ret = DateTime.MinValue;
return false;
}
- public TimeSpan Subtract(DateTime dt)
- {
- return new TimeSpan(ticks.Ticks) - dt.ticks;
+ public TimeSpan Subtract (DateTime value)
+ {
+ return new TimeSpan (ticks.Ticks) - value.ticks;
}
- public DateTime Subtract(TimeSpan ts)
+ public DateTime Subtract(TimeSpan value)
{
TimeSpan newticks;
- newticks = (new TimeSpan (ticks.Ticks)) - ts;
- return new DateTime(true,newticks);
+ newticks = (new TimeSpan (ticks.Ticks)) - value;
+ DateTime ret = new DateTime (true,newticks);
+ ret.kind = kind;
+ return ret;
}
public long ToFileTime()
return(universalTime.Ticks - w32file_epoch);
}
-#if NET_1_1
public long ToFileTimeUtc()
{
if (Ticks < w32file_epoch) {
return (Ticks - w32file_epoch);
}
-#endif
public string ToLongDateString()
{
return ToString ("G", null);
}
- public string ToString (IFormatProvider fp)
+ public string ToString (IFormatProvider provider)
{
- return ToString (null, fp);
+ return ToString (null, provider);
}
public string ToString (string format)
{
return ToString (format, null);
}
-
- internal static string _GetStandardPattern (char format, DateTimeFormatInfo dfi, out bool useutc, out bool use_invariant)
- {
- String pattern;
-
- useutc = false;
- use_invariant = false;
-
- switch (format)
- {
- case 'd':
- pattern = dfi.ShortDatePattern;
- break;
- case 'D':
- pattern = dfi.LongDatePattern;
- break;
- case 'f':
- pattern = dfi.LongDatePattern + " " + dfi.ShortTimePattern;
- break;
- case 'F':
- pattern = dfi.FullDateTimePattern;
- break;
- case 'g':
- pattern = dfi.ShortDatePattern + " " + dfi.ShortTimePattern;
- break;
- case 'G':
- pattern = dfi.ShortDatePattern + " " + dfi.LongTimePattern;
- break;
- case 'm':
- case 'M':
- pattern = dfi.MonthDayPattern;
- break;
- case 'r':
- case 'R':
- pattern = dfi.RFC1123Pattern;
- // commented by LP 09/jun/2002, rfc 1123 pattern is always in GMT
- // uncommented by AE 27/may/2004
-// useutc = true;
- use_invariant = true;
- break;
- case 's':
- pattern = dfi.SortableDateTimePattern;
- break;
- case 't':
- pattern = dfi.ShortTimePattern;
- break;
- case 'T':
- pattern = dfi.LongTimePattern;
- break;
- case 'u':
- pattern = dfi.UniversalSortableDateTimePattern;
- useutc = true;
- break;
- case 'U':
-// pattern = dfi.LongDatePattern + " " + dfi.LongTimePattern;
- pattern = dfi.FullDateTimePattern;
- useutc = true;
- break;
- case 'y':
- case 'Y':
- pattern = dfi.YearMonthPattern;
- break;
- default:
- pattern = null;
- break;
- }
-
- return pattern;
- }
-
- internal string _ToString (string format, DateTimeFormatInfo dfi)
- {
- // the length of the format is usually a good guess of the number
- // of chars in the result. Might save us a few bytes sometimes
- // Add + 10 for cases like mmmm dddd
- StringBuilder result = new StringBuilder (format.Length + 10);
-
- // For some cases, the output should not use culture dependent calendar
- DateTimeFormatInfo inv = DateTimeFormatInfo.InvariantInfo;
- if (format == inv.RFC1123Pattern)
- dfi = inv;
- else if (format == inv.UniversalSortableDateTimePattern)
- dfi = inv;
-
- int i = 0;
-
- while (i < format.Length) {
- int tokLen;
- char ch = format [i];
-
- switch (ch) {
-
- //
- // Time Formats
- //
- case 'h':
- // hour, [1, 12]
- tokLen = CountRepeat (format, i, ch);
-
- int hr = this.Hour % 12;
- if (hr == 0)
- hr = 12;
-
- ZeroPad (result, hr, tokLen == 1 ? 1 : 2);
- break;
- case 'H':
- // hour, [0, 23]
- tokLen = CountRepeat (format, i, ch);
- ZeroPad (result, this.Hour, tokLen == 1 ? 1 : 2);
- break;
- case 'm':
- // minute, [0, 59]
- tokLen = CountRepeat (format, i, ch);
- ZeroPad (result, this.Minute, tokLen == 1 ? 1 : 2);
- break;
- case 's':
- // second [0, 29]
- tokLen = CountRepeat (format, i, ch);
- ZeroPad (result, this.Second, tokLen == 1 ? 1 : 2);
- break;
- case 'f':
- // fraction of second, to same number of
- // digits as there are f's
-
- tokLen = CountRepeat (format, i, ch);
- if (tokLen > 7)
- throw new FormatException ("Invalid Format String");
-
- int dec = (int)((long)(this.Ticks % TimeSpan.TicksPerSecond) / (long) Math.Pow (10, 7 - tokLen));
- ZeroPad (result, dec, tokLen);
-
- break;
- case 't':
- // AM/PM. t == first char, tt+ == full
- tokLen = CountRepeat (format, i, ch);
- string desig = this.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator;
-
- if (tokLen == 1) {
- if (desig.Length >= 1)
- result.Append (desig [0]);
- }
- else
- result.Append (desig);
-
- break;
- case 'z':
- // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm
- tokLen = CountRepeat (format, i, ch);
- TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset (this);
-
- if (offset.Ticks >= 0)
- result.Append ('+');
- else
- result.Append ('-');
-
- switch (tokLen) {
- case 1:
- result.Append (Math.Abs (offset.Hours));
- break;
- case 2:
- result.Append (Math.Abs (offset.Hours).ToString ("00"));
- break;
- default:
- result.Append (Math.Abs (offset.Hours).ToString ("00"));
- result.Append (':');
- result.Append (Math.Abs (offset.Minutes).ToString ("00"));
- break;
- }
- break;
- //
- // Date tokens
- //
- case 'd':
- // day. d(d?) = day of month (leading 0 if two d's)
- // ddd = three leter day of week
- // dddd+ full day-of-week
- tokLen = CountRepeat (format, i, ch);
-
- if (tokLen <= 2)
- ZeroPad (result, dfi.Calendar.GetDayOfMonth (this), tokLen == 1 ? 1 : 2);
- else if (tokLen == 3)
- result.Append (dfi.GetAbbreviatedDayName (dfi.Calendar.GetDayOfWeek (this)));
- else
- result.Append (dfi.GetDayName (dfi.Calendar.GetDayOfWeek (this)));
-
- break;
- case 'M':
- // Month.m(m?) = month # (with leading 0 if two mm)
- // mmm = 3 letter name
- // mmmm+ = full name
- tokLen = CountRepeat (format, i, ch);
- int month = dfi.Calendar.GetMonth(this);
- if (tokLen <= 2)
- ZeroPad (result, month, tokLen);
- else if (tokLen == 3)
- result.Append (dfi.GetAbbreviatedMonthName (month));
- else
- result.Append (dfi.GetMonthName (month));
-
- break;
- case 'y':
- // Year. y(y?) = two digit year, with leading 0 if yy
- // yyy+ full year, if yyy and yr < 1000, displayed as three digits
- tokLen = CountRepeat (format, i, ch);
-
- if (tokLen <= 2)
- ZeroPad (result, dfi.Calendar.GetYear (this) % 100, tokLen);
- else
- ZeroPad (result, dfi.Calendar.GetYear (this), (tokLen == 3 ? 3 : 4));
-
- break;
- case 'g':
- // Era name
- tokLen = CountRepeat (format, i, ch);
- result.Append (dfi.GetEraName (dfi.Calendar.GetEra (this)));
- break;
-
- //
- // Other
- //
- case ':':
- result.Append (dfi.TimeSeparator);
- tokLen = 1;
- break;
- case '/':
- result.Append (dfi.DateSeparator);
- tokLen = 1;
- break;
- case '\'': case '"':
- tokLen = ParseQuotedString (format, i, result);
- break;
- case '%':
- if (i >= format.Length - 1)
- throw new FormatException ("% at end of date time string");
- if (format [i + 1] == '%')
- throw new FormatException ("%% in date string");
-
- // Look for the next char
- tokLen = 1;
- break;
- case '\\':
- // C-Style escape
- if (i >= format.Length - 1)
- throw new FormatException ("\\ at end of date time string");
-
- result.Append (format [i + 1]);
- tokLen = 2;
-
- break;
- default:
- // catch all
- result.Append (ch);
- tokLen = 1;
- break;
- }
- i += tokLen;
- }
- return result.ToString ();
- }
-
- static int CountRepeat (string fmt, int p, char c)
- {
- int l = fmt.Length;
- int i = p + 1;
- while ((i < l) && (fmt [i] == c))
- i++;
-
- return i - p;
- }
-
- static int ParseQuotedString (string fmt, int pos, StringBuilder output)
- {
- // pos == position of " or '
-
- int len = fmt.Length;
- int start = pos;
- char quoteChar = fmt [pos++];
-
- while (pos < len) {
- char ch = fmt [pos++];
-
- if (ch == quoteChar)
- return pos - start;
-
- if (ch == '\\') {
- // C-Style escape
- if (pos >= len)
- throw new FormatException("Un-ended quote");
- output.Append (fmt [pos++]);
- } else {
- output.Append (ch);
- }
- }
-
- throw new FormatException("Un-ended quote");
- }
-
- static unsafe void ZeroPad (StringBuilder output, int digits, int len)
- {
- // more than enough for an int
- char* buffer = stackalloc char [16];
- int pos = 16;
-
- do {
- buffer [-- pos] = (char) ('0' + digits % 10);
- digits /= 10;
- len --;
- } while (digits > 0);
-
- while (len -- > 0)
- buffer [-- pos] = '0';
-
- output.Append (new string (buffer, pos, 16 - pos));
- }
-
- public string ToString (string format, IFormatProvider fp)
-
+ public string ToString (string format, IFormatProvider provider)
{
- DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance(fp);
+ DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (provider);
- if (format == null)
+ if (format == null || format == String.Empty)
format = "G";
bool useutc = false, use_invariant = false;
if (format.Length == 1) {
char fchar = format [0];
- format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
+ format = DateTimeUtils.GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
+ if (fchar == 'U')
+ return DateTimeUtils.ToString (ToUniversalTime (), format, dfi);
+// return ToUniversalTime()._ToString (format, dfi);
+
+ if (format == null)
+ throw new FormatException ("format is not one of the format specifier characters defined for DateTimeFormatInfo");
}
// Don't convert UTC value. It just adds 'Z' for
// 'u' format, for the same ticks.
- return this._ToString (format, dfi);
+ return DateTimeUtils.ToString (this, format, dfi);
}
public DateTime ToLocalTime ()
public static DateTime operator +(DateTime d, TimeSpan t)
{
- return new DateTime (true, d.ticks + t);
+ DateTime ret = new DateTime (true, d.ticks + t);
+ ret.kind = d.kind;
+ return ret;
}
public static bool operator ==(DateTime d1, DateTime d2)
public static DateTime operator -(DateTime d,TimeSpan t)
{
- return new DateTime (true, d.ticks - t);
+ DateTime ret = new DateTime (true, d.ticks - t);
+ ret.kind = d.kind;
+ return ret;
}
bool IConvertible.ToBoolean(IFormatProvider provider)
throw new InvalidCastException();
}
- object IConvertible.ToType (Type conversionType, IFormatProvider provider)
+ object IConvertible.ToType (Type targetType, IFormatProvider provider)
{
- if (conversionType == null)
- throw new ArgumentNullException ("conversionType");
+ if (targetType == null)
+ throw new ArgumentNullException ("targetType");
- if (conversionType == typeof (DateTime))
+ if (targetType == typeof (DateTime))
return this;
- else if (conversionType == typeof (String))
+ else if (targetType == typeof (String))
return this.ToString (provider);
- else if (conversionType == typeof (Object))
+ else if (targetType == typeof (Object))
return this;
else
throw new InvalidCastException();
{
throw new InvalidCastException();
}
-
+
UInt32 IConvertible.ToUInt32(IFormatProvider provider)
{
throw new InvalidCastException();
{
throw new InvalidCastException();
}
+
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ long t = ticks.Ticks;
+ info.AddValue ("ticks", t);
+
+ // This is the new .NET format, encodes the kind on the top bits
+ info.AddValue ("dateData", t | (((uint)kind) << 62));
+ }
+
}
}