Files
2026-09-15 15:44:13 +07:00

219 lines
7.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:hgdj/hj_utils/text_util.dart';
import 'package:hgdj/tools_base/net/net_manager.dart';
class DateTimeUtil {
/// 计算当前时间差值
static int calTime3(String? date) {
if (TextUtil.isEmpty(date) || date?.contains("0001-01-01") == true)
return -1;
final time = DateTime.parse(utc2iso(date)).toLocal();
return time.difference(DateTime.now()).inSeconds;
}
/// 日期时间本地格式化 yyyy-MM-dd HH:mm:ss
static String utc2iso(String? formattedString) {
if (formattedString == null || formattedString.isEmpty) return '';
try {
final dt = DateTime.parse(formattedString).toLocal();
return '${_fourDigits(dt.year)}-${_twoDigits(dt.month)}-${_twoDigits(dt.day)} '
'${_twoDigits(dt.hour)}:${_twoDigits(dt.minute)}:${_twoDigits(dt.second)}';
} catch (e) {
return '';
}
}
/// utc 转 MM月dd日
static String utc2isoMD(String? formattedString) {
if (formattedString == null || formattedString.isEmpty) return '';
try {
final dt = DateTime.parse(formattedString).toLocal();
return '${_twoDigits(dt.month)}${_twoDigits(dt.day)}日';
} catch (e) {
return '';
}
}
/// 2020-11-26T17:44:45.000Z
static String? format2utc(DateTime dateTime) {
return '${_fourDigits(dateTime.year)}-${_twoDigits(dateTime.month)}-${_twoDigits(dateTime.day)}'
'T${_twoDigits(dateTime.hour)}:${_twoDigits(dateTime.minute)}:${_twoDigits(dateTime.second)}'
'.${_threeDigits(dateTime.millisecond)}Z';
}
/// 4 位补零(年份),负数前置符号,例:5 → "0005"-5 → "-0005"
static String _fourDigits(int n) {
final absN = n.abs();
final sign = n < 0 ? '-' : '';
if (absN >= 1000) return '$n';
if (absN >= 100) return '${sign}0$absN';
if (absN >= 10) return '${sign}00$absN';
return '${sign}000$absN';
}
/// 3 位补零(毫秒),例:5 → "005"
static String _threeDigits(int n) {
if (n >= 100) return '$n';
if (n >= 10) return '0$n';
return '00$n';
}
/// 2 位补零(月/日/时/分/秒),例:5 → "05"
static String _twoDigits(int n) {
if (n >= 10) return '$n';
return '0$n';
}
/// utc 转 年月日
static String utcTurnYear(String? date,
{String? char, bool? isChina, bool showHM = false}) {
if (date == null || date.isEmpty) return '';
final gap = char ?? '-';
final dt = DateTime.parse(date).toLocal();
final y = _fourDigits(dt.year);
final m = _twoDigits(dt.month);
final d = _twoDigits(dt.day);
if (isChina == true) return '$y年$m月$d日';
if (showHM == true) {
return '$y$gap$m$gap$d ${_twoDigits(dt.hour)}:${_twoDigits(dt.minute)}';
}
return '$y$gap$m$gap$d ';
}
static String utc3YearMonthDay(DateTime dateTime) {
try {
return '${_twoDigits(dateTime.year)}-${_twoDigits(dateTime.month)}-${_twoDigits(dateTime.day)} ';
} catch (e) {
return '';
}
}
/// 计算字符串时间差值
static String? calTimediffFromNow(String? date) {
final time = DateTime.parse(date ?? '').toLocal();
final difference = time.difference(DateTime.now());
final day = difference.inDays;
final hours = difference.inHours % 24;
return '$day天$hours小时';
}
/// 秒 → MM:SS / HH:MM:SS(小时补零)。[alwaysHour] 为 true 时不足 1 小时也带小时位(00:MM:SS)
/// 时长格式化的唯一入口,外部各处统一走这里
static String formatHMS(int seconds, {bool alwaysHour = false}) {
if (seconds < 0) seconds = 0;
final h = seconds ~/ 3600;
final m = (seconds % 3600) ~/ 60;
final s = seconds % 60;
final ms = '${_twoDigits(m)}:${_twoDigits(s)}';
return (h == 0 && !alwaysHour) ? ms : '${_twoDigits(h)}:$ms';
}
/// 时长 → MM:SS / HH:MM:SS(统一走 [formatHMS]
static String? formatDuration(Duration position) =>
formatHMS(position.inSeconds);
/// 秒 → (时, 分, 秒) 各两位补零字符串;时为总小时数(不取模 24),供分段倒计时 UI 用
static (String, String, String) hms(int seconds) => (
_twoDigits(seconds ~/ 3600),
_twoDigits((seconds ~/ 60) % 60),
_twoDigits(seconds % 60),
);
/// 这个时间是否**还没到期**(晚于服务器当前时间)。名字有歧义,别按字面理解成「已过期」。
/// 解析不了一律当没权益:这里被登录/刷新用户信息那条主路调用(见 GlobalStore._setMe)
/// 裸 DateTime.parse 遇到后端换格式会把整条链抛断,登录都进不去
static bool isExpireDate(String? time) {
if (time == null || time.isEmpty) return false;
final dt = DateTime.tryParse(time);
return dt != null && dt.isAfter(netManager.getFixedCurTime());
}
}
/// 构建分秒 MM:SS
String buildMMSS(int seconds) {
final minute = seconds ~/ 60;
final second = seconds % 60;
return '${_formatTime(minute)}:${_formatTime(second)}';
}
/// 数字格式化,将 0~9 的时间转换为 00~09
String _formatTime(int timeNum) => timeNum.toString().padLeft(2, '0');
const String kFormatTimeFallback = '—';
/// 最早可展示时间(早于该时间视为非正常)
final DateTime _kMinValidDateTime = DateTime(1970, 1, 1);
/// 非正常时间兜底:占位默认时间、过早/未来时间等不可用于展示
bool _isAbnormalDateTime(DateTime date, {DateTime? now}) {
if (date.year <= 1) return true;
if (date.isBefore(_kMinValidDateTime)) return true;
final reference = now ?? DateTime.now();
if (date.isAfter(reference)) return true;
return false;
}
/// 解析时间;空值、解析失败、非正常时间返回 null
DateTime? _parseFormatTimeDate(String? utcTime) {
if (utcTime == null || utcTime.trim().isEmpty) return null;
try {
final date = DateTime.parse(utcTime.trim()).toLocal();
if (_isAbnormalDateTime(date)) return null;
return date;
} catch (_) {
return null;
}
}
bool _isSameDay(DateTime a, DateTime b) =>
a.year == b.year && a.month == b.month && a.day == b.day;
/// 1 小时内 →「X分钟前」;当天 →「HH:mm」;其余 →「MM-DD HH:mm」;非正常时间 →「—」
String formatTime(String? utcTime,
{String gap = '-', bool hasTimeGap = false}) {
final date = _parseFormatTimeDate(utcTime);
if (date == null) return kFormatTimeFallback;
final now = DateTime.now();
final diff = now.difference(date);
if (diff.inHours < 1) {
final minutes = diff.inMinutes;
return minutes < 1 ? '1分钟前' : '$minutes分钟前';
}
if (_isSameDay(date, now)) {
return '${DateTimeUtil._twoDigits(date.hour)}:${DateTimeUtil._twoDigits(date.minute)}';
}
return '${DateTimeUtil._twoDigits(date.month)}$gap${DateTimeUtil._twoDigits(date.day)} '
'${DateTimeUtil._twoDigits(date.hour)}:${DateTimeUtil._twoDigits(date.minute)}';
}
String formatTimeTwo(String? utcTime) {
if (utcTime?.isNotEmpty != true) return '近期';
final now = DateTime.now();
final date = DateTime.parse(utcTime!).toLocal();
final changeTime =
(now.millisecondsSinceEpoch - date.millisecondsSinceEpoch) ~/ 1000;
final s = changeTime;
if (s >= 0 && s < 60) return '刚刚';
final m = s ~/ 60;
if (m > 0 && m < 60) return '$m分钟前';
final h = m ~/ 60;
if (h > 0 && h < 24) return '$h小时前';
final d = h ~/ 24;
if (d > 0 && d < 7) return '$d天前';
final w = d ~/ 7;
if (w > 0 && w < 4) return '$w周前';
final month = d ~/ 30;
if (month > 0 && month < 12) return DateTimeUtil.utc2isoMD(utcTime);
final year = month ~/ 12;
if (year > 0 && year < 12)
return DateTimeUtil.utcTurnYear(utcTime, isChina: true);
return '';
}