Skip to content

Commit 32f2db6

Browse files
Import-DbaCsv - Fix Culture+DateTimeFormats on Linux/ICU (issue #10338)
Root cause: DateTime.ParseExact treats unescaped ':' in format strings as a placeholder for the culture's TimeSeparator. On Linux with ICU, de-CH has TimeSeparator='.' so 'HH:mm:ss' would expect '17.09.41' but CSV data uses '17:09:41', causing a CSV parse error. Also avoided using $dtf.ShortDatePattern/$dtf.LongTimePattern directly as these differ between Windows (NLS) and Linux (ICU). Instead derive from stable primitives: DateSeparator + date-order from ShortDatePattern. Fix: escape time colons as HH':'mm':'ss for literal ':' matching, and construct date pattern from DateSeparator + detected day/month order. (do Import-DbaCsv) Co-authored-by: Andreas Jordan <andreasjordan@users.noreply.github.com>
1 parent 1cc609b commit 32f2db6

1 file changed

Lines changed: 29 additions & 9 deletions

File tree

public/Import-DbaCsv.ps1

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -604,20 +604,40 @@ function Import-DbaCsv {
604604
}
605605

606606
# When Culture is specified but DateTimeFormats is not, derive unambiguous datetime format
607-
# strings from the culture's ShortDatePattern only (purely numeric patterns).
608-
# This forces ParseExact (which respects field order) instead of Parse (which can swap
609-
# day/month when both values are <= 12).
610-
# NOTE: Only short patterns (no LongDatePattern with MMMM/dddd) are derived, because
611-
# locale-specific month/day name tokens combined with InvariantCulture can cause the
612-
# library's parser to throw and fall back to a broken Parse path.
607+
# strings that force ParseExact (which respects day/month field order) instead of
608+
# DateTime.Parse (which can swap day/month when both values are <= 12).
613609
# See: https://github.com/dataplat/dbatools/issues/10338
610+
#
611+
# Design notes:
612+
# 1. We do NOT use $dtf.ShortDatePattern or $dtf.LongTimePattern directly because these
613+
# properties differ between Windows (NLS) and Linux (ICU), causing platform-specific
614+
# format mismatches in CI. Instead we derive the format from stable culture primitives.
615+
# 2. Time colons are escaped as HH':'mm':'ss rather than HH:mm:ss. In DateTime.ParseExact,
616+
# an unescaped ':' is a placeholder for the culture's TimeSeparator property. For de-CH
617+
# on Linux/ICU the TimeSeparator is '.' not ':', so HH:mm:ss would expect "17.09.41"
618+
# and fail to match CSV data that uses the common "17:09:41" format. Escaping forces
619+
# a literal ':' match regardless of the culture's TimeSeparator.
614620
if ($PSBoundParameters.Culture -and -not $PSBoundParameters.DateTimeFormats) {
615621
$cultureObj = New-Object System.Globalization.CultureInfo($Culture)
616622
$dtf = $cultureObj.DateTimeFormat
623+
$dateSep = $dtf.DateSeparator
624+
625+
# Determine date field order from the first character of ShortDatePattern
626+
if ($dtf.ShortDatePattern -match '^y') {
627+
# Year-first cultures (e.g. yyyy-MM-dd)
628+
$datePattern = "yyyy${dateSep}MM${dateSep}dd"
629+
} elseif ($dtf.ShortDatePattern -match '^M') {
630+
# Month-first cultures (e.g. MM/dd/yyyy for en-US)
631+
$datePattern = "MM${dateSep}dd${dateSep}yyyy"
632+
} else {
633+
# Day-first cultures (e.g. dd.MM.yyyy for de-CH, de-DE, en-GB)
634+
$datePattern = "dd${dateSep}MM${dateSep}yyyy"
635+
}
636+
617637
$effectiveDateTimeFormats = @(
618-
"$($dtf.ShortDatePattern) $($dtf.LongTimePattern)",
619-
"$($dtf.ShortDatePattern) $($dtf.ShortTimePattern)",
620-
$dtf.ShortDatePattern
638+
"$datePattern HH':'mm':'ss",
639+
"$datePattern HH':'mm",
640+
$datePattern
621641
)
622642
Write-Message -Level Verbose -Message "Derived DateTimeFormats from Culture '$Culture': $($effectiveDateTimeFormats -join ', ')"
623643
} elseif ($PSBoundParameters.DateTimeFormats) {

0 commit comments

Comments
 (0)