编辑代码


// 加年
function addYear(var1, years) {
    if(!Number.isInteger(years)) return "错误,years必须为整数";
    let dateComponent = parseDateComponents(var1)  //获取日期格式以及年月日时分秒数据
    if(dateComponent.msg) return "日期格式错误!";
    if(!dateComponent.format.includes("yyyy")) return "错误,日期必须包含年份";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setFullYear(date.getFullYear() + years)
    return recoverDateFormat(date, dateComponent.format); //返回原格式的日期
}

// 加月
function addMonth(var1,months) {
    if(!Number.isInteger(months)) return "错误,months必须为整数";
    let  dateComponent = parseDateComponents(var1)
    if(!dateComponent.format.includes("MM")) return "错误,日期必须包含月份";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setMonth(date.getMonth() + months)
    return recoverDateFormat(date, dateComponent.format)
}

// 加日
function addDay(var1,days) {
    if(!Number.isInteger(days)) return "错误,days必须为整数";
    let  dateComponent = parseDateComponents(var1)
    if(!dateComponent.format.includes("dd")) return "错误,日期中必须含有day数据";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setDate(date.getDate() + days)
    return recoverDateFormat(date, dateComponent.format)
}

// 加时
function addHour(var1,hours) {
    if(isNaN(hours)) return "错误,hours必须为数字";
    let  dateComponent = parseDateComponents(var1)
    if(!dateComponent.format.includes("HH")) return "错误,日期中必须含有时间";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setTime(date.getTime() + hours*60*60*1000)
    return recoverDateFormat(date, dateComponent.format)
}

// 加分钟
function addMinutes(var1,minutes) {
    if(isNaN(minutes)) return "错误,minutes必须为数字";
    let  dateComponent = parseDateComponents(var1)
    if(!dateComponent.format.includes("mm")) return "错误,日期中必须含有分钟数";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setTime(date.getTime() + minutes*60*1000)
    return recoverDateFormat(date, dateComponent.format)
}

// 加秒
function addSeconds(var1,seconds) {
    if(!Number.isInteger(seconds)) return "错误,seconds必须为整数";
    let  dateComponent = parseDateComponents(var1)
    if(!dateComponent.format.includes("ss")) return "错误,日期中必须含有秒数";

    let date = new Date(dateComponent.year, dateComponent.month-1, dateComponent.day, dateComponent.hour, dateComponent.minute, dateComponent.second)
    date.setTime(date.getTime() + seconds*1000)
    return recoverDateFormat(date, dateComponent.format)
}

//  求时间差(年)
function yearSpan(var1, var2) {
    // 要求日期格式一致
    dateComponent1 = parseDateComponents(var1);
    dateComponent2 = parseDateComponents(var2);
    if(dateComponent1.msg|| dateComponent2.msg|| dateComponent1.format!=dateComponent2.format)
        return "日期格式错误或不一致";

    date1 = new Date(dateComponent1.year, dateComponent1.month-1, dateComponent1.day, dateComponent1.hour, dateComponent1.minute, dateComponent1.second)
    date2 = new Date(dateComponent2.year, dateComponent2.month-1, dateComponent2.day, dateComponent2.hour, dateComponent2.minute, dateComponent2.second)
    // 让date1中是小日期,date2中是大日期
    if(date1 > date2) { [date1, date2] = [date2, date1] }
    yearDiff = date2.getFullYear() - date1.getFullYear();
    temp = new Date(date1);
    temp.setFullYear(date2.getFullYear());
    return date2 < temp ? yearDiff-1 : yearDiff;
}

// 求时间差(月)
function monthSpan(var1, var2){
    // 要求日期格式一致
    dateComponent1 = parseDateComponents(var1);
    dateComponent2 = parseDateComponents(var2);
    if(dateComponent1.msg|| dateComponent2.msg|| dateComponent1.format!=dateComponent2.format)
        return "日期格式错误或不一致";

    date1 = new Date(dateComponent1.year, dateComponent1.month-1, dateComponent1.day, dateComponent1.hour, dateComponent1.minute, dateComponent1.second)
    date2 = new Date(dateComponent2.year, dateComponent2.month-1, dateComponent2.day, dateComponent2.hour, dateComponent2.minute, dateComponent2.second)
    // 让date1中是小日期,date2中是大日期
    if(date1 > date2) { [date1, date2] = [date2, date1] }
    // 计算基础月份差(不考虑日和时间)
    let monthDiff = (date2.getFullYear() - date1.getFullYear()) * 12 
                + (date2.getMonth() - date1.getMonth());

    // 创建临时日期验证是否超出实际结束日期
    const tempDate = new Date(date1);
    tempDate.setMonth(tempDate.getMonth() + monthDiff);
    // 如果调整后的日期晚于实际结束日期,说明未满完整月份
    return date2 < tempDate ? --monthDiff : monthDiff;
}

