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
3 changes: 2 additions & 1 deletion Jint/Native/Intl/IntlUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,8 @@ private static string RemoveUnicodeExtensions(string locale)
// Use new CultureInfo() instead of GetCultureInfo() to get correct locale-specific
// data. GetCultureInfo returns a cached read-only culture that may have different
// NumberFormatInfo patterns (e.g., CurrencyNegativePattern).
return new CultureInfo(cultureTag);
// useUserOverride: false ensures consistent behavior regardless of Windows regional settings.
return new CultureInfo(cultureTag, false);
}
catch (CultureNotFoundException)
{
Expand Down
82 changes: 37 additions & 45 deletions Jint/Native/Intl/JsDateTimeFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,58 +465,48 @@ public LocaleDateFormatInfo(string dateOrder, string dateSeparator, bool hasText
}

/// <summary>
/// Determines the locale-specific date format order and separator.
/// Determines the locale-specific date format order and separator
/// by parsing the ShortDatePattern from DateTimeFormatInfo.
/// </summary>
private LocaleDateFormatInfo GetLocaleDateFormat()
{
// Check if month is textual (long, short, narrow) vs numeric
var hasTextualMonth = Month != null && Month is "long" or "short" or "narrow";

// Get locale's short date pattern to determine order
var lang = Locale.Split('-')[0].ToLowerInvariant();
var region = Locale.Contains('-') ? Locale.Split('-')[1].ToUpperInvariant() : "";
// Derive date order and separator from the locale's ShortDatePattern (e.g., "dd-MM-yyyy", "M/d/yyyy")
var pattern = CultureInfo.DateTimeFormat.ShortDatePattern;
var dateOrder = ParseDateOrder(pattern);
var dateSeparator = hasTextualMonth ? " " : CultureInfo.DateTimeFormat.DateSeparator;

// Determine date component order based on locale
// MDY: en-US, en-CA (Canada uses MDY in English), fil (Philippines)
// DMY: Most of Europe, UK, Australia, most of world
// YMD: East Asia (China, Japan, Korea), Lithuania, Hungary
string dateOrder;
if (string.Equals(lang, "en", StringComparison.Ordinal) &&
(string.Equals(region, "US", StringComparison.Ordinal) ||
string.Equals(region, "", StringComparison.Ordinal)))
{
// en-US and plain "en" use MDY
dateOrder = "Mdy";
}
else if (string.Equals(lang, "zh", StringComparison.Ordinal) ||
string.Equals(lang, "ja", StringComparison.Ordinal) ||
string.Equals(lang, "ko", StringComparison.Ordinal) ||
string.Equals(lang, "hu", StringComparison.Ordinal) ||
string.Equals(lang, "lt", StringComparison.Ordinal))
{
// East Asian languages and Hungarian/Lithuanian use YMD
dateOrder = "yMd";
}
else
{
// Most of the world uses DMY
dateOrder = "dMy";
}
return new LocaleDateFormatInfo(dateOrder, dateSeparator, hasTextualMonth);
}

// Determine separator based on whether month is textual
string dateSeparator;
if (hasTextualMonth)
{
// Textual month uses space separators: "Jan 3, 2019"
dateSeparator = " ";
}
else
/// <summary>
/// Parses a .NET ShortDatePattern to extract the date component order (e.g., "dMy", "Mdy", "yMd").
/// </summary>
private static string ParseDateOrder(string pattern)
{
var order = new StringBuilder(3);
foreach (var c in pattern)
{
// Numeric format uses "/" for en-US, varies by locale
dateSeparator = "/";
var component = char.ToLowerInvariant(c) switch
{
'd' => 'd',
'm' => 'M',
'y' => 'y',
_ => '\0'
};

if (component != '\0' && (order.Length == 0 || order[order.Length - 1] != component))
{
order.Append(component);
if (order.Length == 3)
{
break;
}
}
}

return new LocaleDateFormatInfo(dateOrder, dateSeparator, hasTextualMonth);
return order.Length == 3 ? order.ToString() : "dMy"; // fallback to DMY
}

private void AddMonthPart(DateTime dateTime, List<DateTimePart> result, ref bool hasDate, string separator, bool hasTextualMonth, ChineseCalendarHelper.ChineseCalendarDate? lunisolarDate = null)
Expand Down Expand Up @@ -1198,9 +1188,11 @@ private string BuildFormatString(List<string> parts)
}
else
{
// Numeric format: "1/3/2019"
// Use literal '/' by escaping with single quotes to avoid .NET's culture-specific date separator
result.Append("'/'");
// Numeric format: use locale-specific date separator
var sep = CultureInfo.DateTimeFormat.DateSeparator;
result.Append('\'');
result.Append(sep);
result.Append('\'');
}
}
}
Expand Down