formatUtils.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /**
  2. * 格式化方法
  3. * @param {Date|string|number} input - 输入时间,可以是Date对象、时间戳或ISO格式字符串
  4. * @param {string} [format='YYYY-MM-DD hh:mm:ss'] - 格式化字符串
  5. * @param {Object} [options] - 配置选项
  6. * @param {string} [options.timeZone='local'] - 时区,'local'或'UTC'
  7. * @param {string} [options.locale='zh-CN'] - 区域设置
  8. * @param {string} [options.defaultResult='-:-'] - 空值返回
  9. * @returns {string} 格式化后的时间字符串
  10. */
  11. export function formatTime(input, format = 'YYYY-MM-DD hh:mm:ss', options = {}) {
  12. const {
  13. timeZone = 'local',
  14. locale = 'zh-CN',
  15. defaultResult = '-:-'
  16. } = options;
  17. try {
  18. // 1. 参数校验
  19. if (input === null || input === undefined) {
  20. return defaultResult;
  21. }
  22. // 2. 转换为Date对象
  23. let date;
  24. if (input instanceof Date) {
  25. date = new Date(input);
  26. } else if (typeof input === 'number') {
  27. date = new Date(input);
  28. } else if (typeof input === 'string') {
  29. // 尝试解析ISO格式或常见字符串格式
  30. date = new Date(input);
  31. if (isNaN(date.getTime())) {
  32. // 尝试处理不带时区的格式
  33. const cleanedInput = input.replace(/-/g, '/').replace(/T/, ' ');
  34. date = new Date(cleanedInput);
  35. if (isNaN(date.getTime())) {
  36. throw new Error(`Invalid date string: ${input}`);
  37. }
  38. }
  39. } else {
  40. return defaultResult;
  41. }
  42. // 检查日期是否有效
  43. if (isNaN(date.getTime())) {
  44. return defaultResult;
  45. }
  46. // 3. 提取日期时间组件(根据时区选择方法)
  47. let year, month, day, hours, minutes, seconds, milliseconds, dayOfWeek;
  48. if (timeZone === 'UTC') {
  49. year = date.getUTCFullYear();
  50. month = date.getUTCMonth() + 1;
  51. day = date.getUTCDate();
  52. hours = date.getUTCHours();
  53. minutes = date.getUTCMinutes();
  54. seconds = date.getUTCSeconds();
  55. milliseconds = date.getUTCMilliseconds();
  56. dayOfWeek = date.getUTCDay();
  57. } else {
  58. year = date.getFullYear();
  59. month = date.getMonth() + 1;
  60. day = date.getDate();
  61. hours = date.getHours();
  62. minutes = date.getMinutes();
  63. seconds = date.getSeconds();
  64. milliseconds = date.getMilliseconds();
  65. dayOfWeek = date.getDay();
  66. }
  67. // 4. 定义替换规则
  68. const replacements = {
  69. YYYY: String(year).padStart(4, '0'),
  70. YY: String(year).slice(-2),
  71. MM: String(month).padStart(2, '0'),
  72. M: String(month),
  73. DD: String(day).padStart(2, '0'),
  74. D: String(day),
  75. hh: String(hours).padStart(2, '0'),
  76. h: String(hours),
  77. HH: String(hours % 12 || 12).padStart(2, '0'),
  78. H: String(hours % 12 || 12),
  79. mm: String(minutes).padStart(2, '0'),
  80. m: String(minutes),
  81. ss: String(seconds).padStart(2, '0'),
  82. s: String(seconds),
  83. SSS: String(milliseconds).padStart(3, '0'),
  84. A: hours < 12 ? 'AM' : 'PM',
  85. a: hours < 12 ? 'am' : 'pm',
  86. ddd: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][dayOfWeek],
  87. dddd: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][dayOfWeek],
  88. Q: Math.ceil((month) / 3),
  89. W: getWeekOfYear(date, timeZone),
  90. Z: timeZone === 'UTC' ? '+00:00' : formatTimeZoneOffset(date.getTimezoneOffset())
  91. };
  92. // 5. 执行替换
  93. let result = format;
  94. for (const [token, value] of Object.entries(replacements)) {
  95. result = result.replace(new RegExp(token, 'g'), value);
  96. }
  97. // 6. 处理本地化月份和星期名称
  98. if (result.includes('MMMM') || result.includes('MMM')) {
  99. const formatter = new Intl.DateTimeFormat(locale, {
  100. month: 'long',
  101. timeZone: timeZone === 'UTC' ? 'UTC' : undefined
  102. });
  103. const longMonth = formatter.format(date);
  104. result = result.replace(/MMMM/g, longMonth);
  105. const shortFormatter = new Intl.DateTimeFormat(locale, {
  106. month: 'short',
  107. timeZone: timeZone === 'UTC' ? 'UTC' : undefined
  108. });
  109. const shortMonth = shortFormatter.format(date);
  110. result = result.replace(/MMM/g, shortMonth);
  111. }
  112. if (result.includes('dddd') || result.includes('ddd')) {
  113. const formatter = new Intl.DateTimeFormat(locale, {
  114. weekday: 'long',
  115. timeZone: timeZone === 'UTC' ? 'UTC' : undefined
  116. });
  117. const longDay = formatter.format(date);
  118. result = result.replace(/dddd/g, longDay);
  119. const shortFormatter = new Intl.DateTimeFormat(locale, {
  120. weekday: 'short',
  121. timeZone: timeZone === 'UTC' ? 'UTC' : undefined
  122. });
  123. const shortDay = shortFormatter.format(date);
  124. result = result.replace(/ddd/g, shortDay);
  125. }
  126. return result;
  127. } catch (error) {
  128. console.error('Error formatting time:', error);
  129. return defaultResult;
  130. }
  131. }
  132. // 辅助函数:获取一年中的第几周
  133. function getWeekOfYear(date, timeZone = 'local') {
  134. const tempDate = new Date(date);
  135. const firstDayOfYear = new Date(tempDate.getFullYear(), 0, 1);
  136. if (timeZone === 'UTC') {
  137. // 使用UTC方法计算
  138. const utcDate = Date.UTC(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate());
  139. const utcFirstDay = Date.UTC(firstDayOfYear.getUTCFullYear(), 0, 1);
  140. const pastDaysOfYear = (utcDate - utcFirstDay) / 86400000;
  141. return Math.ceil((pastDaysOfYear + firstDayOfYear.getUTCDay() + 1) / 7);
  142. } else {
  143. // 使用本地方法计算
  144. const pastDaysOfYear = (tempDate - firstDayOfYear) / 86400000;
  145. return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
  146. }
  147. }
  148. // 辅助函数:格式化时区偏移
  149. function formatTimeZoneOffset(offset) {
  150. const sign = offset <= 0 ? '+' : '-';
  151. const absOffset = Math.abs(offset);
  152. const hours = Math.floor(absOffset / 60);
  153. const minutes = absOffset % 60;
  154. return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
  155. }
  156. /*
  157. // 基本格式
  158. console.log(formatTime(now)); // "2023-05-15 14:30:45"
  159. console.log(formatTime(now, 'YYYY/MM/DD')); // "2023/05/15"
  160. console.log(formatTime(now, 'hh:mm:ss.SSS')); // "14:30:45.123"
  161. // 12小时制
  162. console.log(formatTime(now, 'HH:mm:ss A')); // "02:30:45 PM"
  163. // 季度和周数
  164. console.log(formatTime(now, 'YYYY-Q W')); // "2023-2 20"
  165. // 星期和月份名称
  166. console.log(formatTime(now, 'dddd, MMMM D, YYYY')); // "Monday, May 15, 2023"
  167. // 不同区域设置
  168. console.log(formatTime(now, 'dddd, MMMM D, YYYY', { locale: 'zh-CN' })); // "星期一, 五月 15, 2023"
  169. // UTC时间
  170. console.log(formatTime(now, 'YYYY-MM-DD HH:mm:ss Z', { timeZone: 'UTC' })); // "2023-05-15 06:30:45 +00:00"
  171. // 不同输入类型
  172. console.log(formatTime(1673789445123)); // 时间戳
  173. console.log(formatTime('2023-01-15T14:30:45.123Z')); // ISO字符串
  174. console.log(formatTime('2023/01/15 14:30:45')); // 普通字符串
  175. */
  176. export function isAfterTodayStart(timeString) {
  177. if (!timeString || typeof timeString !== 'string') {
  178. return false;
  179. }
  180. // 尝试多种日期格式解析
  181. let inputDate;
  182. // 尝试标准格式
  183. inputDate = new Date(timeString);
  184. // 如果解析失败,尝试其他常见格式
  185. if (isNaN(inputDate.getTime())) {
  186. // 处理 YYYY-MM-DD 格式
  187. if (/^\d{4}-\d{2}-\d{2}$/.test(timeString)) {
  188. inputDate = new Date(timeString + 'T00:00:00');
  189. }
  190. // 处理 YYYY/MM/DD 格式
  191. else if (/^\d{4}\/\d{2}\/\d{2}$/.test(timeString)) {
  192. inputDate = new Date(timeString.replace(/\//g, '-') + 'T00:00:00');
  193. }
  194. // 处理时间戳
  195. else if (/^\d+$/.test(timeString)) {
  196. inputDate = new Date(parseInt(timeString));
  197. }
  198. }
  199. // 如果还是无效日期,返回false
  200. if (isNaN(inputDate.getTime())) {
  201. console.warn('无效的时间格式:', timeString);
  202. return false;
  203. }
  204. // 获取当前日期的零点时刻
  205. const now = new Date();
  206. const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
  207. return inputDate.getTime() > todayStart.getTime();
  208. }
  209. /**
  210. * 格式化人名
  211. * @param {string} name - 输入的人名
  212. * @returns {string} 格式化后的人名
  213. */
  214. export function formatName(name) {
  215. // 1. 输入验证
  216. if (name == null) return ''; // 处理null或undefined
  217. if (typeof name !== 'string') {
  218. // 尝试转换非字符串输入
  219. try {
  220. name = String(name);
  221. } catch (e) {
  222. return '';
  223. }
  224. }
  225. // 2. 去除首尾空白字符
  226. name = name.trim();
  227. // 3. 检查是否为空字符串
  228. if (name.length === 0) return '';
  229. // 4. 处理2个字符的情况(中文或英文)
  230. if (name.length === 2) {
  231. // 检查是否是有效字符(避免在符号或数字中间加空格)
  232. const isAllValidChars = /^[\p{L}\p{M}]{2}$/u.test(name);
  233. if (isAllValidChars) {
  234. return `${name[0]} ${name[1]}`;
  235. }
  236. }
  237. // 5. 返回其他情况的原名
  238. return name;
  239. }
  240. // 计算时间范围
  241. export function calculateTimeRange(activeTimeRange, timeRange) {
  242. const now = new Date();
  243. let startDate = new Date();
  244. let endDate = new Date();
  245. switch (activeTimeRange) {
  246. case 'week':
  247. startDate.setDate(now.getDate() - 7);
  248. break;
  249. case 'month':
  250. // 本月:当前月份的第一天到当前天
  251. startDate = new Date(now.getFullYear(), now.getMonth(), 1);
  252. endDate = new Date(now);
  253. break;
  254. case 'quarter':
  255. startDate.setDate(now.getDate() - 90);
  256. break;
  257. case 'year':
  258. // 本年:当前年份的第一天到当前天
  259. startDate = new Date(now.getFullYear(), 0, 1);
  260. endDate = new Date(now);
  261. break;
  262. case 'custom':
  263. // 自定义时间范围:需要从组件中获取
  264. // 假设组件有startDate和endDate属性
  265. startDate = new Date(timeRange[0]);
  266. endDate = new Date(timeRange[1]);
  267. break;
  268. default:
  269. startDate.setDate(now.getDate() - 7); // 默认近一周
  270. }
  271. // 格式化日期为YYYY-MM-DD格式
  272. const formatDate = (date) => {
  273. const year = date.getFullYear();
  274. const month = String(date.getMonth() + 1).padStart(2, '0');
  275. const day = String(date.getDate()).padStart(2, '0');
  276. return `${year}-${month}-${day}`;
  277. };
  278. return {
  279. startDate: formatDate(startDate),
  280. endDate: formatDate(endDate)
  281. }
  282. }