DateTimeUtils.ts 14.1 KB
/**
 * 日期/时间工具
 */
// let myDate = new Date();
// myDate.getTime()              // 获取以毫秒为单位的时间值。
// myDate.getFullYear();         // 获取完整的年份(4位,1970-????)
// myDate.getMonth();            // 获取myDate月份(0-11,0代表1月)
// myDate.getDate();             // 获取myDate日期(1-31)
// myDate.getDay();              // 获取myDate星期X(0-6,0代表星期天)
// myDate.getTime();             // 获取myDate时间(从1970.1.1开始的毫秒数)
// myDate.getHours();            // 获取myDate小时数(0-23)
// myDate.getMinutes();          // 获取myDate分钟数(0-59)
// myDate.getSeconds();          // 获取myDate秒数(0-59)
// myDate.getMilliseconds();     // 获取myDate毫秒数(0-999)
// myDate.toLocaleDateString();  // 获取当前日期:使用当前或指定的区域设置将日期转换为字符串。
// myDate.toLocaleTimeString();  // 获取当前时间:使用当前或指定的区域设置将时间转换为字符串。
// myDate.toLocaleString();      // 获取日期与时间:使用当前或指定的区域设置将日期和时间转换为字符串

// 日期时间的年月日时分秒以及毫秒的各个字段
export class Opt {
  year: string = ''; // 年 小写y
  month: string = ''; // 月 大写M
  day: string = ''; // 日 小写d
  hour: string = ''; // 时 大写H(24小时制)
  // hh: string = ''; // 时 小写H(12小时制)
  minute: string = ''; // 分 小写m
  second: string = ''; // 秒 小写s
  millisecond: string = ''; // 毫秒 大写S
}

export class DateTimeUtils {
  // 日期+时间格式
  static readonly PATTERN_DATE_TIME_DEFAULT: string = 'yyyyMMddHHmmss'; // 年月日时分秒
  static readonly PATTERN_DATE_TIME_HYPHEN: string = 'yyyy-MM-dd HH:mm:ss'; // 日期中包含连字符/中划线(HYPHEN),时间是以冒号(Colon)分割
  static readonly PATTERN_DATE_TIME_HYPHEN_MS: string = 'yyyy-MM-dd HH:mm:ss.SSS'; // 日期中包含连字符/中划线(HYPHEN),时间是以冒号(Colon)分割
  static readonly PATTERN_DATE_TIME_SLASH: string = 'yyyy/MM/dd HH:mm:ss'; // 日期中包含正斜杠(forward slash '/')
  static readonly PATTERN_DATE_TIME_BACK_SLASH: string = 'yyyy\\MM\\dd HH:mm:ss'; // 日期中包含反斜杠(back slash '\')
  static readonly PATTERN_DATE_TIME_DOT: string = 'yyyy.MM.dd HH:mm:ss'; // 日期中包含英文小圆点(DOT '.')
  static readonly PATTERN_DATE_TIME_CN: string = 'yyyy年MM月dd日 HH:mm:ss'; // 日期中包含包含中文年月日
  static readonly PATTERN_DATE_TIME_MS: string = 'yyyyMMddHHmmssSSS'; // 时间中包含毫秒
  static readonly PATTERN_DATE_TIME_WITHOUT_SECOND: string = 'yyyyMMddHHmm'; // 时间中不包含秒
  static readonly PATTERN_DATE_TIME_SIMPLIFY: string = 'MM/dd HH:mm'; // 精简的日期+时间(不包含年份和秒), 月/日 时:分

