Skip to content

Commit f6f3bfd

Browse files
authored
Merge 65ebdd3 into 71509e3
2 parents 71509e3 + 65ebdd3 commit f6f3bfd

3 files changed

Lines changed: 131 additions & 7 deletions

File tree

RELEASE_NOTES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## XMLUnit.NET 2.11.2 - /not released, yet/
44

5+
* `IsDateTimePlaceholderHandler` now supports an optional second argument specifying a
6+
different CultureInfo than the invariant culture used by default as a BCP 47 language tag.
7+
8+
Also modified the logic when no argument is present to still try
9+
parsing using the current culture and then trying a set of ISO
10+
patterns using the invariant culture in turn - making `isDateTime`
11+
match what the Java version does mor closely.
12+
13+
PR [#54](https://github.com/xmlunit/xmlunit.net/pull/54) based on
14+
PR [xmlunit/#335](https://github.com/xmlunit/xmlunit/pull/335) by [@jmestwa-coder](https://github.com/jmestwa-coder)
15+
516
## XMLUnit.NET 2.11.1 - /Released 2025-05-19/
617

718
* placeholders can now also be used inside of the local part of `xsi:type` attributes.

src/main/net-placeholders/IsDateTimePlaceholderHandler.cs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,94 @@ namespace Org.XmlUnit.Placeholder
2828
/// <para>
2929
/// since 2.8.0
3030
/// </para>
31+
/// <para>
32+
/// The first optional argument is the date/time pattern, the second optional
33+
/// argument is the culture used to parse it, given as a culture name (for example
34+
/// <c>de</c> or <c>fr-FR</c>). When no culture is given <see cref="CultureInfo.InvariantCulture"/>
35+
/// is used.
36+
/// </para>
3137
/// </remarks>
3238
public class IsDateTimePlaceholderHandler : IPlaceholderHandler
3339
{
3440
private const string _keyword = "isDateTime";
3541

42+
private static readonly IEnumerable<string> _isoPatterns = new List<string>
43+
{
44+
"yyyy-MM-dd",
45+
"yyyy-MM-ddTHH:mm",
46+
"yyyy-MM-ddTHH:mm:ss",
47+
"yyyy-MM-ddTHH:mm:ss.fff",
48+
"yyyy-MM-ddTHH:mm:ssK",
49+
"yyyy-MM-ddTHH:mm:ss.fffK",
50+
"yyyy-MM-dd HH:mm",
51+
"yyyy-MM-dd HH:mm:ss",
52+
"yyyy-MM-dd HH:mm:ss.fff",
53+
"yyyy-MM-dd HH:mm:ssK",
54+
"yyyy-MM-dd HH:mm:ss.fffK"
55+
};
56+
3657
/// <inheritdoc/>
3758
public string Keyword { get { return _keyword; } }
3859

3960
/// <inheritdoc/>
61+
/// <remarks>
62+
/// <para>
63+
/// When <paramref name="args"/> contains one element, it is used as the date/time pattern
64+
/// and <see cref="CultureInfo.InvariantCulture"/> is used for parsing.
65+
/// </para>
66+
/// <para>
67+
/// When <paramref name="args"/> contains two elements, the first is used as the date/time
68+
/// pattern and the second as the culture name for parsing.
69+
/// </para>
70+
/// <para>
71+
/// When no arguments are provided, the method tries to parse the text using ISO patterns
72+
/// with <see cref="CultureInfo.InvariantCulture"/>.
73+
/// </para>
74+
/// </remarks>
4075
public ComparisonResult Evaluate(string testText, params string[] args)
4176
{
42-
if (args != null && args.Length == 1) {
43-
return CanParse(args[0], testText)
77+
if (args != null && args.Length >= 1) {
78+
var culture = args.Length >= 2 && !string.IsNullOrEmpty(args[1])
79+
? new CultureInfo(args[1])
80+
: CultureInfo.InvariantCulture;
81+
return CanParse(args[0], testText, culture)
4482
? ComparisonResult.EQUAL
4583
: ComparisonResult.DIFFERENT;
4684
}
47-
DateTime _;
48-
var result = DateTime.TryParse(testText, out _);
49-
return result
85+
return CanParse(testText)
5086
? ComparisonResult.EQUAL
5187
: ComparisonResult.DIFFERENT;
5288
}
5389

54-
private bool CanParse(string pattern, string testText) {
90+
private bool CanParse(string testText) {
91+
if (string.IsNullOrEmpty(testText)) {
92+
return false;
93+
}
94+
// Try locale-aware short date and datetime formats with current culture
95+
if (CanParseWithCurrentCulture(testText)) {
96+
return true;
97+
}
98+
// Try all ISO patterns with InvariantCulture
99+
foreach (var pattern in _isoPatterns) {
100+
if (CanParse(pattern, testText, CultureInfo.InvariantCulture)) {
101+
return true;
102+
}
103+
}
104+
return false;
105+
}
106+
107+
private bool CanParseWithCurrentCulture(string testText) {
108+
DateTime result;
109+
// Try short date format
110+
if (DateTime.TryParse(testText, out result)) {
111+
return true;
112+
}
113+
return false;
114+
}
115+
116+
private bool CanParse(string pattern, string testText, CultureInfo culture) {
55117
try {
56-
var _ = DateTime.ParseExact(testText, pattern, CultureInfo.InvariantCulture);
118+
var _ = DateTime.ParseExact(testText, pattern, culture);
57119
return true;
58120
} catch (FormatException) {
59121
return false;

src/tests/net-placeholders/IsDateTimePlaceholderHandlerTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,26 @@ public void ShouldGetKeyword()
6363
Assert.AreEqual(expected, keyword);
6464
}
6565

66+
[Test]
67+
public void ShouldAcceptABunchOfStrings()
68+
{
69+
// Test various date formats that should be accepted by ISO patterns
70+
// Corresponds to Java test shouldAcceptABunchOfStrings
71+
var testStrings = new string[] {
72+
"2020-01-01",
73+
"01/01/2020",
74+
"01/01/2020",
75+
"2020-01-01T15:00",
76+
"2020-01-01 15:00:00Z",
77+
"01/01/2020 15:00"
78+
};
79+
80+
foreach (var testString in testStrings)
81+
{
82+
Assert.AreEqual(ComparisonResult.EQUAL, placeholderHandler.Evaluate(testString));
83+
}
84+
}
85+
6686
[Test]
6787
public void ShouldParseExplicitPattern() {
6888
Assert.AreEqual(ComparisonResult.EQUAL,
@@ -71,5 +91,36 @@ public void ShouldParseExplicitPattern() {
7191
placeholderHandler.Evaluate("abc", "dd MM yyyy HH:mm"));
7292
}
7393

94+
[Test]
95+
public void ShouldParsePatternIndependentOfDefaultLocale() {
96+
// Test that parsing works regardless of current culture
97+
// This corresponds to the Java test shouldParsePatternIndependentOfDefaultLocale
98+
var originalCulture = Thread.CurrentThread.CurrentCulture;
99+
try {
100+
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); // German culture
101+
// Should still parse with InvariantCulture (default for explicit patterns)
102+
Assert.AreEqual(ComparisonResult.EQUAL,
103+
placeholderHandler.Evaluate("24 June 2023", "dd MMMM yyyy"));
104+
} finally {
105+
Thread.CurrentThread.CurrentCulture = originalCulture;
106+
}
107+
}
108+
109+
[Test]
110+
public void ShouldParsePatternWithExplicitLocale() {
111+
// Test explicit locale specification - corresponds to Java test shouldParsePatternWithExplicitLocale
112+
Assert.AreEqual(ComparisonResult.EQUAL,
113+
placeholderHandler.Evaluate("24 Juni 2023", "dd MMMM yyyy", "de"));
114+
Assert.AreEqual(ComparisonResult.DIFFERENT,
115+
placeholderHandler.Evaluate("24 Juni 2023", "dd MMMM yyyy", "en"));
116+
}
117+
118+
[Test]
119+
public void ShouldUseInvariantCultureWithTwoArgsWhenSecondIsEmpty() {
120+
// When second argument is empty string, should fall back to InvariantCulture
121+
Assert.AreEqual(ComparisonResult.EQUAL,
122+
placeholderHandler.Evaluate("24 June 2023", "dd MMMM yyyy", ""));
123+
}
124+
74125
}
75126
}

0 commit comments

Comments
 (0)