// 求时间差(天)
function daySpan(var1, var2){
    dateComponent1 = parseDateComponents(var1);
    dateComponent2 = parseDateComponents(var2);
    // ==========还需做合法性判断=================

    date1 = new Date(dateComponent1.year, dateComponent1.month-1, dateComponent1.day, dateComponent1.hour, dateComponent1.minute, dateComponent1.second)
    date2 = new Date(dateComponent2.year, dateComponent2.month-1, dateComponent2.day, dateComponent2.hour, dateComponent2.minute, dateComponent2.second)
    // 让date1中是小日期,date2中是大日期
    if(date1 > date2) { [date1, date2] = [date2, date1] }
    return Math.floor((date2 - date1)/(1000*60*60*24));
}

// 求时间差(分钟)
function minuteSpan(var1, var2){
    dateComponent1 = parseDateComponents(var1);
    dateComponent2 = parseDateComponents(var2);
    // ==========还需做合法性判断=================

    date1 = new Date(dateComponent1.year, dateComponent1.month-1, dateComponent1.day, dateComponent1.hour, dateComponent1.minute, dateComponent1.second)
    date2 = new Date(dateComponent2.year, dateComponent2.month-1, dateComponent2.day, dateComponent2.hour, dateComponent2.minute, dateComponent2.second)
    // 让date1中是小日期,date2中是大日期
    if(date1 > date2) { [date1, date2] = [date2, date1] }
    return Math.floor((date2 - date1)/(1000*60));
}

// 比较日期时间大小(支持未知时间)
function compareTime(var1, var2) {
    if( isEmpty(var1) && isEmpty(var2) ) return 0;
    if( isEmpty(var1) || isEmpty(var2) ) throw "比较日期大小时,某个日期为空";
    const validFormat = ["yyyy-MM-dd HH:mm:ss","yyyy-MM-dd HH:mm","yyyy-MM-dd HH","yyyy-MM-dd","yyyy-MM","yyyy","HH:mm:ss","HH:mm"];
    dateComponent1 = parseDateComponents(var1);
    dateComponent2 = parseDateComponents(var2);
    if(!validFormat.includes(dateComponent1.format) || !validFormat.includes(dateComponent2.format) || dateComponent1.format!=dateComponent2.format)
        throw "日期格式错误或格式不一致";
    date1 = new Date(dateComponent1.year, dateComponent1.month-1, dateComponent1.day, dateComponent1.hour, dateComponent1.minute, dateComponent1.second)
    date2 = new Date(dateComponent2.year, dateComponent2.month-1, dateComponent2.day, dateComponent2.hour, dateComponent2.minute, dateComponent2.second)
    
    if(date1 < date2) return 1;
    if(date1 > date2) return -1;
    return 0;
}