  // 仅日期格式(不包含时间)
  static readonly PATTERN_DATE_DEFAULT: string = 'yyyyMMdd'; // 年月日
  static readonly PATTERN_DATE_HYPHEN: string = 'yyyy-MM-dd'; // 日期中包含连字符/中划线(HYPHEN)
  static readonly PATTERN_DATE_SLASH: string = 'yyyy/MM/dd'; // 日期中包含正斜杠(forward slash '/')
  static readonly PATTERN_DATE_BACK_SLASH: string = 'yyyy\\MM\\dd'; // 日期中包含反斜杠(back slash '\')
  static readonly PATTERN_DATE_DOT: string = 'yyyy.MM.dd'; // 日期中包含英文小圆点(DOT '.')
  static readonly PATTERN_DATE_CN: string = 'yyyy年MM月dd日'; // 日期中包含包含中文年月日
  static readonly PATTERN_DATE_SLASH_WITHOUT_YEAR: string = 'MM/dd'; // 日期中不包含年份
  static readonly PATTERN_DATE_CN_WITHOUT_YEAR: string = 'MM月dd日'; // 日期中不包含年份,且month与day是中文

  // 仅时间格式(不包含日期)
  static readonly PATTERN_TIME_DEFAULT: string = 'HHmmss'; // 时分秒
  static readonly PATTERN_TIME_COLON: string = 'HH:mm:ss'; // 时间是以冒号(Colon)分割
  static readonly PATTERN_TIME_MS: string = 'HHmmssSSS'; // 时间中包含毫秒
  static readonly PATTERN_TIME_COLON_DOT_MS: string = 'HH:mm:ss.SSS'; // 时间是以冒号(Colon)与点号(DOT)分割,且包含毫秒
  static readonly PATTERN_TIME_WITHOUT_SECOND: string = 'HHmm'; // 时间中不包含秒
  static readonly PATTERN_TIME_COLON_WITHOUT_SECOND: string = 'HH:mm'; //  时间是以冒号(Colon)分割,且不包含秒
  static readonly PATTERN_TIME_COLON_WITHOUT_HOUR: string = 'mm:ss'; // 时间是以冒号(Colon)分割,且不包含小时(分钟长度至少2位,可能是3位)

  static readonly RADIX: number = 60; // 时分秒的进制
  static readonly MILLISECOND_PER_SECOND: number = 1000; // 1000毫秒每秒,秒与毫秒之间的转换进制

