toDateAutoFormat method
Attempts to parse the date using a variety of known formats.
Returns a local DateTime for calendar-style inputs (e.g. yyyyMMdd,
MM/dd/yyyy, long month names) unless utc is true, in which case the
parsed value is converted to UTC.
Inputs that describe an instant (ISO strings with offsets/Z, HTTP-date with GMT,
or Unix epochs) preserve their UTC meaning. If utc is false, the result is
converted back to the local time zone.
Parsing Priority
- Unix Epoch: 9–10 digits (seconds) or 12–13 digits (milliseconds).
- ISO-8601 / RFC3339: Standard
DateTime.parse. - HTTP Date: RFC 7231 (e.g.,
EEE, dd MMM yyyy HH:mm:ss 'GMT'). - Ambiguous Numeric: Slashed dates (e.g.,
MM/dd/yyyy) resolved by locale. - Compact Numeric:
yyyyMMdd(8 digits),yyyyMMddHHmm(12 digits), etc. - Long Form:
MMMM d, yyyy, etc. - Time Only:
HH:mm:ss(defaults to today's date).
Implementation
DateTime toDateAutoFormat({
String? locale,
bool useCurrentLocale = false,
bool utc = false,
}) {
final raw = trim();
if (raw.isEmpty) {
throw const FormatException('Invalid or unsupported date format', '');
}
// Effective locale for ambiguous decisions.
final effectiveLocale =
locale ?? (useCurrentLocale ? Intl.getCurrentLocale() : null);
final isUS = (effectiveLocale ?? Intl.getCurrentLocale()).startsWith(
'en_US',
);
// 0) Unix epoch first (handles pure digits), with 12-digit disambiguation.
final unix = _tryParseUnix(raw);
if (unix != null) {
// _tryParseUnix returns a UTC DateTime.
return utc ? unix.toUtc() : unix.toLocal();
}
// 1) ISO/RFC3339
try {
final iso = DateTime.parse(raw);
return utc ? iso.toUtc() : iso;
} catch (_) {}
// 2) HTTP-date (RFC 7231 IMF-fixdate)
final http = _tryParseHttpDate(raw);
if (http != null) return utc ? http.toUtc() : http;
// Normalize variants we might want to try
final variants = <String>{
raw,
_normalize(raw),
raw.replaceAll('_', ' '),
}.where((s) => s.isNotEmpty).toList();
// 3) Slashed ambiguous numeric using intl (ensures formatter/parser symmetry)
// Try the preferred interpretation first, then the alternative.
final slashedCandidates = isUS
? const [
'MM/dd/yyyy HH:mm:ss',
'MM/dd/yyyy',
'dd/MM/yyyy HH:mm:ss',
'dd/MM/yyyy',
]
: const [
'dd/MM/yyyy HH:mm:ss',
'dd/MM/yyyy',
'MM/dd/yyyy HH:mm:ss',
'MM/dd/yyyy',
];
for (final fmt in slashedCandidates) {
for (final text in variants) {
final dt = _tryParseWith(fmt, text, effectiveLocale);
if (dt != null) return utc ? dt.toUtc() : dt;
}
}
// 4) Compact numeric (yyyyMMdd[HHmm[ss]]) and underscored/space variants
final compact = _tryParseCompactDate(raw, utc: utc);
if (compact != null) return compact;
// 5) Long/alpha forms via intl
const intlPatterns = <String>[
"EEEE, MMMM d, yyyy 'at' h:mm a",
'EEEE, MMMM d, yyyy',
'MMMM d, yyyy h:mm a',
'MMMM d, yyyy',
'd MMMM yyyy HH:mm:ss',
'd MMMM yyyy',
// Safety net
'yyyy-MM-dd HH:mm:ss',
];
for (final fmt in intlPatterns) {
for (final text in variants) {
final dt = _tryParseWith(fmt, text, effectiveLocale);
if (dt != null) return utc ? dt.toUtc() : dt;
}
}
// 6) Time-only → today (local)
for (final fmt in ['HH:mm:ss', 'HH:mm', 'hh:mm:ss a', 'hh:mm a']) {
for (final text in variants) {
final t = _tryParseWith(fmt, text, effectiveLocale);
if (t != null) {
final now = DateTime.now();
final today = DateTime(
now.year,
now.month,
now.day,
t.hour,
t.minute,
t.second,
t.millisecond,
t.microsecond,
);
return utc ? today.toUtc() : today;
}
}
}
throw FormatException('Invalid or unsupported date format', raw);
}