782cf057a3
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.9 KiB
Dart
71 lines
2.9 KiB
Dart
/// 日期工具:统一 `yyyy-MM-dd` 的格式化与解析,收敛各处重复的 padLeft 逻辑。
|
|
library;
|
|
|
|
/// 格式化为 `yyyy-MM-dd`(提交后端的统一格式)。
|
|
String formatYmd(DateTime d) =>
|
|
'${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
|
|
|
|
/// 解析 `yyyy-MM-dd`(或更长的带时间字符串,取前 10 位)。无效返回 null。
|
|
DateTime? parseYmd(String? s) {
|
|
if (s == null || s.isEmpty) return null;
|
|
final t = s.length >= 10 ? s.substring(0, 10) : s;
|
|
return DateTime.tryParse(t);
|
|
}
|
|
|
|
/// 安全截取日期字符串前 10 位(`yyyy-MM-dd`)。
|
|
///
|
|
/// null 或空串一律返回 null(视为「无值」,供上层「两值皆空不渲染」的逻辑判断);
|
|
/// 长度不足 10 原样返回。用于兜底后端 `Date` 零值序列化为 `""`(而非 null)、
|
|
/// 或历史导入数据缺失生产日期的场景,避免 `''.substring(0, 10)` 抛 RangeError。
|
|
String? shortDate(String? v) {
|
|
if (v == null || v.isEmpty) return null;
|
|
return v.length >= 10 ? v.substring(0, 10) : v;
|
|
}
|
|
|
|
/// 组合年/月/日为 `yyyy-MM-dd`,自动把超出当月的「日」clamp 回当月最大值
|
|
/// (如 2 月选 31 → 28/29)。任一为空返回 null。
|
|
String? composeYmd(int? year, int? month, int? day) {
|
|
if (year == null || month == null || day == null) return null;
|
|
final maxDay = DateTime(year, month + 1, 0).day; // 下月第 0 天 = 当月最后一天
|
|
final d = day > maxDay ? maxDay : day;
|
|
return formatYmd(DateTime(year, month, d));
|
|
}
|
|
|
|
/// 归一用户键入的日期为 `yyyy-MM-dd`。支持「2024」「2024-5」「2024-5-12」
|
|
/// 「20240512」「2024/05/12」等;缺月/日补 01(老酒只知年份,填 2024 即
|
|
/// 2024-01-01),超出当月的日 clamp 回当月最大值。无有效年(1900–2200)返回 null。
|
|
String? normalizeYmd(String raw) {
|
|
final s = raw.trim();
|
|
if (s.isEmpty) return null;
|
|
int? y, mo, d;
|
|
if (RegExp(r'^[0-9]+$').hasMatch(s)) {
|
|
// 纯数字串:yyyymmdd / yyyymm / yyyy
|
|
if (s.length >= 8) {
|
|
y = int.tryParse(s.substring(0, 4));
|
|
mo = int.tryParse(s.substring(4, 6));
|
|
d = int.tryParse(s.substring(6, 8));
|
|
} else if (s.length == 6) {
|
|
y = int.tryParse(s.substring(0, 4));
|
|
mo = int.tryParse(s.substring(4, 6));
|
|
} else {
|
|
y = int.tryParse(s);
|
|
}
|
|
} else {
|
|
final parts =
|
|
s.split(RegExp(r'[^0-9]+')).where((e) => e.isNotEmpty).toList();
|
|
if (parts.isNotEmpty) y = int.tryParse(parts[0]);
|
|
if (parts.length >= 2) mo = int.tryParse(parts[1]);
|
|
if (parts.length >= 3) d = int.tryParse(parts[2]);
|
|
}
|
|
if (y == null || y < 1900 || y > 2200) return null;
|
|
mo ??= 1;
|
|
d ??= 1;
|
|
if (mo < 1 || mo > 12) return null;
|
|
final maxDay = DateTime(y, mo + 1, 0).day;
|
|
if (d < 1) d = 1;
|
|
if (d > maxDay) d = maxDay;
|
|
return '${y.toString().padLeft(4, '0')}-'
|
|
'${mo.toString().padLeft(2, '0')}-'
|
|
'${d.toString().padLeft(2, '0')}';
|
|
}
|