引言
在项目开发中,日期时间格式化展示是常见的功能需求,但由于不同场景对格式的要求各异,团队中每个开发者都独立编写代码来满足各自需求,这导致项目中出现大量重复且冗余的代码。这种现象不仅降低了开发效率,也违背了代码规范中关于 避免重复代码(DRY-Don't Repeat Yourself) 和 保持一致性 的原则。
避免过度封装 :虽然 DRY 原则 强调避免重复代码,但过度封装可能会导致代码复杂难懂。在封装时需要权衡代码的通用性和灵活性。
基于以上思考,封装一个通用的日期格式化函数,不仅能减少重复代码,还能显著提升代码的可复用性和可维护性。通过学习和整合项目组中同学们的优秀实现,我记录下自己的学习过程,并尝试封装出一个易于使用的日期格式化工具函数。
代码实现
- MDN的Date文档
- 通过指定占位符定义
format
格式,使用replace
方法将占位符替换为具体的日期时间值
定义 占位符 规则
输入 | 示例 | 描述 |
---|---|---|
YY | 25 | 两位数的年份 |
YYYY | 2025 | 四位数的年份 |
M | 1-12 | 月份/从 1 开始 |
MM | 01-12 | 月份/两位数 |
MMM | Jan-Dec | 简写英文的月份名称 |
MMMM | January-December | 完整英文的月份名称 |
MMMMM | 一月-十二月 | 大写汉字的月份名称 |
D | 1-31 | 月份里的一天 |
DD | 01-31 | 月份里的一天/两位数 |
W | 0 | 0-6,分别表示星期日-星期六 |
WW | 周日 | 周x |
WWW | 星期日 | 星期x |
Q | 1 | 1-4 |
一季度 | 一季度--四季度 | |
H | 0-23 | 小时/24 小时制 |
HH | 00-23 | 小时/24 小时制/两位数 |
h | 1-12 | 小时/12 小时制 |
hh | 01-12 | 小时/12 小时制/两位数 |
A | AM / PM | 上午/下午 大写 |
a | am / pm | 上午/下午 小写 |
m | 0-59 | 分钟 |
mm | 00-59 | 分钟/两位数 |
s | 0-59 | 秒 |
ss | 00-59 | 秒/两位数 |
S | 0-9 | 毫秒/一位数 |
SS | 00-99 | 毫秒/两位数 |
SSS | 000-999 | 毫秒/三位数 |
源码
ts
function dateFormatter(format: string = 'YYYY-MM-DD HH:mm:ss', date: Date | string | number = new Date()): string {
let parsedDate: Date;
if (date instanceof Date) {
parsedDate = date;
} else if (typeof date === 'string' || typeof date === 'number') {
parsedDate = new Date(date);
if (isNaN(parsedDate.getTime())) {
parsedDate = new Date();
}
} else {
parsedDate = new Date()
}
// 获取日期的基本信息
const year = parsedDate.getFullYear();
const month = parsedDate.getMonth(); // 0 表示 1 月
const day = parsedDate.getDate();
const weekday = parsedDate.getDay(); // 0 表示星期日
const hour = parsedDate.getHours();
const minute = parsedDate.getMinutes();
const second = parsedDate.getSeconds();
const millisecond = parsedDate.getMilliseconds();
// 星期和月份的常量定义
const FULL_WEEKDAYS = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const SHORT_WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
const MONTHS_CHINESE = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
const MONTHS_SHORT_EN = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const MONTHS_LONG_EN = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const QUARTERS = ['一季度', '二季度', '三季度', '四季度'];
// 获取季度的函数
const getQuarter = (month: number) => Math.ceil((month + 1) / 3);
// 映射表:格式化占位符到实际值的映射
const PLACEHOLDER_MAPPING = {
// 年份
YYYY: year.toString(),
YY: year.toString().slice(-2),
// 月份
M: (month + 1).toString(),
MM: (month + 1).toString().padStart(2, '0'),
MMM: MONTHS_SHORT_EN[month],
MMMM: MONTHS_LONG_EN[month],
MMMMM: MONTHS_CHINESE[month],
// 日期
D: day.toString(),
DD: day.toString().padStart(2, '0'),
// 星期
W: weekday.toString(),
WW: SHORT_WEEKDAYS[weekday],
WWW: FULL_WEEKDAYS[weekday],
// 季度
Q: getQuarter(month).toString(),
QQ: QUARTERS[getQuarter(month) - 1],
// 时间(24小时制)
H: hour.toString(),
HH: hour.toString().padStart(2, '0'),
// 时间(12小时制)
h: (hour % 12 || 12).toString(),
hh: (hour % 12 || 12).toString().padStart(2, '0'),
A: hour >= 12 ? 'PM' : 'AM',
a: hour >= 12 ? 'pm' : 'am',
// 分钟和秒
m: minute.toString(),
mm: minute.toString().padStart(2, '0'),
s: second.toString(),
ss: second.toString().padStart(2, '0'),
// 毫秒
S: millisecond.toString(),
SS: millisecond.toString().padStart(2, '0'),
SSS: millisecond.toString().padStart(3, '0')
};
// 替换逻辑:根据格式字符串中的占位符替换为实际值
return format.replace(/Y+|M+|D+|W+|Q+|H+|h+|A|a|m+|s+|S+/g, (match) => {
return PLACEHOLDER_MAPPING[match] || '';
});
}
感谢阅读,敬请斧正!