// 拼接日期时间
function textDateTime(dateStr1, dateStr2, format1, format2) {
  const monthAbbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

  function parseDate(str, format) {
    const patterns = {
      'yyyy': { regex: /^(\d{4})$/, parts: ['year'] },
      'MM': { regex: /^(\d{2})$/, parts: ['month'] },
      'MMM': { regex: /^([A-Za-z]{3})$/, parts: ['monthAbbr'] },
      'dd': { regex: /^(\d{2})$/, parts: ['day'] },
      'yyyy-MM-dd': { regex: /^(\d{4})-(\d{2})-(\d{2})$/, parts: ['year', 'month', 'day'] },
      'MM-dd': { regex: /^(\d{2})-(\d{2})$/, parts: ['month', 'day'] },
      'dd-MMM-yyyy': { regex: /^(\d{2})-([A-Za-z]{3})-(\d{4})$/, parts: ['day', 'monthAbbr', 'year'] },
      'dd-MMM': { regex: /^(\d{2})-([A-Za-z]{3})$/, parts: ['day', 'monthAbbr'] },
      'MMM-yyyy': { regex: /^([A-Za-z]{3})-(\d{4})$/, parts: ['monthAbbr', 'year'] },
      'yyyy-MM': { regex: /^(\d{4})-(\d{2})$/, parts: ['year', 'month'] },
      'yyyy-MM-dd HH:mm': { regex: /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/, parts: ['year', 'month', 'day', 'hour', 'minute'] },
      'yyyy-MM-dd HH': { regex: /^(\d{4})-(\d{2})-(\d{2}) (\d{2})$/, parts: ['year', 'month', 'day', 'hour'] },
      'HH': { regex: /^(\d{2})$/, parts: ['hour'] },
      'mm': { regex: /^(\d{2})$/, parts: ['minute'] },
      'ss': { regex: /^(\d{2})$/, parts: ['second'] },
      'HH:mm': { regex: /^(\d{2}):(\d{2})$/, parts: ['hour', 'minute'] },
      'HH:mm:ss': { regex: /^(\d{2}):(\d{2}):(\d{2})$/, parts: ['hour', 'minute', 'second'] },
      'mm:ss': { regex: /^(\d{2}):(\d{2})$/, parts: ['minute', 'second'] },
      'dd-MMM-yyyy HH:mm': { regex: /^(\d{2})-([A-Za-z]{3})-(\d{4}) (\d{2}):(\d{2})$/, parts: ['day', 'monthAbbr', 'year', 'hour', 'minute'] },
      'dd-MMM-yyyy HH': { regex: /^(\d{2})-([A-Za-z]{3})-(\d{4}) (\d{2})$/, parts: ['day', 'monthAbbr', 'year', 'hour'] }
    };

    const { regex, parts } = patterns[format];
    const match = str.match(regex);
    if (!match) throw new Error('Invalid format');

    const result = {};
    parts.forEach((part, index) => {
      const value = match[index + 1];
      switch(part) {
        case 'year':
          result.year = parseInt(value);
          break;
        case 'month':
          result.month = parseInt(value) - 1;
          break;
        case 'monthAbbr':
          result.month = monthAbbr.findIndex(m => m === value);
          if (result.month === -1) throw new Error('Invalid month');
          break;
        case 'day':
          result.day = parseInt(value);
          break;
        case 'hour':
          result.hour = parseInt(value);
          break;
        case 'minute':
          result.minute = parseInt(value);
          break;
        case 'second':
          result.second = parseInt(value);
          break;
      }
    });

    // 基本验证
    if (result.month !== undefined && (result.month < 0 || result.month > 11)) throw new Error('Invalid month');
    if (result.day !== undefined && (result.day < 1 || result.day > 31)) throw new Error('Invalid day');
    if (result.hour !== undefined && (result.hour < 0 || result.hour > 23)) throw new Error('Invalid hour');
    if (result.minute !== undefined && (result.minute < 0 || result.minute > 59)) throw new Error('Invalid minute');
    if (result.second !== undefined && (result.second < 0 || result.second > 59)) throw new Error('Invalid second');

    return result;
  }

  // 解析两个日期
  const date1 = parseDate(dateStr1, format1);
  const date2 = parseDate(dateStr2, format2);

  // 合并日期时间对象
  const merged = { ...date1, ...date2 };

  // 创建日期对象
  const finalDate = new Date(
    merged.year,
    merged.month,
    merged.day || 1,
    merged.hour || 0,
    merged.minute || 0,
    merged.second || 0
  );

  // 最终验证
  if (isNaN(finalDate)) throw new Error('Invalid combined date');
  if (finalDate.getDate() !== (merged.day || 1)) throw new Error('Invalid day');
  if (merged.year !== undefined && finalDate.getFullYear() !== merged.year) throw new Error('Invalid year');
  if (merged.month !== undefined && finalDate.getMonth() !== merged.month) throw new Error('Invalid month');

  // 格式化输出
  return `${finalDate.getFullYear()}-${String(finalDate.getMonth() + 1).padStart(2, '0')}-${String(finalDate.getDate()).padStart(2, '0')} ` +
         `${String(finalDate.getHours()).padStart(2, '0')}:${String(finalDate.getMinutes()).padStart(2, '0')}:${String(finalDate.getSeconds()).padStart(2, '0')}`;
}