  // 周x
  static readonly weeks1: string[] = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
  // 星期X
  static readonly weeks2: string[] = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];

  static readonly today: string = '今天';
  static readonly yesterday: string = '昨天';
  static readonly tomorrow: string = '明天';

  /**
   * 获取当前日期:年月日
   * 返回格式,默认如:yyyyMMdd,20230926
   */
  static getCurDate(format = DateTimeUtils.PATTERN_DATE_DEFAULT): string {
    const DATETIME = new Date()
    return DateTimeUtils.formatDate(DATETIME.getTime(), format)
  }

  /**
   * 获取当前时间:时分秒
   * 返回格式,默认如:HHmmss,230559
   */
  static getCurTime(format = DateTimeUtils.PATTERN_TIME_DEFAULT): string {
    const DATETIME = new Date()
    return DateTimeUtils.formatDate(DATETIME.getTime(), format)
  }

  /**
   * 日期不足两位前面补充0
   * @param value-数据值
   */
  static fill(value: number): string {
    return (value > 9 ? '' : '0') + value
  }

  /**
   * 获取两个时间点的时间差
   * @param milliseconds1
   * @param milliseconds2
   * @returns Duration
   */
  static getDuration(milliseconds1: number, milliseconds2: number): number {
    let date1 = new Date(milliseconds1)
    let date2 = new Date(milliseconds2)
    let timeDiff = Math.abs(date2.getTime() - date1.getTime());
    return timeDiff;
  }


  /**
   * 格式化Duration时间
   * @param duration 毫秒
   * @returns 返回格式化后的Duration时间
   */
  static getFormattedDuration(duration: number): string {
    // 也可以直接先除以1000转换为秒后再计算 // duration = duration/1000
    // Math.floor 用于向下取整
    let hour = Math.floor(duration / (1000 * 60 * 60))
    // 用总毫秒数duration减去hour所对应的毫秒数差,再除以每分钟所占用的毫秒数
    let minute = Math.floor((duration - hour * (1000 * 60 * 60)) / (1000 * 60));
    // let second = Math.floor((duration - hour * (1000 * 60 * 60) - minute * (60 * 1000)) / 1000)
    let second = Math.floor(duration / 1000) % 60; // 先除以1000,转换为秒;再对整分钟取余
    if (hour > 0) {
      return `${DateTimeUtils.fill(hour)}:${DateTimeUtils.fill(minute)}:${DateTimeUtils.fill(second)}`
    }
    return `${DateTimeUtils.fill(minute)}:${DateTimeUtils.fill(second)}`
  }

  /**
   * 格式化显示倒计时
   * @param milliseconds 时间戳 毫秒
   * @returns 返回格式化后的Duration时间
   */
  static getDurationToNow(milliseconds: number): string {
    let nowDateTime = new Date();
    let duration = milliseconds - nowDateTime.getTime();
    if (duration < 0) {
      duration = 0;
    }
    return DateTimeUtils.getFormattedDuration(duration)
  }

  /**
   * 根据日期/时间字符串反序列化对应的时间戳
   * @param dateTimeString string 日期+时间字符串
   * 列举字符串格式,如:
   * 20231129,yyyyMMdd
   * 20231126230559 yyyyMMddHHmmss
   * 20231126230559729 yyyyMMddHHmmssSSS
   * 2023-11-26 23:05:59.729, yyyy-MM-dd HH:mm:ss.SSS(先移除所有中划线和冒号及空格等非数字字符)
   * @returns 转换后的时间戳:milliseconds 毫秒
   */
  static parseDate(dateTimeString?: string): number {
    if (!dateTimeString) {
      return 0
    }
    // 移除连字符/中划线(Hyphen/Strike)/移除冒号(Colon)/移除空白字符(包括空格、制表符、换行符等)/移除正斜杠(forward slash '/')/移除反斜杠(back slash '\')
    // let regExp = new RegExp('(\d)(?=(\d{3})+\.)/g')
    let regExpHyphen: RegExp = new RegExp('-', 'g');
    let regExpSlash: RegExp = new RegExp('/', 'g');
    let regExpBackSlash: RegExp = new RegExp(/\\/, 'g');
    let regExpColon: RegExp = new RegExp(':', 'g');
    let regExpBlank: RegExp = new RegExp('\s', 'g');
    dateTimeString = dateTimeString.replace(regExpHyphen, '')
      .replace(regExpSlash, '')
      .replace(regExpBackSlash, '')
      .replace(regExpColon, '')
      .replace(regExpBlank, '');
    if (dateTimeString.length < 8 || dateTimeString.length > 17) {
      return 0
    }
    console.log("dateTimeString:" + dateTimeString)
    // substring(startIndex: number, endIndex?: number): string:
    // 返回从startIndex开始到endIndex(不包括)之间的子字符串。
    // 如果省略endIndex,则返回从startIndex到字符串末尾的子字符串。
    let year: number = Number.parseInt(dateTimeString.substring(0, 4));
    console.log("year:" + year)
    let month: number = Number.parseInt(dateTimeString.substring(4, 6)) - 1;
    console.log("month:" + month)
    let day: number = Number.parseInt(dateTimeString.substring(6, 8));
    console.log("day:" + day)
    if (dateTimeString.length < 10) {
      return new Date(year, month, day).getTime();
    }
    let hours: number = Number.parseInt(dateTimeString.substring(8, 10));
    console.log("hours:" + hours)
    if (dateTimeString.length < 12) {
      return new Date(year, month, day, hours).getTime();
    }
    let min: number = Number.parseInt(dateTimeString.substring(10, 12));
    console.log("min:" + min)
    if (dateTimeString.length < 14) {
      return new Date(year, month, day, hours, min).getTime();
    }
    let sec: number = Number.parseInt(dateTimeString.substring(12, 14));
    console.log("sec:" + sec)
    if (dateTimeString.length < 17) {
      return new Date(year, month, day, hours, min, sec).getTime();
    }
    let ms: number = Number.parseInt(dateTimeString.substring(14, 17));
    console.log("ms:" + ms)
    return new Date(year, month, day, hours, min, sec, ms).getTime();
  }

  /**
   * 把整数时间戳格式化为指定格式的日期+时间字符串
   * @param timestamp:毫秒
   * @param format 指定要格式化的pattern
   * @returns 格式化后的字符串
   */
  static formatDate(timestamp: number, format = DateTimeUtils.PATTERN_DATE_HYPHEN): string {
    let res = "";
    try {
      const date = new Date(timestamp);
      const opt: Opt = {
        year: date.getFullYear().toString(), // 年 小写y
        month: (date.getMonth() + 1).toString(), // 月 大写M
        day: date.getDate().toString(), // 日 小写d
        hour: date.getHours().toString(), // 时 大写H(24小时制)
        minute: date.getMinutes().toString(), // 分 小写m
        second: date.getSeconds().toString(), // 秒 小写s
        millisecond: date.getMilliseconds().toString() // 毫秒 大写S
      };
      const regKeys: string[] = ['y+', 'M+', 'd+', 'H+', 'm+', 's+', 'S+'];
      for (let i = 0; i < regKeys.length; i++) {
        const regKey = regKeys[i];
        const reg = new RegExp(regKey);
        let ret = reg.exec(format); // 使用 RegExp 的 match 或 exec 方法在目标字符串中查找匹配项
        if (ret) {
          switch (regKey) {
            case 'y+':
              format = format.replace(reg, ret[0].length <= opt.year.length ? opt.year : opt.year.padStart(ret[0].length, "0"));
              break;
            case 'M+':
              format = format.replace(reg, ret[0].length < opt.month.length ? opt.month : opt.month.padStart(ret[0].length, "0"));
              break;
            case 'd+':
              format = format.replace(reg, ret[0].length < opt.day.length ? opt.day : opt.day.padStart(ret[0].length, "0"));
              break;
            case 'H+':
              format = format.replace(reg, ret[0].length < opt.hour.length ? opt.hour : opt.hour.padStart(ret[0].length, "0"));
              break;
            case 'm+':
              format = format.replace(reg, ret[0].length < opt.minute.length ? opt.minute : opt.minute.padStart(ret[0].length, "0"));
              break;
            case 's+':
              format = format.replace(reg, ret[0].length < opt.second.length ? opt.second : opt.second.padStart(ret[0].length, "0"));
              break;
            case 'S+':
              format = format.replace(reg, ret[0].length < opt.millisecond.length ? opt.millisecond : opt.millisecond.padStart(ret[0].length, "0"));
              break;
          }
        }
      }
      res = format;
    } catch (error) {
      console.error(`ERROR formatDate error: ${error}.`);
    }
    return res;
  }

  /**
   * 构造星期X/周X
   * @param timestamp
   * @returns
   */
  static buildDay(timestamp: number, weeks = this.weeks1): string {
    try {
      const date = new Date(timestamp);
      let day = date.getDay()
      let week = weeks[day];
      return week
    } catch (error) {
      console.error(`ERROR formatDate error: ${error}.`);
    }
  }

  static startOfDay(argument: number): Date {
    const _date = new Date(argument);
    _date.setHours(0, 0, 0, 0);
    return _date;
  }

  static isSameDay(dateLeft: number, dateRight: number): boolean {
    const dateLeftStartOfDay = this.startOfDay(dateLeft);
    const dateRightStartOfDay = this.startOfDay(dateRight);
    return +dateLeftStartOfDay === +dateRightStartOfDay;
  }

  static addDays(argument: number, amount: number): Date {
    const _date = new Date(argument);
    if (amount == 0) {
      return _date;
    }
    _date.setDate(_date.getDate() + amount);
    return _date;
  }

  static subDays(date: number, amount: number): Date {
    return this.addDays(date, -amount);
  }

  static isToday(date: number,): boolean {
    return this.isSameDay(date, Date.now());
  }

  static isYesterday(date: number): boolean {
    return this.isSameDay(date, this.subDays(Date.now(), 1).getTime());
  }

  static isTomorrow(date: number): boolean {
    return this.isSameDay(date, this.addDays(Date.now(), 1).getTime());
  }
}

// const dateTimeUtils = new DateTimeUtils()