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",
+ // 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: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",
+
+#if NET_2_0
+ "yyyy/d/MMMM",
+ "yyyy/MMM/d",
+#else
+ "yyyy/MMMM/d",
+ "yyyy/d/MMM",
#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'",
-
- // This one is parsed for all cultures
- "HH':'mm tt MM/dd/yyyy",
-
-/*
- // 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"
-*/
+ "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",
+#if NET_2_0
+ "MMM/yy",
+#else // In .Net 1.0 Feb 03 is always Feb 3rd (and not Feb 2003)
+ "MMM/d",
+#endif
+ "yyyy/MMMM",
};
private enum Which
if ((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
days = daysmonthleap;
-
+
while (totaldays >= days[M])
totaldays -= days[M++];
#endif
}
- [MonoTODO ("The Calendar is not taken into consideration")]
public DateTime (int year, int month, int day, Calendar calendar)
: this (year, month, day, 0, 0, 0, 0, calendar)
{
}
- [MonoTODO ("The Calendar is not taken into consideration")]
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 ("The Calendar is not taken into consideration")]
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");
+ ticks = calendar.ToDateTime (year, month, day, hour, minute, second, millisecond).ticks;
+#if NET_2_0
+ kind = DateTimeKind.Unspecified;
+#endif
}
internal DateTime (bool check, TimeSpan value)
{
if (check && (value.Ticks < MinValue.Ticks || value.Ticks > MaxValue.Ticks))
- throw new ArgumentOutOfRangeException ();
+ throw new ArgumentOutOfRangeException ();
ticks = value;
return ret;
}
}
-
+
public int Month
{
get
}
}
-
public int Day
{
get
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 static bool IsLeapYear (int year)
{
+ if (year < 1 || year > 9999)
+ throw new ArgumentOutOfRangeException ();
return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}
public static DateTime Parse (string s, IFormatProvider fp, 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.
const string formatExceptionMessage = "String was not recognized as a valid DateTime.";
const string argumentYearRangeExceptionMessage = "Valid values are between 1 and 9999, inclusive.";
if (s == null)
throw new ArgumentNullException (Locale.GetText ("s is null"));
- DateTime result;
-
if (fp == null)
fp = CultureInfo.CurrentCulture;
DateTimeFormatInfo dfi = DateTimeFormatInfo.GetInstance (fp);
bool longYear = false;
+ DateTime result;
+ // Try first all the combinations of ParseAllDateFormats & ParseTimeFormats
+ string[] allDateFormats = YearMonthDayFormats (dfi);
+ for (int i = 0; i < allDateFormats.Length; i++) {
+ string firstPart = allDateFormats [i];
+ bool incompleteFormat = false;
+ if (_DoParse (s, firstPart, "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return result;
+ if (!incompleteFormat)
+ continue;
- // Try all the patterns
- if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), 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, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return result;
+ }
+ }
+ string[] monthDayFormats = IsDayBeforeMonth (dfi) ? DayMonthShortFormats : MonthDayShortFormats;
+ for (int i = 0; i < monthDayFormats.Length; i++) {
+ bool incompleteFormat = false;
+ if (_DoParse (s, monthDayFormats[i], "", false, out result, dfi, styles, true, ref incompleteFormat, ref longYear))
+ return result;
+ }
+ for (int j = 0; j < ParseTimeFormats.Length; j++) {
+ string firstPart = ParseTimeFormats [j];
+ bool incompleteFormat = false;
+ if (_DoParse (s, firstPart, "", false, out result, dfi, styles, false, ref incompleteFormat, 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 i = 0; i < monthDayFormats.Length; i++) {
+ if (_DoParse (s, firstPart, monthDayFormats [i], false, out result, dfi, styles, false, ref incompleteFormat, ref longYear))
+ return result;
+ }
+ 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, dfi, styles, false, ref incompleteFormat, ref longYear))
+ return result;
+ }
+ }
- // Try common formats with invariant culture
- if (ParseExact (s, commonFormats, DateTimeFormatInfo.InvariantInfo, styles, out result, false, ref longYear))
+ // Try as a last resort all the patterns
+ if (ParseExact (s, dfi.GetAllDateTimePatternsInternal (), dfi, styles, out result, false, ref longYear))
return result;
#if NET_2_0
return ParseExact (s, format, fp, DateTimeStyles.None);
}
- internal static int _ParseNumber (string s, int valuePos,
- int min_digits,
- int digits,
- bool leadingzero,
- bool sloppy_parsing,
- out int num_parsed)
+ private static bool IsDayBeforeMonth (DateTimeFormatInfo dfi)
+ {
+ int dayIndex = dfi.MonthDayPattern.IndexOf('d');
+ int monthIndex = dfi.MonthDayPattern.IndexOf('M');
+ if (dayIndex == -1 || monthIndex == -1)
+ throw new FormatException (Locale.GetText("Order of month and date is not defined by {0}", dfi.MonthDayPattern));
+
+ return dayIndex < monthIndex;
+ }
+
+ private static string[] YearMonthDayFormats (DateTimeFormatInfo dfi)
+ {
+ 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)
+ throw new FormatException (Locale.GetText("Order of year, month and date is not defined by {0}", dfi.ShortDatePattern));
+
+ 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
+ throw new FormatException (Locale.GetText("Order of date, year and month defined by {0} is not supported", dfi.ShortDatePattern));
+ else if (dayIndex < monthIndex)
+ return ParseDayMonthYearFormats;
+ else if (dayIndex < yearIndex)
+ return ParseMonthDayYearFormats;
+ else
+ // The year cannot be between the month and the date
+ throw new FormatException (Locale.GetText("Order of month, year and date defined by {0} is not supported", dfi.ShortDatePattern));
+ }
+
+ 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)
+ private static int _ParseEnum (string s, int sPos, string[] values, string[] invValues, bool exact, out int num_parsed)
{
- int i;
-
// 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 (i = values.Length - 1; i >= 0; 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;
+ 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)
+ {
+ 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,
+ DateTimeFormatInfo dfi,
+ DateTimeStyles style,
+ bool firstPartIsDate,
+ ref bool incompleteFormat,
+ ref bool longYear)
{
#if NET_2_0
DateTimeKind explicit_kind = DateTimeKind.Unspecified;
bool useutc = false, use_localtime = true;
bool use_invariant = false;
bool sloppy_parsing = false;
+ bool afterTimePart = firstPartIsDate && secondPart == "";
+ bool flexibleTwoPartsParsing = !exact && secondPart != null;
+ incompleteFormat = false;
int valuePos = 0;
- if (format.Length == 1)
+ string format = firstPart;
+ bool afterTFormat = false;
+ DateTimeFormatInfo invInfo = DateTimeFormatInfo.InvariantInfo;
+ if (format.Length == 1) {
+ if (format == "u")
+ use_localtime = false;
format = _GetStandardPattern (format [0], dfi, out useutc, out use_invariant);
+ }
else if (!exact && CultureInfo.InvariantCulture.CompareInfo.IndexOf (format, "GMT", CompareOptions.Ordinal) >= 0)
useutc = true;
+ result = new DateTime (0);
+ if (format == 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 (afterTimePart && 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;
+ if (!firstPartIsDate || format == "")
+ afterTimePart = true;
+ continue;
+ }
+ break;
+ }
+
bool leading_zeros = true;
if (chars[pos] == '\'') {
if (chars[pos+num] == '\'')
break;
- if (valuePos == s.Length)
- return false;
- if (s [valuePos] != chars [pos + num])
+ if (valuePos == s.Length || 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.RawAbbreviatedDayNames, out num_parsed);
+ dayofweek = _ParseEnum (s, valuePos, dfi.RawAbbreviatedDayNames, invInfo.RawAbbreviatedDayNames, exact, out num_parsed);
else
- {
- dayofweek = _ParseEnum (s, valuePos, dfi.RawDayNames, 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.RawAbbreviatedMonthNames , out num_parsed) + 1;
+ month = _ParseEnum (s, valuePos, dfi.RawAbbreviatedMonthNames, invInfo.RawAbbreviatedMonthNames, exact, out num_parsed) + 1;
else
- {
- month = _ParseEnum (s, valuePos, dfi.RawMonthNames, 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;
goto case 'f';
#endif
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, 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);
- if (num_parsed < 0)
- return false;
- valuePos += num_parsed;
- if (Char.IsDigit (s [valuePos]))
- num_parsed = 0;
- else if (!_ParseString (s, valuePos, 0, dfi.TimeSeparator, out num_parsed))
- return false;
+ tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
+ else {
+ tzoffset = _ParseNumber (s, valuePos, 1, 2, true, sloppy_parsing, out num_parsed);
valuePos += num_parsed;
- tzoffmin = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
if (num_parsed < 0)
return false;
- num = 2;
+
+ 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;
#if NET_2_0
if (s [valuePos] == 'Z') {
valuePos++;
explicit_kind = DateTimeKind.Utc;
- } else if (s [valuePos] == '+' || s [valuePos] == '-') {
+ }
+ else if (s [valuePos] == '+' || s [valuePos] == '-') {
if (tzsign != -1)
return false;
if (s [valuePos] == '+')
// zzz
tzoffset = _ParseNumber (s, valuePos, 0, 2, true, sloppy_parsing, out num_parsed);
+ valuePos += num_parsed;
if (num_parsed < 0)
return false;
- valuePos += num_parsed;
+
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);
- if (num_parsed < 0)
- return false;
num = 2;
explicit_kind = DateTimeKind.Local;
+ if (num_parsed < 0)
+ return false;
}
break;
#endif
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':
#endif
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 < 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 (dayofweek != -1 && dayofweek != (int) result.DayOfWeek)
+ return false;
// If no timezone was specified, default to the local timezone.
TimeSpan utcoffset;
return true;
}
-
public static DateTime ParseExact (string s, string format,
IFormatProvider fp, DateTimeStyles style)
{
if (formats.Length == 0)
throw new FormatException ("Format specifier was invalid.");
- for (int i = 0; i < formats.Length; i++) {
- string format = formats [i];
- if (format == null || format.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))
bool exact, ref bool longYear)
{
int i;
+ bool incompleteFormat = false;
for (i = 0; i < formats.Length; i++)
{
DateTime result;
+ string format = formats[i];
+ if (format == null || format == String.Empty)
+ throw new FormatException ("Invalid Format String");
- if (_DoParse (s, formats[i], exact, out result, dfi, style, ref longYear)) {
+ if (_DoParse (s, formats[i], null, exact, out result, dfi, style, false, ref incompleteFormat, ref longYear)) {
ret = result;
return true;
}
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
+ // yyy+ full year with leading zeros if needed.
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));
-
+ ZeroPad (result, dfi.Calendar.GetYear (this), tokLen);
break;
+
case 'g':
// Era name
tokLen = CountRepeat (format, i, ch);
if (format.Length == 1) {
char fchar = format [0];
format = _GetStandardPattern (fchar, dfi, out useutc, out use_invariant);
+ if (fchar == 'U')
+ 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
631502115000000000L, // 25 Feb 2002 - 05:25:00
631502115130000000L, // 25 Feb 2002 - 05:25:13
631502079130000000L, // 25 Feb 2002 - 04:25:13
- 629197085770000000L // 06 Nov 1994 - 08:49:37
+ 629197085770000000L, // 06 Nov 1994 - 08:49:37
+ 631796544000000000L, // 01 Feb 2003 - 00:00:00
};
[SetUp]
{
DateTime t1 = new DateTime (myTicks[2]);
DateTime t2 = new DateTime (myTicks[1]);
+ DateTime t3 = new DateTime (999, 1, 2, 3, 4, 5);
// Standard patterns
Assert.AreEqual ("02/25/2002", t1.ToString ("d"), "#A1");
Assert.AreEqual ("Monday, 25 February 2002", t1.ToString ("D"), "#A2");
Assert.AreEqual (" / ", t1.ToString (" / "), "#B30");
Assert.AreEqual (" yyy ", t1.ToString (" 'yyy' "), "#B31");
Assert.AreEqual (" d", t1.ToString (" \\d"), "#B32");
+ Assert.AreEqual ("2002", t1.ToString ("yyy"), "#B33");
+ Assert.AreEqual ("0002002", t1.ToString ("yyyyyyy"), "#B34");
+ Assert.AreEqual ("999", t3.ToString ("yyy"), "#B33");
+ Assert.AreEqual ("0999", t3.ToString ("yyyy"), "#B33");
}
[Test]
public void TestParseExact3 ()
{
DateTime t1 = DateTime.ParseExact ("2002-02-25 04:25:13Z", "u", null);
- t1 = TimeZone.CurrentTimeZone.ToUniversalTime(t1);
Assert.AreEqual (2002, t1.Year, "#1");
Assert.AreEqual (02, t1.Month, "#2");
- // FIXME: This test is timezone dependent.
- //Assert.AreEqual (25, t1.Day, "#3");
- //Assert.AreEqual (04, t1.Hour, "#4");
+ Assert.AreEqual (25, t1.Day, "#3");
+ Assert.AreEqual (04, t1.Hour, "#4");
Assert.AreEqual (25, t1.Minute, "#5");
Assert.AreEqual (13, t1.Second, "#6");
}
Assert.AreEqual (04, t1.Hour, "#A10");
Assert.AreEqual (25, t1.Minute, "#A11");
Assert.AreEqual (13, t1.Second, "#A12");
+ t1 = DateTime.ParseExact ("Monday, 25 February 2002 04:25:13", "U", null);
+ Assert.AreEqual ("Monday, 25 February 2002 04:25:13", t1.ToString ("U"), "#A13");
DateTime t2 = new DateTime (DateTime.Today.Year, 2, 25);
t1 = DateTime.ParseExact ("February 25", "m", null);
t1 = DateTime.ParseExact ("Sun Nov 6 08:49:37 1994", formats, enUS,
DateTimeStyles.AllowWhiteSpaces);
Assert.AreEqual (myTicks [6], t1.Ticks, "#J6");
+ t1 = DateTime.ParseExact ("Monday, 25 February 2002 05:25:13",
+ "ddddddd, dd MMMMMMM yyyy HHHHH:mmmmm:sssss",
+ null, DateTimeStyles.AdjustToUniversal);
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#J7");
// Bug 52274
t1 = DateTime.ParseExact ("--12--", "--MM--" , null);
}
[Test]
- public void TestParse ()
+ public void TestParseDateFirst ()
{
// Standard patterns
- DateTime t1 = DateTime.Parse ("02/25/2002");
+ CultureInfo USCultureInfo = new CultureInfo("en-US");
+ DateTime t1 = DateTime.Parse ("02/25/2002", USCultureInfo);
Assert.AreEqual (myTicks [0], t1.Ticks, "#A1");
- t1 = DateTime.Parse ("02-25-2002");
- t1 = DateTime.Parse ("2002-02-25");
+ t1 = DateTime.Parse ("2002-02-25", USCultureInfo);
Assert.AreEqual (myTicks [0], t1.Ticks, "#A2");
t1 = DateTime.Parse ("Monday, 25 February 2002");
Assert.AreEqual (myTicks [0], t1.Ticks, "#A3");
Assert.AreEqual (myTicks [3], t1.Ticks, "#A4");
t1 = DateTime.Parse ("Monday, 25 February 2002 05:25:13");
Assert.AreEqual (myTicks [4], t1.Ticks, "#A5");
- t1 = DateTime.Parse ("02/25/2002 05:25");
+ t1 = DateTime.Parse ("02/25/2002 05:25", USCultureInfo);
Assert.AreEqual (myTicks [3], t1.Ticks, "#A6");
- t1 = DateTime.Parse ("02/25/2002 05:25:13");
+ t1 = DateTime.Parse ("02/25/2002 05:25:13", USCultureInfo);
Assert.AreEqual (myTicks [4], t1.Ticks, "#A7");
t1 = DateTime.Parse ("2002-02-25 04:25:13Z");
t1 = TimeZone.CurrentTimeZone.ToUniversalTime(t1);
Assert.AreEqual (04, t1.Hour, "#A11");
Assert.AreEqual (25, t1.Minute, "#A12");
Assert.AreEqual (13, t1.Second, "#A13");
-
- DateTime t2 = new DateTime (DateTime.Today.Year, 2, 25);
- t1 = DateTime.Parse ("February 25");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#B1");
-
- t2 = new DateTime (DateTime.Today.Year, 2, 8);
- t1 = DateTime.Parse ("February 08");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#B2");
+ t1 = DateTime.Parse ("Mon,02/25/2002", USCultureInfo);
+ Assert.AreEqual (myTicks [0], t1.Ticks, "#A14");
+ DateTime t2 = new DateTime (1999, 1, 2, 0, 3, 4);
+ t1 = DateTime.Parse (t2.ToLongTimeString ());
+ Assert.AreEqual (0, t1.Hour, "#A14");
// parsed as UTC string
t1 = DateTime.Parse ("Mon, 25 Feb 2002 04:25:13 GMT");
Assert.AreEqual (25, t1.Minute, "#C5");
Assert.AreEqual (13, t1.Second, "#C6");
+ // Some date 'T' time formats
+#if NET_2_0 // Net_1_1 requires hh:mm:ss
+ t1 = DateTime.Parse ("2002-02-25T05:25");
+ Assert.AreEqual (myTicks [3], t1.Ticks, "#D1");
+#endif
t1 = DateTime.Parse ("2002-02-25T05:25:13");
Assert.AreEqual (myTicks [4], t1.Ticks, "#D1");
+ t1 = DateTime.Parse ("2002-02-25T05:25:13.008");
+ Assert.AreEqual (myTicks [2], t1.Ticks, "#D1");
+ t1 = DateTime.Parse ("02-2002-25T05:25:13");
+ Assert.AreEqual (myTicks [4], t1.Ticks, "#D1");
- t2 = DateTime.Today + new TimeSpan (5,25,0);
- t1 = DateTime.Parse ("05:25");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D2");
+ // Day month
+ t2 = new DateTime (DateTime.Today.Year, 2, 25);
+ t1 = DateTime.Parse ("February 25", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#B1");
- t2 = DateTime.Today + new TimeSpan (5,25,13);
- t1 = DateTime.Parse ("05:25:13");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D3");
+ t2 = new DateTime (DateTime.Today.Year, 2, 8);
+ t1 = DateTime.Parse ("February 08", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#B2");
+ t2 = new DateTime (DateTime.Today.Year, 2, 8);
+ t1 = DateTime.Parse ("February 8", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#D6");
+
+ // Month year
t2 = new DateTime (2002, 2, 1);
t1 = DateTime.Parse ("2002 February");
Assert.AreEqual (t2.Ticks, t1.Ticks, "#D4");
t1 = DateTime.Parse ("2002 February", new CultureInfo ("ja-JP"));
Assert.AreEqual (t2.Ticks, t1.Ticks, "#D5");
- t2 = new DateTime (DateTime.Today.Year, 2, 8);
- t1 = DateTime.Parse ("February 8");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D6");
-
// bug #72132
t2 = new DateTime (2002, 2, 25, 5, 25, 22);
t1 = DateTime.Parse ("Monday, 25 February 2002 05:25:22",
new CultureInfo ("hi-IN"));
Assert.AreEqual (t2.Ticks, t1.Ticks, "#D8");
+ // MM-yyyy-dd + different time formats
+ t1 = DateTime.Parse ("02-2002-25 05:25", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#E1");
+ t1 = DateTime.Parse ("02-2002-25 05:25:13", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#E1");
+ t1 = DateTime.Parse ("02-2002-25 05:25:13 Mon", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#E2");
+ t1 = DateTime.Parse ("02-2002-25 05:25:13 Monday", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#E3");
+ t1 = DateTime.Parse ("02-2002-25 05:25:13.008", USCultureInfo);
+ Assert.AreEqual (myTicks[2], t1.Ticks, "#E4");
+
+ // Formats with timezone
+ long offset = TimeZone.CurrentTimeZone.GetUtcOffset(t1).Ticks;
+ long hourTicks = 36000000000L;
+ long halfHourTicks = hourTicks / 2;
+ t1 = DateTime.Parse ("02-2002-25 05:25+01", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks + hourTicks - offset, "#F1");
+ t1 = DateTime.Parse ("02-2002-25 05:25-01", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks - hourTicks - offset, "#F2");
+ t1 = DateTime.Parse ("02-2002-25 05:25+00:30", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks + hourTicks/2 - offset, "#F3");
+ t1 = DateTime.Parse ("02-2002-25 05:25:13+02", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks + 2*hourTicks - offset, "#F4");
+#if NET_2_0
+ // NET 1.0 doesn't accept second fractions and time zone.
+ t1 = DateTime.Parse ("2002-02-25 05:25:13.008-02");
+ Assert.AreEqual (myTicks[2], t1.Ticks - 2*hourTicks - offset, "#F5");
+ // NET 1.0 doesn't parse well time zone with AM afterwards.
+ t1 = DateTime.Parse ("02-25-2002 05:25:13-02 AM", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks - 2*hourTicks - offset, "#F6");
+ t1 = DateTime.Parse ("25 Feb 2002 05:25:13-02 AM", USCultureInfo);
+ Assert.AreEqual (myTicks[4], t1.Ticks - 2*hourTicks - offset, "#F6");
+#endif
+ }
+
+ [Test]
+ public void TestParseTimeFirst ()
+ {
+ CultureInfo USCultureInfo = new CultureInfo("en-US");
+
+ // Hour only patterns
+ DateTime t2 = DateTime.Today + new TimeSpan (5,25,0);
+ DateTime t1 = DateTime.Parse ("05:25");
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#C1");
+ t2 = DateTime.Today + new TimeSpan (5,25,13);
+ t1 = DateTime.Parse ("05:25:13");
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#B2");
+
+ // Test with different date formats
+ t1 = DateTime.Parse ("05:25 02/25/2002", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#B1");
+ t1 = DateTime.Parse ("05:25:13 2002-02-25");
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#B2");
+ t1 = DateTime.Parse ("05:25:13.008 02-2002-25");
+ Assert.AreEqual (myTicks[2], t1.Ticks, "#B3");
+ t1 = DateTime.Parse ("05:25:13.008 Feb 25 2002");
+ Assert.AreEqual (myTicks[2], t1.Ticks, "#B4");
+ t1 = DateTime.Parse ("05:25:13.008 25 Feb 2002");
+ Assert.AreEqual (myTicks[2], t1.Ticks, "#B5");
+
+ // Add AM and day of the week
+ t1 = DateTime.Parse ("AM 05:25:13 2002-02-25");
+ Assert.AreEqual (myTicks[4], t1.Ticks, "#C1");
+ t1 = DateTime.Parse ("Monday05:25 02/25/2002", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#C2");
+ t1 = DateTime.Parse ("Mon 05:25 AM 02/25/2002", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#C3");
+ t1 = DateTime.Parse ("AM 05:25 Monday, 02/25/2002", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#C4");
+ t1 = DateTime.Parse ("05:25 02/25/2002 Monday", USCultureInfo);
+ Assert.AreEqual (myTicks[3], t1.Ticks, "#C5");
+ t1 = DateTime.Parse ("PM 03:25:13.008 02-2002-25");
+ Assert.AreEqual (myTicks[1], t1.Ticks, "#C6");
+
// ASP.NET QuickStarts
t2 = new DateTime (2002, 10, 7, 15, 6, 0);
- t1 = DateTime.Parse ("3:06 PM 10/7/2002");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D9");
-
+ t1 = DateTime.Parse ("3:06 PM 10/7/2002", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#D1");
t2 = new DateTime (2002, 10, 7, 15, 6, 0);
- t1 = DateTime.Parse ("3:06 pm 10/7/2002");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D10");
-
+ t1 = DateTime.Parse ("3:06 pm 10/7/2002", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#D2");
t2 = new DateTime (2002, 10, 7, 3, 6, 0);
- t1 = DateTime.Parse ("3:06 AM 10/7/2002");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D11");
-
+ t1 = DateTime.Parse ("3:06 AM 10/7/2002", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#D3");
t2 = new DateTime (2002, 10, 7, 3, 6, 0);
- t1 = DateTime.Parse ("3:06 am 10/7/2002");
- Assert.AreEqual (t2.Ticks, t1.Ticks, "#D12");
+ t1 = DateTime.Parse ("3:06 am 10/7/2002", USCultureInfo);
+ Assert.AreEqual (t2.Ticks, t1.Ticks, "#D4");
+ }
+
+ [Test]
+ public void TestParseWithDifferentShortDatePatterns ()
+ {
+ CultureInfo cultureInfo = new CultureInfo("en-US");
+ DateTimeFormatInfo dateFormatInfo = cultureInfo.DateTimeFormat;
+ DateTime t1 = DateTime.Parse ("02/01/2003", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#A1");
+
+ // Day, month year behaviour
+ dateFormatInfo.ShortDatePattern = "dd/MM/yyyy";
+ t1 = DateTime.Parse ("01/02/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#B1");
+ t1 = DateTime.Parse ("01/02/2003", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#B2");
+ t1 = DateTime.Parse ("2003/02/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#B3");
+ t1 = DateTime.Parse ("01/Feb/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#B4");
+ t1 = DateTime.Parse ("Feb/01/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#B5");
+
+ // Month, day year behaviour
+ dateFormatInfo.ShortDatePattern = "MM/dd/yyyy";
+ t1 = DateTime.Parse ("02/01/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#C1");
+ t1 = DateTime.Parse ("02/01/2003", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#C2");
+ t1 = DateTime.Parse ("2003/02/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#C3");
+ t1 = DateTime.Parse ("01/Feb/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#C4");
+ t1 = DateTime.Parse ("Feb/01/03", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#C5");
+
+ // Year, month day behaviour
+ dateFormatInfo.ShortDatePattern = "yyyy/MM/dd";
+ t1 = DateTime.Parse ("03/02/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#D1");
+ t1 = DateTime.Parse ("02/01/2003", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#D2");
+ t1 = DateTime.Parse ("2003/02/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#D3");
+ t1 = DateTime.Parse ("03/Feb/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#D4");
+ t1 = DateTime.Parse ("Feb/03/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#D5");
+
+ // Year, day month behaviour
+ // Note that no culture I am aware of has this pattern, and indeed
+ dateFormatInfo.ShortDatePattern = "yyyy/dd/MM";
+ t1 = DateTime.Parse ("03/01/02", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#E1");
+ t1 = DateTime.Parse ("01/02/2003", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#E2");
+#if NET_2_0
+ t1 = DateTime.Parse ("2003/01/02", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#E3");
+#else
+ t1 = DateTime.Parse ("2003/02/01", cultureInfo);
+ Assert.AreEqual (myTicks[7], t1.Ticks, "#E3");
+#endif
+ // For some reason the following throws an exception on .Net
+ // t1 = DateTime.Parse ("03/Feb/01", cultureInfo);
+ // Assert.AreEqual (myTicks[7], t1.Ticks, "#E4");
+ // t1 = DateTime.Parse ("03/01/Feb", cultureInfo);
+ // Assert.AreEqual (myTicks[7], t1.Ticks, "#E5");
+ // t1 = DateTime.Parse ("Feb/01/03", cultureInfo);
+ // Assert.AreEqual (myTicks[7], t1.Ticks, "#E6");
+ }
+
+ [Test]
+ public void TestParseWithDifferentMonthDayPatterns ()
+ {
+ CultureInfo cultureInfo = new CultureInfo("en-US");
+ DateTimeFormatInfo dateFormatInfo = cultureInfo.DateTimeFormat;
+ DateTime t1 = DateTime.Parse ("Feb 03", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#A1");
+ Assert.AreEqual (3, t1.Day, "#A2");
+
+ // Day month behaviour
+ dateFormatInfo.MonthDayPattern = "dd/MM";
+#if NET_2_0
+ t1 = DateTime.Parse ("Feb 03", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#B1");
+ Assert.AreEqual (1, t1.Day, "#B2");
+ Assert.AreEqual (2003, t1.Year, "#B3");
+#else // In .Net 1.0 "Feb 03" is always Feb 3rd (and not Feb 2003).
+ t1 = DateTime.Parse ("Feb 03", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#B4");
+ Assert.AreEqual (3, t1.Day, "#B5");
+#endif
+ t1 = DateTime.Parse ("03/02", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#B6");
+ Assert.AreEqual (3, t1.Day, "#B7");
+ t1 = DateTime.Parse ("03 Feb", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#B8");
+ Assert.AreEqual (3, t1.Day, "#B9");
+
+ // Month day behaviour
+ dateFormatInfo.MonthDayPattern = "MM/dd";
+ t1 = DateTime.Parse ("Feb 03", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#C1");
+ Assert.AreEqual (3, t1.Day, "#C2");
+ t1 = DateTime.Parse ("02/03", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#C3");
+ Assert.AreEqual (3, t1.Day, "#C4");
+ t1 = DateTime.Parse ("03 Feb", cultureInfo);
+ Assert.AreEqual (2, t1.Month, "#C5");
+ Assert.AreEqual (3, t1.Day, "#C6");
}
[Test]
}
[Test]
- [Category ("NotWorking")] // Mono accept this format for ALL cultures
+ // FIXME: This test doesn't work on cultures like es-DO which have patterns
+ // for both dd/MM/yyyy & MM/dd/yyyy
+ [Category ("NotWorking")]
public void Parse_Bug53023a ()
{
foreach (CultureInfo ci in CultureInfo.GetCultures (CultureTypes.SpecificCultures)) {
}
}
+ [Test]
+ [ExpectedException (typeof (FormatException))]
+ [Category ("NotWorking")]
+ public void Parse_RequireSpaceSeparator ()
+ {
+ DateTime.Parse ("05:25:132002-02-25", CultureInfo.InvariantCulture);
+ }
+
[Test]
[ExpectedException (typeof (FormatException))]
public void Parse_DontAccept2DigitsYears ()
DateTime.ParseExact ("05", "yyyy", CultureInfo.InvariantCulture);
}
+ [Test]
+ [ExpectedException (typeof (FormatException))]
+ public void Parse_DontAcceptEmptyHours ()
+ {
+ DateTime.ParseExact (":05", "H:m", CultureInfo.InvariantCulture);
+ }
+
+ [Test]
+ [ExpectedException (typeof (FormatException))]
+ public void Parse_DontAcceptEmptyMinutes ()
+ {
+ DateTime.ParseExact ("0::0", "H:m:s", CultureInfo.InvariantCulture);
+ }
+
[Test]
public void ParseCOMDependentFormat ()
{