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 ''; }