219 lines
7.5 KiB
Dart
219 lines
7.5 KiB
Dart
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 '';
|
||
}
|