Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -3289,6 +3289,47 @@ private static bool ParseTimeZoneOffset(ref __DTString str, int len, scoped ref
return true;
}

/// Determines if a format string contains a day-of-month specifier ('d' or 'dd').
/// Properly handles quoted sections and escape characters.
private static bool FormatContainsDayOfMonthSpecifier(ReadOnlySpan<char> format)
{
bool inQuote = false;
for (int i = 0; i < format.Length; i++)
{
char ch = format[i];
// Skip the next character if it's escaped
if (ch == '\\' || ch == '%')
{
i++;
continue;
}
// Toggle quote state
if (ch == '\'' || ch == '"')
{
inQuote = !inQuote;
continue;
}
// Only check for 'd' when not in quotes
if (!inQuote && ch == 'd')
{
// Make sure it's a day-of-month specifier (d or dd)
// and not a day-of-week specifier (ddd or dddd)
int repeatCount = 1;
while (i + 1 < format.Length && format[i + 1] == 'd')
{
repeatCount++;
i++;
}
// Only day-of-month specifiers (d or dd) trigger genitive case
if (repeatCount <= 2)
{
return true;
}
}
}
return false;
}

/*=================================MatchAbbreviatedMonthName==================================
**Action: Parse the abbreviated month name from string starting at str.Index.
**Returns: A value from 1 to 12 for the first month to the twelfth month.
Expand All @@ -3297,7 +3338,7 @@ private static bool ParseTimeZoneOffset(ref __DTString str, int len, scoped ref
**Exceptions: FormatException if an abbreviated month name can not be found.
==============================================================================*/

private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result)
private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, scoped ref int result, ReadOnlySpan<char> format)
{
int maxMatchStrLen = 0;
result = -1;
Expand Down Expand Up @@ -3357,12 +3398,12 @@ private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormat
}
}

// Search genitive form.
if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0)
// Search genitive form only if the format contains a day-of-month specifier
bool useDaySpecifier = FormatContainsDayOfMonthSpecifier(format);
if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && useDaySpecifier)
{
int tempResult = str.MatchLongestWords(dtfi.InternalGetGenitiveMonthNames(abbreviated: true), ref maxMatchStrLen);

// We found a longer match in the genitive month name. Use this as the result.
// We found a longer match in the genitive month name. Use this as the result.
// tempResult + 1 should be the month value.
if (tempResult >= 0)
{
Expand Down Expand Up @@ -4045,7 +4086,7 @@ private static bool ParseByFormat(
{
if (tokenLen == 3)
{
if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth))
if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth, format.Value))
{
result.SetBadDateTimeFailure();
return false;
Expand Down
Loading