// 截取日期时间
function formatDate(var1, format) {
    // var1日期格式检查=============

    const formatOptions = {
        "yyyy": { year: "numeric" },
        "yyyy-MM": { year: "numeric", month: "2-digit" },
        "yyyy-MM-dd": { year: "numeric", month: "2-digit", day: "2-digit" },
        "dd-MMM-yyyy": { day: "2-digit", month: "short", year: "numeric" },
        "yyyy-MM-dd HH": { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", hour12: false },
        "dd-MMM-yyyy HH": { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", hour12: false },
        "yyyy-MM-dd HH:mm": { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false },
        "dd-MMM-yyyy HH:mm": { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", hour12: false },
        "HH:mm": { hour: "2-digit", minute: "2-digit", hour12: false },
        "HH:mm:ss": { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false }
    };

    // 判断是否是中文格式
    const isChinese = /[\u4e00-\u9fa5]/.test(format);
    const locale = isChinese ? "zh-CN" : "en-US";

    // 获取格式化选项
    const options = formatOptions[format];
    if (!options) {
        console.error("Unsupported format: " + format);
        return null;
    }
    // 格式化日期
    return new Intl.DateTimeFormat(locale, options).format(date).replace(",", "");
}






// 对不同格式的日期进行解析,返回日期格式和年月日时分秒字段
function parseDateComponents(dateStr) {
    let result = { format: null, year: null, month: null, day: null, hour: null, minute: null, second: null ,msg: null};

    // 16 种可能的日期格式
    const patterns = [
        { regex: /^(\d{2})-(\d{2})-(\d{4})$/, format: "dd-MM-yyyy" },
        { regex: /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2})$/, format: "dd-MM-yyyy HH:mm" },
        { regex: /^(\d{2})-(\d{2})-(\d{4}) (\d{2}):(\d{2}):(\d{2})$/, format: "dd-MM-yyyy HH:mm:ss" },
        { regex: /^(\d{2})-(\d{4})$/, format: "MM-yyyy" },

        { regex: /^(\d{4})-(\d{2})-(\d{2})$/, format: "yyyy-MM-dd" },
        { regex: /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/, format: "yyyy-MM-dd HH:mm" },
        { regex: /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/, format: "yyyy-MM-dd HH:mm:ss" },
        { regex: /^(\d{4})-(\d{2})$/, format: "yyyy-MM" },
        { regex: /^(\d{4})$/, format: "yyyy" },

        { regex: /^(\d{2}):(\d{2}):(\d{2})$/, format: "HH:mm:ss" },
        { regex: /^(\d{2}):(\d{2})$/, format: "HH:mm" },
        { regex: /^(\d{2})$/, format: "HH" }
    ];

    // 遍历匹配合适的格式
    for (let pattern of patterns) {
        let match = dateStr.match(pattern.regex);
        if (match) {
            switch (pattern.format) {
                case "dd-MM-yyyy":
                    result = {format: pattern.format, year: +match[3], month: +match[2], day: +match[1], hour: null, minute: null, second: null };
                    break;
                case "dd-MM-yyyy HH:mm":
                    result = {format: pattern.format,  year: +match[3], month: +match[2], day: +match[1], hour: +match[4], minute: +match[5], second: null };
                    break;
                case "dd-MM-yyyy HH:mm:ss":
                    result = {format: pattern.format,  year: +match[3], month: +match[2], day: +match[1], hour: +match[4], minute: +match[5], second: +match[6] };
                    break;
                case "MM-yyyy":
                    result = {format: pattern.format,  year: +match[2], month: +match[1], day: 1, hour: null, minute: null, second: null };
                    break;
                case "yyyy-MM-dd":
                    result = {format: pattern.format,  year: +match[1], month: +match[2], day: +match[3], hour: null, minute: null, second: null };
                    break;
                case "yyyy-MM-dd HH:mm":
                    result = {format: pattern.format,  year: +match[1], month: +match[2], day: +match[3], hour: +match[4], minute: +match[5], second: null };
                    break;
                case "yyyy-MM-dd HH:mm:ss":
                    result = {format: pattern.format,  year: +match[1], month: +match[2], day: +match[3], hour: +match[4], minute: +match[5], second: +match[6] };
                    break;
                case "yyyy-MM":
                    result = {format: pattern.format,  year: +match[1], month: +match[2], day: 1, hour: null, minute: null, second: null };
                    break;
                case "yyyy":
                    result = {format: pattern.format,  year: +match[1], month: 1, day: 1, hour: null, minute: null, second: null };
                    break;
                case "HH:mm:ss":
                    result = {format: pattern.format,  year: null, month: null, day: null, hour: +match[1], minute: +match[2], second: +match[3] };
                    break;
                case "HH:mm":
                    result = {format: pattern.format,  year: null, month: null, day: null, hour: +match[1], minute: +match[2], second: null };
                    break;
                case "HH":
                    result = {format: pattern.format,  year: null, month: null, day: null, hour: +match[1], minute: null, second: null };
                    break;
            }
            return result;
        }
    }
    result.msg = "错误:无法识别的日期格式"
    return result;
}

// 对操作后的日期数据转化格式(与原输入格式一致)
function recoverDateFormat(dateObj, format) {
    let yyyy = dateObj.getFullYear();
    let MM = String(dateObj.getMonth() + 1).padStart(2, "0");
    let dd = String(dateObj.getDate()).padStart(2, "0");
    let HH = String(dateObj.getHours()).padStart(2, "0");
    let mm = String(dateObj.getMinutes()).padStart(2, "0");
    let ss = String(dateObj.getSeconds()).padStart(2, "0");

    switch (format) {
        case "yyyy-MM-dd":
            return `${yyyy}-${MM}-${dd}`;
        case "yyyy-MM-dd HH:mm":
            return `${yyyy}-${MM}-${dd} ${HH}:${mm}`;
        case "yyyy-MM-dd HH:mm:ss":
            return `${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}`;
        case "yyyy-MM":
            return  `${yyyy}-${MM}`;
        case "yyyy":
            return `${yyyy}`;
        case "HH:mm:ss":
            return `${HH}:${mm}:${ss}`;
        case "HH:mm":
            return `${HH}:${mm}`;
        case "HH":
            return `${HH}`;
            
        case "dd-MM-yyyy":
            return `${dd}-${MM}-${yyyy}`;
        case "dd-MM-yyyy HH:mm":
            return `${dd}-${MM}-${yyyy} ${HH}:${mm}`;
        case "dd-MM-yyyy HH:mm:ss":
            return `${dd}-${MM}-${yyyy} ${HH}:${mm}:${ss}`;
        case "MM-yyyy":
            return `${MM}-${yyyy}`;




        default:
            return "错误:无法格式化";
    }
}

function isEmpty(value) {
    return (
        value == null || // null 或 undefined
        (typeof value === "string" && value.trim() === "") || // 空字符串
        (Array.isArray(value) && value.length === 0) || // 空数组
        (typeof value === "object" && value !== null && Object.keys(value).length === 0) || // 空对象
        (typeof value === "number" && isNaN(value)) // NaN
    );
}

// console.log(parseDateComponents('2024/02/01 12:00:00'))//还需考虑格式错误的情况
// console.log(addYear('2024-04-01 20:30:21', 1));
// console.log(addYear('20:09:00', 1));
// console.log(addYear('2024', 1));//无月份的情况
console.log(addYear('20-01-2024 20:30:21', 1));
console.log(addMonth('2024-01-31 00:00:00',2));
console.log(addDay('2024-02-20 00:00:00',11));
console.log(addDay('2024-02',11));
console.log(addHour('2024-02-01 20:40:00',5.5));
console.log(addHour('20:40:00',5.5));
console.log(addMinutes('20:40:00',5.5));
console.log(addMinutes('2024-02-29 23:40:00',20.5));
console.log(addSeconds('2024-02-29 23:40:00',20));
console.log(addYear('2024/02/29 23:40:00',1));
console.log(yearSpan('2024/02/29 23:40:00','2024-02-01 00:00:00'));
console.log(yearSpan('2024-02-28 01:00:00','2025-02-28 01:02:20'));
console.log(yearSpan('2025-02-28 01:02:20','2024-02-28 01:00:00'));
console.log(yearSpan('01:00:00','01:02:20'));
console.log(monthSpan('2024-02-29','2025-02-28'));
console.log(monthSpan('2025-02-28','2025-06-01'));
console.log(daySpan('2025-02-28','2025-06-01'));
console.log(daySpan('2025-02-28 20:00:00','2025-06-01 10:00:00'));
console.log(minuteSpan('2025-02-28 20:00:00','2025-02-28 10:01:01'));
console.log(compareTime('2025-02-28 20:00:00','2025-02-28 20:00:00'));
console.log(compareTime('2025-02-28 20:00:00','2025-02-28 10:01:01'));
console.log(compareTime('2025-02-28 24:00:00','2025-03-01 00:00:00'));
console.log(textDateTime("2020-04-10","00:00:30","yyyy-MM-dd","HH:mm:ss"));
console.log(textDateTime("00:00:00","10-Dec-2020","HH:mm:ss","dd-MMM-yyyy"));
// 示例调用:
// console.log(formatDate(new Date("2024-02-01T20:00:00"), "yyyy-MM-dd HH:mm")); // 2024-02-01 20:00
// console.log(formatDate(new Date("2024-02-01T20:00:00"), "dd-MMM-yyyy HH:mm")); // 01-Feb-2024 20:00
// console.log(formatDate(new Date("2024-02-01T20:00:00"), "HH:mm:ss")); // 20:00:00