源码日期格式化实现逻辑?

访客 源码剖析 1

本文目录导读:

  1. 核心实现方式
  2. 主流框架实现分析
  3. 关键实现技巧
  4. 完整封装示例
  5. 性能优化建议

我来详细解析源码中日期格式化的实现逻辑。

核心实现方式

基本模板替换法(最常见)

function formatDate(date, format) {
  const map = {
    'yyyy': date.getFullYear(),
    'MM': String(date.getMonth() + 1).padStart(2, '0'),
    'dd': String(date.getDate()).padStart(2, '0'),
    'HH': String(date.getHours()).padStart(2, '0'),
    'mm': String(date.getMinutes()).padStart(2, '0'),
    'ss': String(date.getSeconds()).padStart(2, '0'),
    'S': date.getMilliseconds()
  };
  return format.replace(/yyyy|MM|dd|HH|mm|ss|S/g, match => map[match]);
}

正则替换实现

function format(date, fmt = 'yyyy-MM-dd HH:mm:ss') {
  return fmt.replace(/[yMdHmsS]+/g, (match) => {
    const year = date.getFullYear();
    switch(match[0]) {
      case 'y': 
        return String(year).slice(-match.length);
      case 'M': 
        return pad(date.getMonth() + 1, match.length);
      case 'd': 
        return pad(date.getDate(), match.length);
      case 'H': 
        return pad(date.getHours(), match.length);
      case 'm': 
        return pad(date.getMinutes(), match.length);
      case 's': 
        return pad(date.getSeconds(), match.length);
      case 'S': 
        return date.getMilliseconds();
    }
  });
  function pad(num, len) {
    return String(num).padStart(len, '0');
  }
}

主流框架实现分析

Vue.js 源码实现

// Vue 2.x 中的日期格式化
function formatDate(date, fmt) {
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  };
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      const str = o[k] + '';
      fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str));
    }
  }
  return fmt;
  function padLeftZero(str) {
    return ('00' + str).substr(str.length);
  }
}

Day.js 核心实现

// day.js 的格式化逻辑
const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|S{1,3}/g;
function format(date, formatStr) {
  const instance = this;
  const locale = instance.$locale();
  const result = formatStr.replace(REGEX_FORMAT, (match) => {
    const args = [match, instance];
    switch (match) {
      case 'YYYY': return instance.year();
      case 'YY': return pad(instance.year() % 100, 2);
      case 'MM': return pad(instance.month() + 1, 2);
      case 'M': return instance.month() + 1;
      case 'DD': return pad(instance.date(), 2);
      case 'D': return instance.date();
      case 'HH': return pad(instance.hour(), 2);
      case 'H': return instance.hour();
      case 'hh': return pad(instance.hour() % 12 || 12, 2);
      case 'h': return instance.hour() % 12 || 12;
      case 'mm': return pad(instance.minute(), 2);
      case 'm': return instance.minute();
      case 'ss': return pad(instance.second(), 2);
      case 's': return instance.second();
      case 'SSS': return pad(instance.millisecond(), 3);
      case 'S': return instance.millisecond();
      default: return match;
    }
  });
  return result;
}

关键实现技巧

补零处理

// 方法一:padStart(ES2017)
String(num).padStart(2, '0');
// 方法二:数组拼接
('00' + num).slice(-2);
// 方法三:条件判断
num < 10 ? '0' + num : String(num);

月份从0开始

// JavaScript 月份是 0-11
const month = date.getMonth() + 1; // 需要 +1

转义处理

function format(date, formatStr) {
  // 支持转义字符,[YYYY] 输出 YYYY 而非年份
  return formatStr.replace(/\[([^\]]+)]|Y{1,4}|M{1,2}|D{1,2}/g, (match) => {
    if (match.startsWith('[')) {
      return match.slice(1, -1);
    }
    // 正常格式化逻辑
  });
}

完整封装示例

class DateFormatter {
  constructor(date) {
    this.date = date instanceof Date ? date : new Date(date);
  }
  format(formatStr = 'YYYY-MM-DD HH:mm:ss') {
    const pad = (num, len = 2) => String(num).padStart(len, '0');
    const tokens = {
      'YYYY': this.date.getFullYear(),
      'YY': String(this.date.getFullYear()).slice(-2),
      'MM': pad(this.date.getMonth() + 1),
      'M': this.date.getMonth() + 1,
      'DD': pad(this.date.getDate()),
      'D': this.date.getDate(),
      'HH': pad(this.date.getHours()),
      'H': this.date.getHours(),
      'hh': pad(this.date.getHours() % 12 || 12),
      'h': this.date.getHours() % 12 || 12,
      'mm': pad(this.date.getMinutes()),
      'm': this.date.getMinutes(),
      'ss': pad(this.date.getSeconds()),
      's': this.date.getSeconds(),
      'SSS': pad(this.date.getMilliseconds(), 3),
      'S': this.date.getMilliseconds()
    };
    return formatStr.replace(/\[([^\]]+)]|YYYY|YY|MM|M|DD|D|HH|H|hh|h|mm|m|ss|s|SSS|S/g, 
      (match) => tokens[match] || match.slice(1, -1)
    );
  }
}

性能优化建议

  1. 缓存正则表达式
  2. 使用 Map 替代对象
  3. 预编译格式化模板
  4. 减少字符串拼接

这样的源码实现逻辑清晰、可扩展性强,是大多数日期库的核心实现方式。

标签: 日期格式化

抱歉,评论功能暂时关闭!