mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 22:51:25 +08:00
514 lines
15 KiB
JavaScript
514 lines
15 KiB
JavaScript
const time = {
|
|
// 解析时间字符串或时间戳为 Date 对象
|
|
_parseTime: function (time) {
|
|
if (!time || time === "now") {
|
|
return new Date();
|
|
}
|
|
if (typeof time === "number") {
|
|
return new Date(time.toString().length === 10 ? time * 1000 : time);
|
|
}
|
|
return new Date(time);
|
|
},
|
|
|
|
// 格式化数字为两位数
|
|
_pad: function (number) {
|
|
return number.toString().padStart(2, "0");
|
|
},
|
|
|
|
// 格式化时间
|
|
format: function (time, format) {
|
|
const date = this._parseTime(time);
|
|
if (!date.getTime()) return "";
|
|
|
|
const year = date.getFullYear();
|
|
const month = this._pad(date.getMonth() + 1);
|
|
const day = this._pad(date.getDate());
|
|
const hours = this._pad(date.getHours());
|
|
const minutes = this._pad(date.getMinutes());
|
|
const seconds = this._pad(date.getSeconds());
|
|
|
|
switch (format) {
|
|
case "YYYY-MM-DD":
|
|
return `${year}-${month}-${day}`;
|
|
case "YYYY-MM-DD HH:mm:ss":
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
case "YYYY年MM月DD日":
|
|
return `${year}年${month}月${day}日`;
|
|
case "MM/DD/YYYY":
|
|
return `${month}/${day}/${year}`;
|
|
case "DD/MM/YYYY":
|
|
return `${day}/${month}/${year}`;
|
|
case "HH:mm:ss":
|
|
return `${hours}:${minutes}:${seconds}`;
|
|
case "YYYY-MM-DD HH:mm":
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
case "timestamp":
|
|
return Math.floor(date.getTime() / 1000);
|
|
case "timestamp_ms":
|
|
return date.getTime();
|
|
case "relative":
|
|
return this._getRelativeTime(date);
|
|
default:
|
|
return date.toLocaleString();
|
|
}
|
|
},
|
|
|
|
// 获取相对时间描述
|
|
_getRelativeTime: function (date) {
|
|
const now = new Date();
|
|
const diff = now - date;
|
|
const seconds = Math.floor(diff / 1000);
|
|
const minutes = Math.floor(seconds / 60);
|
|
const hours = Math.floor(minutes / 60);
|
|
const days = Math.floor(hours / 24);
|
|
const months = Math.floor(days / 30);
|
|
const years = Math.floor(days / 365);
|
|
|
|
if (seconds < 60) return "刚刚";
|
|
if (minutes < 60) return `${minutes}分钟前`;
|
|
if (hours < 24) return `${hours}小时前`;
|
|
if (days < 30) return `${days}天前`;
|
|
if (months < 12) return `${months}个月前`;
|
|
return `${years}年前`;
|
|
},
|
|
|
|
// 解析时间
|
|
parse: function (time, format) {
|
|
if (!time) return null;
|
|
|
|
const now = new Date();
|
|
let year = now.getFullYear();
|
|
let month = now.getMonth() + 1; // 转换为正常月份
|
|
let day = now.getDate();
|
|
let hours = 0;
|
|
let minutes = 0;
|
|
let seconds = 0;
|
|
|
|
// 处理时间戳
|
|
if (format === "timestamp") {
|
|
const date = new Date(Number(time) * 1000);
|
|
return this._formatTimeObject(date);
|
|
}
|
|
if (format === "timestamp_ms") {
|
|
const date = new Date(Number(time));
|
|
return this._formatTimeObject(date);
|
|
}
|
|
|
|
// 如果没有指定格式,尝试自动识别
|
|
if (!format) {
|
|
// 尝试直接解析
|
|
const date = new Date(time);
|
|
if (date.getTime()) {
|
|
return this._formatTimeObject(date);
|
|
}
|
|
|
|
// 尝试解析常见格式
|
|
const patterns = {
|
|
// 标准格式
|
|
"YYYY-MM-DD HH:mm:ss":
|
|
/^(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/,
|
|
"YYYY-MM-DD HH:mm": /^(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2})$/,
|
|
"YYYY-MM-DD": /^(\d{4})-(\d{1,2})-(\d{1,2})$/,
|
|
// 中文格式
|
|
"YYYY年MM月DD日 HH时mm分ss秒":
|
|
/^(\d{4})年(\d{1,2})月(\d{1,2})日 (\d{1,2})时(\d{1,2})分(\d{1,2})秒$/,
|
|
"YYYY年MM月DD日 HH:mm:ss":
|
|
/^(\d{4})年(\d{1,2})月(\d{1,2})日 (\d{1,2}):(\d{1,2}):(\d{1,2})$/,
|
|
YYYY年MM月DD日: /^(\d{4})年(\d{1,2})月(\d{1,2})日$/,
|
|
// 斜杠格式
|
|
"MM/DD/YYYY HH:mm:ss":
|
|
/^(\d{1,2})\/(\d{1,2})\/(\d{4}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/,
|
|
"DD/MM/YYYY HH:mm:ss":
|
|
/^(\d{1,2})\/(\d{1,2})\/(\d{4}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/,
|
|
"MM/DD/YYYY": /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
|
|
"DD/MM/YYYY": /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
|
|
// 点号格式
|
|
"DD.MM.YYYY": /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/,
|
|
// 时间格式
|
|
"HH:mm:ss": /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/,
|
|
"HH:mm": /^(\d{1,2}):(\d{1,2})$/,
|
|
};
|
|
|
|
for (const [patternFormat, regex] of Object.entries(patterns)) {
|
|
const matches = time.match(regex);
|
|
if (matches) {
|
|
format = patternFormat;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析时间字符串
|
|
const parseTimeString = (timeStr) => {
|
|
const parts = timeStr.split(/[: ]/);
|
|
return {
|
|
hours: parseInt(parts[0]) || 0,
|
|
minutes: parseInt(parts[1]) || 0,
|
|
seconds: parseInt(parts[2]) || 0,
|
|
};
|
|
};
|
|
|
|
// 根据格式解析
|
|
if (format) {
|
|
// 移除所有非数字和分隔符
|
|
const cleanTime = time.replace(/[^0-9/\-.: ]/g, "");
|
|
const parts = cleanTime.split(/[/\-.: ]/);
|
|
|
|
switch (format) {
|
|
case "YYYY-MM-DD":
|
|
case "YYYY-MM-DD HH:mm:ss":
|
|
case "YYYY-MM-DD HH:mm":
|
|
case "YYYY年MM月DD日":
|
|
case "YYYY年MM月DD日 HH:mm:ss":
|
|
case "YYYY年MM月DD日 HH时mm分ss秒":
|
|
year = parseInt(parts[0]);
|
|
month = parseInt(parts[1]);
|
|
day = parseInt(parts[2]);
|
|
if (parts.length > 3) {
|
|
const timeStr = parts.slice(3).join(":");
|
|
const timeObj = parseTimeString(timeStr);
|
|
hours = timeObj.hours;
|
|
minutes = timeObj.minutes;
|
|
seconds = timeObj.seconds;
|
|
}
|
|
break;
|
|
case "MM/DD/YYYY":
|
|
case "MM/DD/YYYY HH:mm:ss":
|
|
year = parseInt(parts[2]);
|
|
month = parseInt(parts[0]);
|
|
day = parseInt(parts[1]);
|
|
if (parts.length > 3) {
|
|
const timeStr = parts.slice(3).join(":");
|
|
const timeObj = parseTimeString(timeStr);
|
|
hours = timeObj.hours;
|
|
minutes = timeObj.minutes;
|
|
seconds = timeObj.seconds;
|
|
}
|
|
break;
|
|
case "DD/MM/YYYY":
|
|
case "DD/MM/YYYY HH:mm:ss":
|
|
case "DD.MM.YYYY":
|
|
year = parseInt(parts[2]);
|
|
month = parseInt(parts[1]);
|
|
day = parseInt(parts[0]);
|
|
if (parts.length > 3) {
|
|
const timeStr = parts.slice(3).join(":");
|
|
const timeObj = parseTimeString(timeStr);
|
|
hours = timeObj.hours;
|
|
minutes = timeObj.minutes;
|
|
seconds = timeObj.seconds;
|
|
}
|
|
break;
|
|
case "HH:mm:ss":
|
|
case "HH:mm":
|
|
const timeObj = parseTimeString(cleanTime);
|
|
hours = timeObj.hours;
|
|
minutes = timeObj.minutes;
|
|
seconds = timeObj.seconds;
|
|
break;
|
|
default:
|
|
// 尝试使用原生解析
|
|
const date = new Date(time);
|
|
if (date.getTime()) {
|
|
year = date.getFullYear();
|
|
month = date.getMonth() + 1;
|
|
day = date.getDate();
|
|
hours = date.getHours();
|
|
minutes = date.getMinutes();
|
|
seconds = date.getSeconds();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 验证日期是否有效
|
|
const testDate = new Date(year, month - 1, day, hours, minutes, seconds);
|
|
if (!testDate.getTime()) return null;
|
|
|
|
return this._formatTimeObject(testDate);
|
|
},
|
|
|
|
// 格式化时间对象
|
|
_formatTimeObject: function (date) {
|
|
const year = date.getFullYear();
|
|
const month = date.getMonth() + 1;
|
|
const day = date.getDate();
|
|
const hours = date.getHours();
|
|
const minutes = date.getMinutes();
|
|
const seconds = date.getSeconds();
|
|
|
|
return {
|
|
date: {
|
|
year,
|
|
month,
|
|
day,
|
|
},
|
|
time: {
|
|
hours,
|
|
minutes,
|
|
seconds,
|
|
},
|
|
formats: {
|
|
// 标准格式
|
|
iso: date.toISOString(),
|
|
locale: date.toLocaleString(),
|
|
localeDate: date.toLocaleDateString(),
|
|
localeTime: date.toLocaleTimeString(),
|
|
// 常用格式
|
|
"YYYY-MM-DD": `${year}-${this._pad(month)}-${this._pad(day)}`,
|
|
"YYYY-MM-DD HH:mm:ss": `${year}-${this._pad(month)}-${this._pad(
|
|
day
|
|
)} ${this._pad(hours)}:${this._pad(minutes)}:${this._pad(seconds)}`,
|
|
dateCN: `${year}年${this._pad(month)}月${this._pad(day)}日`,
|
|
"MM/DD/YYYY": `${this._pad(month)}/${this._pad(day)}/${year}`,
|
|
"DD/MM/YYYY": `${this._pad(day)}/${this._pad(month)}/${year}`,
|
|
"HH:mm:ss": `${this._pad(hours)}:${this._pad(minutes)}:${this._pad(
|
|
seconds
|
|
)}`,
|
|
},
|
|
timestamp: Math.floor(date.getTime() / 1000),
|
|
timestamp_ms: date.getTime(),
|
|
// 日历信息
|
|
calendar: {
|
|
week: date.getDay(),
|
|
weekText: ["日", "一", "二", "三", "四", "五", "六"][date.getDay()],
|
|
isWeekend: date.getDay() === 0 || date.getDay() === 6,
|
|
isLeapYear: (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0,
|
|
daysInMonth: new Date(year, month, 0).getDate(),
|
|
constellation: this._getConstellation(month, day),
|
|
},
|
|
};
|
|
},
|
|
|
|
// 时间加减
|
|
add: function (time, value, unit) {
|
|
const date = this._parseTime(time);
|
|
if (!date.getTime()) return null;
|
|
|
|
const unitMap = {
|
|
years: "FullYear",
|
|
months: "Month",
|
|
weeks: "Date",
|
|
days: "Date",
|
|
hours: "Hours",
|
|
minutes: "Minutes",
|
|
seconds: "Seconds",
|
|
};
|
|
|
|
const methodName = `set${unitMap[unit]}`;
|
|
const getValue = `get${unitMap[unit]}`;
|
|
|
|
if (unit === "weeks") {
|
|
value *= 7;
|
|
}
|
|
|
|
date[methodName](date[getValue]() + value);
|
|
return date;
|
|
},
|
|
|
|
// 时间差值
|
|
diff: function (time1, time2, unit = "days", absolute = true) {
|
|
const date1 = this._parseTime(time1);
|
|
const date2 = this._parseTime(time2);
|
|
|
|
if (!date1.getTime() || !date2.getTime()) return null;
|
|
|
|
const milliseconds = date2 - date1;
|
|
let result;
|
|
|
|
switch (unit) {
|
|
case "years":
|
|
result = date2.getFullYear() - date1.getFullYear();
|
|
break;
|
|
case "months":
|
|
result =
|
|
(date2.getFullYear() - date1.getFullYear()) * 12 +
|
|
(date2.getMonth() - date1.getMonth());
|
|
break;
|
|
case "weeks":
|
|
result = milliseconds / (7 * 24 * 60 * 60 * 1000);
|
|
break;
|
|
case "days":
|
|
result = milliseconds / (24 * 60 * 60 * 1000);
|
|
break;
|
|
case "hours":
|
|
result = milliseconds / (60 * 60 * 1000);
|
|
break;
|
|
case "minutes":
|
|
result = milliseconds / (60 * 1000);
|
|
break;
|
|
case "seconds":
|
|
result = milliseconds / 1000;
|
|
break;
|
|
case "milliseconds":
|
|
result = milliseconds;
|
|
break;
|
|
default:
|
|
result = milliseconds / (24 * 60 * 60 * 1000);
|
|
}
|
|
|
|
return absolute ? Math.abs(result) : result;
|
|
},
|
|
|
|
// 时间边界
|
|
startOf: function (time, unit, type = "start") {
|
|
const date = this._parseTime(time);
|
|
if (!date.getTime()) return null;
|
|
|
|
const isEnd = type === "end";
|
|
|
|
switch (unit) {
|
|
case "year":
|
|
date.setMonth(isEnd ? 11 : 0, 1);
|
|
date.setHours(
|
|
isEnd ? 23 : 0,
|
|
isEnd ? 59 : 0,
|
|
isEnd ? 59 : 0,
|
|
isEnd ? 999 : 0
|
|
);
|
|
break;
|
|
case "month":
|
|
date.setDate(1);
|
|
if (isEnd) {
|
|
date.setMonth(date.getMonth() + 1, 0);
|
|
date.setHours(23, 59, 59, 999);
|
|
} else {
|
|
date.setHours(0, 0, 0, 0);
|
|
}
|
|
break;
|
|
case "week":
|
|
const day = date.getDay();
|
|
const diff = date.getDate() - day + (day === 0 ? -6 : 1);
|
|
date.setDate(diff);
|
|
if (isEnd) {
|
|
date.setDate(date.getDate() + 6);
|
|
date.setHours(23, 59, 59, 999);
|
|
} else {
|
|
date.setHours(0, 0, 0, 0);
|
|
}
|
|
break;
|
|
case "day":
|
|
date.setHours(
|
|
isEnd ? 23 : 0,
|
|
isEnd ? 59 : 0,
|
|
isEnd ? 59 : 0,
|
|
isEnd ? 999 : 0
|
|
);
|
|
break;
|
|
case "hour":
|
|
date.setMinutes(isEnd ? 59 : 0, isEnd ? 59 : 0, isEnd ? 999 : 0);
|
|
break;
|
|
case "minute":
|
|
date.setSeconds(isEnd ? 59 : 0, isEnd ? 999 : 0);
|
|
break;
|
|
case "second":
|
|
date.setMilliseconds(isEnd ? 999 : 0);
|
|
break;
|
|
}
|
|
|
|
return date;
|
|
},
|
|
|
|
// 时间验证
|
|
isValid: function (time, format) {
|
|
if (!time) return false;
|
|
const date = this.parse(time, format);
|
|
return date && date.getTime() > 0;
|
|
},
|
|
|
|
// 日历信息
|
|
calendar: function (time) {
|
|
const date = this._parseTime(time);
|
|
if (!date.getTime()) return null;
|
|
|
|
const year = date.getFullYear();
|
|
const month = date.getMonth() + 1;
|
|
const day = date.getDate();
|
|
const week = date.getDay();
|
|
const weekText = ["日", "一", "二", "三", "四", "五", "六"][week];
|
|
|
|
// 星座计算
|
|
const constellation = this._getConstellation(month, day);
|
|
|
|
return {
|
|
year,
|
|
month,
|
|
day,
|
|
week,
|
|
weekText: `星期${weekText}`,
|
|
constellation,
|
|
isWeekend: week === 0 || week === 6,
|
|
isLeapYear: (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0,
|
|
daysInMonth: new Date(year, month, 0).getDate(),
|
|
timestamp: Math.floor(date.getTime() / 1000),
|
|
timestamp_ms: date.getTime(),
|
|
};
|
|
},
|
|
|
|
// 获取星座
|
|
_getConstellation: function (month, day) {
|
|
const constellations = [
|
|
"魔羯",
|
|
"水瓶",
|
|
"双鱼",
|
|
"白羊",
|
|
"金牛",
|
|
"双子",
|
|
"巨蟹",
|
|
"狮子",
|
|
"处女",
|
|
"天秤",
|
|
"天蝎",
|
|
"射手",
|
|
"魔羯",
|
|
];
|
|
const dates = [20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22];
|
|
return day < dates[month - 1]
|
|
? constellations[month - 1]
|
|
: constellations[month];
|
|
},
|
|
|
|
// 工作日计算
|
|
workday: function (startTime, days, weekends = false, holidays = []) {
|
|
const date = this._parseTime(startTime);
|
|
if (!date.getTime()) return null;
|
|
|
|
let currentDate = new Date(date);
|
|
let remainingDays = Math.abs(days);
|
|
const direction = days >= 0 ? 1 : -1;
|
|
const holidaySet = new Set(
|
|
holidays.map((h) => this.format(h, "YYYY-MM-DD"))
|
|
);
|
|
|
|
while (remainingDays > 0) {
|
|
currentDate.setDate(currentDate.getDate() + direction);
|
|
const isWeekend =
|
|
currentDate.getDay() === 0 || currentDate.getDay() === 6;
|
|
const isHoliday = holidaySet.has(this.format(currentDate, "YYYY-MM-DD"));
|
|
|
|
if ((!isWeekend || weekends) && !isHoliday) {
|
|
remainingDays--;
|
|
}
|
|
}
|
|
|
|
return currentDate;
|
|
},
|
|
|
|
// 时间范围判断
|
|
between: function (time, startTime, endTime, inclusive = true) {
|
|
const date = this._parseTime(time);
|
|
const start = this._parseTime(startTime);
|
|
const end = this._parseTime(endTime);
|
|
|
|
if (!date.getTime() || !start.getTime() || !end.getTime()) {
|
|
return false;
|
|
}
|
|
|
|
if (inclusive) {
|
|
return date >= start && date <= end;
|
|
}
|
|
return date > start && date < end;
|
|
},
|
|
};
|
|
|
|
module.exports = time;
|