Skip to main content
Glama
time.js13.9 kB
/** * 时间工具模块 * 提供时间格式化、时区转换和日期计算功能 */ import { logger } from './logger.js'; /** * 时间格式枚举 */ export const TimeFormat = { ISO: 'YYYY-MM-DDTHH:mm:ss.SSSZ', DATE: 'YYYY-MM-DD', TIME: 'HH:mm:ss', DATETIME: 'YYYY-MM-DD HH:mm:ss', DATETIME_MS: 'YYYY-MM-DD HH:mm:ss.SSS', FILENAME: 'YYYY-MM-DD_HH-mm-ss', HUMAN_READABLE: 'YYYY年MM月DD日 HH:mm:ss', SHORT_DATE: 'MM-DD', SHORT_TIME: 'HH:mm' }; /** * 时区枚举 */ export const Timezone = { UTC: 'UTC', BEIJING: 'Asia/Shanghai', TOKYO: 'Asia/Tokyo', NEW_YORK: 'America/New_York', LONDON: 'Europe/London', PARIS: 'Europe/Paris', SYDNEY: 'Australia/Sydney' }; /** * 时间工具类 */ export class TimeUtils { constructor() { this.defaultTimezone = Timezone.BEIJING; this.defaultFormat = TimeFormat.DATETIME; } /** * 获取当前时间戳(毫秒) * @returns {number} 时间戳 */ static now() { return Date.now(); } /** * 获取当前时间戳(秒) * @returns {number} 时间戳 */ static nowSeconds() { return Math.floor(Date.now() / 1000); } /** * 格式化时间 * @param {Date|string|number} date - 日期对象、日期字符串或时间戳 * @param {string} format - 格式字符串 * @param {string} timezone - 时区 * @returns {string} 格式化后的时间字符串 */ static format(date, format = TimeFormat.DATETIME, timezone = Timezone.BEIJING) { try { let targetDate; if (date instanceof Date) { targetDate = date; } else if (typeof date === 'string') { targetDate = new Date(date); } else if (typeof date === 'number') { // 判断是秒还是毫秒 targetDate = new Date(date < 1e10 ? date * 1000 : date); } else { targetDate = new Date(); } if (isNaN(targetDate.getTime())) { throw new Error('无效的日期'); } // 转换时区 if (timezone && timezone !== 'UTC') { targetDate = this.convertTimezone(targetDate, timezone); } return this._formatDate(targetDate, format); } catch (error) { logger.error('时间格式化失败:', error); return ''; } } /** * 内部日期格式化 * @private * @param {Date} date - 日期对象 * @param {string} format - 格式字符串 * @returns {string} 格式化后的字符串 */ static _formatDate(date, format) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); const milliseconds = String(date.getMilliseconds()).padStart(3, '0'); return format .replace(/YYYY/g, year) .replace(/MM/g, month) .replace(/DD/g, day) .replace(/HH/g, hours) .replace(/mm/g, minutes) .replace(/ss/g, seconds) .replace(/SSS/g, milliseconds); } /** * 解析日期字符串 * @param {string} dateString - 日期字符串 * @param {string} format - 格式字符串(可选) * @returns {Date} 日期对象 */ static parse(dateString, format = null) { try { if (!dateString) { throw new Error('日期字符串不能为空'); } // 如果是时间戳字符串 if (/^\d+$/.test(dateString)) { const timestamp = parseInt(dateString); return new Date(timestamp < 1e10 ? timestamp * 1000 : timestamp); } // 如果是标准ISO格式 if (dateString.includes('T') || dateString.includes('Z')) { return new Date(dateString); } // 尝试直接解析 const date = new Date(dateString); if (!isNaN(date.getTime())) { return date; } throw new Error('无法解析日期字符串'); } catch (error) { logger.error('日期解析失败:', error); return null; } } /** * 转换时区 * @param {Date} date - 日期对象 * @param {string} timezone - 目标时区 * @returns {Date} 转换后的日期对象 */ static convertTimezone(date, timezone) { try { if (!date || !(date instanceof Date)) { throw new Error('无效的日期对象'); } // 简化的时区转换(实际项目中应该使用更完整的时区库) const timezoneOffsets = { 'Asia/Shanghai': 8, 'Asia/Tokyo': 9, 'America/New_York': -5, 'Europe/London': 0, 'Europe/Paris': 1, 'Australia/Sydney': 11 }; const offset = timezoneOffsets[timezone] || 0; const utc = date.getTime() + (date.getTimezoneOffset() * 60000); const targetTime = utc + (3600000 * offset); return new Date(targetTime); } catch (error) { logger.error('时区转换失败:', error); return date; } } /** * 添加时间 * @param {Date|string|number} date - 基准时间 * @param {number} amount - 数量 * @param {string} unit - 单位 ('ms', 's', 'm', 'h', 'd', 'w', 'M', 'y') * @returns {Date} 新的日期对象 */ static addTime(date, amount, unit = 'ms') { try { const targetDate = this.parse(date) || new Date(); const timeInMs = this._convertToMilliseconds(amount, unit); return new Date(targetDate.getTime() + timeInMs); } catch (error) { logger.error('时间添加失败:', error); return null; } } /** * 减去时间 * @param {Date|string|number} date - 基准时间 * @param {number} amount - 数量 * @param {string} unit - 单位 ('ms', 's', 'm', 'h', 'd', 'w', 'M', 'y') * @returns {Date} 新的日期对象 */ static subtractTime(date, amount, unit = 'ms') { return this.addTime(date, -amount, unit); } /** * 转换时间单位到毫秒 * @private * @param {number} amount - 数量 * @param {string} unit - 单位 * @returns {number} 毫秒数 */ static _convertToMilliseconds(amount, unit) { const multipliers = { 'ms': 1, 's': 1000, 'm': 60 * 1000, 'h': 60 * 60 * 1000, 'd': 24 * 60 * 60 * 1000, 'w': 7 * 24 * 60 * 60 * 1000, 'M': 30 * 24 * 60 * 60 * 1000, // 近似值 'y': 365 * 24 * 60 * 60 * 1000 // 近似值 }; return amount * (multipliers[unit] || 1); } /** * 计算时间差 * @param {Date|string|number} startDate - 开始时间 * @param {Date|string|number} endDate - 结束时间 * @param {string} unit - 返回单位 ('ms', 's', 'm', 'h', 'd') * @returns {number} 时间差 */ static diff(startDate, endDate, unit = 'ms') { try { const start = this.parse(startDate); const end = this.parse(endDate) || new Date(); if (!start || !end) { throw new Error('无效的日期'); } const diffMs = end.getTime() - start.getTime(); const divisor = this._convertToMilliseconds(1, unit); return Math.floor(diffMs / divisor); } catch (error) { logger.error('时间差计算失败:', error); return 0; } } /** * 获取时间范围 * @param {Date|string|number} date - 基准时间 * @param {string} range - 范围类型 ('day', 'week', 'month', 'year') * @returns {Object} 包含开始和结束时间的对象 */ static getTimeRange(date, range = 'day') { try { const targetDate = this.parse(date) || new Date(); let start, end; switch (range) { case 'day': start = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()); end = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate() + 1); break; case 'week': const dayOfWeek = targetDate.getDay(); const startOfWeek = new Date(targetDate); startOfWeek.setDate(targetDate.getDate() - dayOfWeek); start = new Date(startOfWeek.getFullYear(), startOfWeek.getMonth(), startOfWeek.getDate()); const endOfWeek = new Date(start); endOfWeek.setDate(start.getDate() + 7); end = endOfWeek; break; case 'month': start = new Date(targetDate.getFullYear(), targetDate.getMonth(), 1); end = new Date(targetDate.getFullYear(), targetDate.getMonth() + 1, 1); break; case 'year': start = new Date(targetDate.getFullYear(), 0, 1); end = new Date(targetDate.getFullYear() + 1, 0, 1); break; default: throw new Error(`不支持的时间范围类型: ${range}`); } return { start, end }; } catch (error) { logger.error('获取时间范围失败:', error); return { start: null, end: null }; } } /** * 判断是否为工作日 * @param {Date|string|number} date - 日期 * @returns {boolean} 是否为工作日 */ static isWeekday(date) { try { const targetDate = this.parse(date); if (!targetDate) return false; const dayOfWeek = targetDate.getDay(); return dayOfWeek >= 1 && dayOfWeek <= 5; // 周一到周五 } catch (error) { logger.error('工作日判断失败:', error); return false; } } /** * 判断是否为周末 * @param {Date|string|number} date - 日期 * @returns {boolean} 是否为周末 */ static isWeekend(date) { return !this.isWeekday(date); } /** * 获取年龄 * @param {Date|string|number} birthDate - 出生日期 * @param {Date|string|number} currentDate - 当前日期(可选) * @returns {number} 年龄 */ static getAge(birthDate, currentDate = null) { try { const birth = this.parse(birthDate); const current = this.parse(currentDate) || new Date(); if (!birth || !current) { throw new Error('无效的日期'); } let age = current.getFullYear() - birth.getFullYear(); const monthDiff = current.getMonth() - birth.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && current.getDate() < birth.getDate())) { age--; } return Math.max(0, age); } catch (error) { logger.error('年龄计算失败:', error); return 0; } } /** * 获取友好的时间描述 * @param {Date|string|number} date - 日期 * @param {Date|string|number} currentDate - 当前日期(可选) * @returns {string} 友好的时间描述 */ static getFriendlyTime(date, currentDate = null) { try { const targetDate = this.parse(date); const current = this.parse(currentDate) || new Date(); if (!targetDate) return '无效时间'; const diffMs = current.getTime() - targetDate.getTime(); const diffSeconds = Math.floor(diffMs / 1000); const diffMinutes = Math.floor(diffSeconds / 60); const diffHours = Math.floor(diffMinutes / 60); const diffDays = Math.floor(diffHours / 24); if (diffSeconds < 60) { return '刚刚'; } else if (diffMinutes < 60) { return `${diffMinutes}分钟前`; } else if (diffHours < 24) { return `${diffHours}小时前`; } else if (diffDays < 7) { return `${diffDays}天前`; } else if (diffDays < 30) { const weeks = Math.floor(diffDays / 7); return `${weeks}周前`; } else if (diffDays < 365) { const months = Math.floor(diffDays / 30); return `${months}个月前`; } else { const years = Math.floor(diffDays / 365); return `${years}年前`; } } catch (error) { logger.error('友好时间描述生成失败:', error); return '未知时间'; } } /** * 创建定时器 * @param {Function} callback - 回调函数 * @param {number} interval - 间隔时间(毫秒) * @param {boolean} immediate - 是否立即执行 * @returns {Object} 定时器对象 */ static createTimer(callback, interval, immediate = false) { let timerId = null; let isRunning = false; const execute = async () => { if (isRunning) return; isRunning = true; try { await callback(); } catch (error) { logger.error('定时器执行失败:', error); } finally { isRunning = false; } }; const start = () => { if (immediate) { execute(); } timerId = setInterval(execute, interval); }; const stop = () => { if (timerId) { clearInterval(timerId); timerId = null; } }; return { start, stop, isRunning: () => timerId !== null }; } } /** * 快捷函数 */ export const formatTime = TimeUtils.format; export const parseTime = TimeUtils.parse; export const addTime = TimeUtils.addTime; export const subtractTime = TimeUtils.subtractTime; export const timeDiff = TimeUtils.diff; export const getTimeRange = TimeUtils.getTimeRange; export const isWeekday = TimeUtils.isWeekday; export const isWeekend = TimeUtils.isWeekend; export const getAge = TimeUtils.getAge; export const getFriendlyTime = TimeUtils.getFriendlyTime; export const createTimer = TimeUtils.createTimer; export default { TimeUtils, TimeFormat, Timezone, formatTime, parseTime, addTime, subtractTime, timeDiff, getTimeRange, isWeekday, isWeekend, getAge, getFriendlyTime, createTimer };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/billyangbc/xiaohongshu-mcp-nodejs'

If you have feedback or need assistance with the MCP directory API, please join our Discord server