/*=================================DoStrictParse==================================
**Action: Do DateTime parsing using the format in formatParam.
**Returns: The parsed DateTime.
**Arguments:
**Exceptions:
**
**Notes:
** When the following general formats are used, InvariantInfo is used in dtfi:
** 'r', 'R', 's'.
** When the following general formats are used, the time is assumed to be in Universal time.
**
**Limitations:
** Only GregarianCalendar is supported for now.
** Only support GMT timezone.
==============================================================================*/
private static bool DoStrictParse(
String s,
String formatParam,
DateTimeStyles styles,
DateTimeFormatInfo dtfi,
ref DateTimeResult result) {
ParsingInfo parseInfo = new ParsingInfo();
parseInfo.Init();
parseInfo.calendar = dtfi.Calendar;
parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0);
parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0);
// We need the original values of the following two below.
String originalFormat = formatParam;
if (formatParam.Length == 1) {
if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U') {
// The 'U' format is not allowed for DateTimeOffset
result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
return false;
}
formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result);
}
bool bTimeOnly = false;
result.calendar = parseInfo.calendar;
if (parseInfo.calendar.ID == Calendar.CAL_HEBREW) {
parseInfo.parseNumberDelegate = m_hebrewNumberParser;
parseInfo.fCustomNumberParser = true;
}
// Reset these values to negative one so that we could throw exception
// if we have parsed every item twice.
result.Hour = result.Minute = result.Second = -1;
__DTString format = new __DTString(formatParam, dtfi, false);
__DTString str = new __DTString(s, dtfi, false);
if (parseInfo.fAllowTrailingWhite) {
// Trim trailing spaces if AllowTrailingWhite.
format.TrimTail();
format.RemoveTrailingInQuoteSpaces();
str.TrimTail();
}
if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) {
format.SkipWhiteSpaces();
format.RemoveLeadingInQuoteSpaces();
str.SkipWhiteSpaces();
}
//
// Scan every character in format and match the pattern in str.
//
while (format.GetNext()) {
// We trim inner spaces here, so that we will not eat trailing spaces when
// AllowTrailingWhite is not used.
if (parseInfo.fAllowInnerWhite) {
str.SkipWhiteSpaces();
}
if (!ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) {
return (false);
}
}
if (str.Index < str.Value.Length - 1) {
// There are still remaining character in str.
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
return false;
}
if (parseInfo.fUseTwoDigitYear && ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) == 0)) {
// A two digit year value is expected. Check if the parsed year value is valid.
if (result.Year >= 100) {
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
return false;
}
try {
result.Year = parseInfo.calendar.ToFourDigitYear(result.Year);
}
catch (ArgumentOutOfRangeException e) {
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
return false;
}
}
if (parseInfo.fUseHour12) {
if (parseInfo.timeMark == TM.NotSet) {
// hh is used, but no AM/PM designator is specified.
// Assume the time is AM.
// Don't throw exceptions in here becasue it is very confusing for the caller.
// I always got confused myself when I use "hh:mm:ss" to parse a time string,
// and ParseExact() throws on me (because I didn't use the 24-hour clock 'HH').
parseInfo.timeMark = TM.AM;
}
if (result.Hour > 12) {
// AM/PM is used, but the value for HH is too big.
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
return false;
}
if (parseInfo.timeMark == TM.AM) {
if (result.Hour == 12) {
result.Hour = 0;
}
} else {
result.Hour = (result.Hour == 12) ? 12 : result.Hour + 12;
}
}
else
{
// Military (24-hour time) mode
//
// AM cannot be set with a 24-hour time like 17:15.
// PM cannot be set with a 24-hour time like 03:15.
if ( (parseInfo.timeMark == TM.AM && result.Hour >= 12)
||(parseInfo.timeMark == TM.PM && result.Hour < 12)) {
result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
return false;
}
}
// Check if the parased string only contains hour/minute/second values.
bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
if (!CheckDefaultDateTime(ref result, ref parseInfo.calendar, styles)) {
return false;
}
if (!bTimeOnly && dtfi.HasYearMonthAdjustment) {
if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0))) {
result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
return false;
}
}
if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day,
result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate)) {
result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
return false;
}
if (result.fraction > 0) {
result.parsedDate = result.parsedDate.AddTicks((long)Math.Round(result.fraction * Calendar.TicksPerSecond));
}
//
// We have to check day of week before we adjust to the time zone.
// It is because the value of day of week may change after adjusting
// to the time zone.
//
if (parseInfo.dayOfWeek != -1) {
//
// Check if day of week is correct.
//
if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate)) {
result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
return false;
}
}
if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly)) {
return false;
}
return true;
}