Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,10 @@ internal enum DS
new DS[] { DS.BEGIN, DS.ERROR, DS.TX_N, DS.N, DS.D_Nd, DS.T_Nt, DS.ERROR, DS.D_M, DS.D_M, DS.D_S, DS.T_S, DS.BEGIN, DS.D_Y, DS.D_Y, DS.ERROR, DS.BEGIN, DS.BEGIN, DS.ERROR },

// DS.N // DS.N
new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR },
new DS[] { DS.ERROR, DS.DX_NN, DS.TX_NN, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR },

// DS.NN // DS.NN
new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR },
new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_NNN, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR },

// DS.D_Nd // DS.D_Nd
new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.D_NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.D_Nd, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.ERROR, DS.D_Nd, DS.ERROR },
Expand Down Expand Up @@ -3192,8 +3192,8 @@ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref

/*=================================ParseSign==================================
**Action: Parse a positive or a negative sign.
**Returns: true if postive sign. flase if negative sign.
**Arguments: str: a __DTString. The parsing will start from the
**Returns: true if positive sign. false if negative sign.
**Arguments: str: a __DTString. The parsing will start from the
** next character after str.Index.
**Exceptions: FormatException if end of string is encountered or a sign
** symbol is not found.
Expand Down Expand Up @@ -5244,6 +5244,8 @@ private static void Trace(string s)
//
internal ref struct __DTString
{
internal const char RightToLeftMark = '\u200F';

//
// Value property: stores the real string to be parsed.
//
Expand Down Expand Up @@ -5418,7 +5420,7 @@ internal TokenType GetSeparatorToken(DateTimeFormatInfo dtfi, out int indexBefor
indexBeforeSeparator = Index;
charBeforeSeparator = m_current;
TokenType tokenType;
if (!SkipWhiteSpaceCurrent())
if (!SkipWhiteSpaceAndRtlMarkCurrent())
{
// Reach the end of the string.
return TokenType.SEP_End;
Expand Down Expand Up @@ -5672,26 +5674,28 @@ internal void SkipWhiteSpaces()
}

//
// Skip white spaces from the current position
// Skip white spaces and right-to-left Mark from the current position
//
// U+200F is the Unicode right-to-left mark. In some Bidi cultures, this mark gets inserted inside the formatted date or time to have the output displayed in the correct layout.
// This mark does not affect the date or the time component values. Ignoring this mark during parsing wouldn't affect the result but will avoid having the parsing fail.
// Return false if end of string is encountered.
//
internal bool SkipWhiteSpaceCurrent()
internal bool SkipWhiteSpaceAndRtlMarkCurrent()
{
if (Index >= Length)
{
return false;
}

if (!char.IsWhiteSpace(m_current))
if (!char.IsWhiteSpace(m_current) && m_current != RightToLeftMark)
{
return true;
}

while (++Index < Length)
{
m_current = Value[Index];
if (!char.IsWhiteSpace(m_current))
if (!char.IsWhiteSpace(m_current) && m_current != RightToLeftMark)
{
return true;
}
Expand Down
37 changes: 37 additions & 0 deletions src/libraries/System.Runtime/tests/System/DateTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,43 @@ public static void Parse_Japanese()
Assert.Equal(expected, DateTime.Parse(expectedString, cultureInfo));
}

private static bool IsNotOSXOrBrowser => !PlatformDetection.IsOSXLike && !PlatformDetection.IsBrowser;

[ConditionalTheory(nameof(IsNotOSXOrBrowser))]
[InlineData("ar")]
[InlineData("ar-EG")]
[InlineData("ar-IQ")]
[InlineData("ar-SA")]
[InlineData("ar-YE")]
public static void DateTimeParsingWithBiDiCultureTest(string cultureName)
{
DateTime dt = new DateTime(2021, 11, 30, 14, 30, 40);
CultureInfo ci = CultureInfo.GetCultureInfo(cultureName);
string formatted = dt.ToString("d", ci);
Assert.Equal(dt.Date, DateTime.Parse(formatted, ci));
formatted = dt.ToString("g", ci);
DateTime parsed = DateTime.Parse(formatted, ci);
Assert.Equal(dt.Date, parsed.Date);
Assert.Equal(dt.Hour, parsed.Hour);
Assert.Equal(dt.Minute, parsed.Minute);
}

[Fact]
public static void DateTimeParsingWithSpaceTimeSeparators()
{
DateTime dt = new DateTime(2021, 11, 30, 14, 30, 40);
CultureInfo ci = CultureInfo.GetCultureInfo("en-US");
// It is possible we find some cultures use such formats. dz-BT is example of that
string formatted = dt.ToString("yyyy/MM/dd hh mm tt", ci);
DateTime parsed = DateTime.Parse(formatted, ci);
Assert.Equal(dt.Hour, parsed.Hour);
Assert.Equal(dt.Minute, parsed.Minute);

formatted = dt.ToString("yyyy/MM/dd hh mm ss tt", ci);
parsed = DateTime.Parse(formatted, ci);
Assert.Equal(dt, parsed);
}

[Fact]
public static void Parse_InvalidArguments_Throws()
{
Expand Down