⚙️ DateTime::ToString 日期时间文本格式化深度解析(C++)
——结构、流程、原理与完整实现
引用:
- ppp/DateTime.cpp#L139
家山别矣 想念天涯子
怅然老矣
我共二三子
惶然梦矣
梦也徘徊起
暮暮朝朝
老去吟游
离魂无据
梦里痴痴语
🏗️ 一、整体结构
组成模块:
- 格式解析器:扫描格式字符串,识别
yMdHmsfuT
格式符 - 状态追踪器:使用
symbol
和symbol_size
跟踪当前格式组 - 组件处理器:将时间分量转换为字符串
- 宽度控制器:实现fixed模式下的截断/补零
- 字符串构建器:拼接最终结果
🔄 二、处理流程
⏱️ 三、格式处理原理
关键算法:
while (*format) {
ch = *format++;
if (是格式符(ch)) {
if (symbol != 0 && symbol != ch) {
symbol_exec(0); // 提交当前组
}
symbol = ch; // 设置新符号
symbol_size++; // 增加计数
} else {
symbol_exec(ch); // 提交组并添加字面值
}
}
symbol_exec(0); // 处理剩余组
符号处理器行为:
🛠️ 四、完整代码实现
ppp::string DateTime::ToString(const char* format, bool fixed) noexcept {
ppp::string result;
if (NULL == format || *format == '\x0') {
return result;
}
char symbol = 0;
int symbol_size = 0;
auto symbol_exec = int ch noexcept {
ppp::string seg;
switch (symbol) {
case 'y': seg = stl::to_string<ppp::string>(Year()); break;
case 'M': seg = stl::to_string<ppp::string>(Month()); break;
case 'd': seg = stl::to_string<ppp::string>(Day()); break;
case 'H': seg = stl::to_string<ppp::string>(Hour()); break;
case 'm': seg = stl::to_string<ppp::string>(Minute()); break;
case 's': seg = stl::to_string<ppp::string>(Second()); break;
case 'f': seg = stl::to_string<ppp::string>(Millisecond()); break;
case 'u': seg = stl::to_string<ppp::string>(Microseconds()); break;
case 'T': seg = stl::to_string<ppp::string>((int64_t)TotalHours()); break;
}
if (fixed && symbol != 0) {
int64_t seg_size = seg.size();
if (seg_size > symbol_size) {
seg = seg.substr(seg_size - symbol_size);
}
else if (seg_size < symbol_size) {
seg = PaddingLeft(seg, symbol_size, '0');
}
}
if (ch != 0) {
seg.append(1, ch);
}
result += seg;
symbol = 0;
symbol_size = 0;
};
const char* p = format;
while (*p) {
char ch = *p++;
if (ch == 'y' || ch == 'M' || ch == 'd' ||
ch == 'H' || ch == 'm' || ch == 's' ||
ch == 'f' || ch == 'u' || ch == 'T')
{
if (symbol != 0 && symbol != ch) {
symbol_exec(0);
}
symbol = ch;
symbol_size++;
} else {
symbol_exec(ch);
}
}
symbol_exec(0);
return result;
}
template <typename _Ty>
_Ty DateTime::PaddingLeft(const _Ty& s, int count, char padding_char) noexcept {
int string_length = static_cast<int>(s.size());
if (count < 1 || count <= string_length) {
return s;
}
_Ty r = s;
for (int i = 0; i < count - string_length; i++) {
r = _Ty(1ul, padding_char) + r;
}
return r;
}
🔍 五、核心机制剖析
1. 格式符处理矩阵
输入 | 当前状态 | 动作 |
---|---|---|
新符号 | symbol相同 | symbol_size++ |
新符号 | symbol不同 | 提交旧组,开始新组 |
非符号 | 有状态 | 提交组,添加字面值 |
非符号 | 无状态 | 直接添加字面值 |
2. fixed模式控制逻辑
3. 时间分量映射关系
💡 六、应用场景示例
1. 标准日期格式
// 输出:2025-07-29
ToString("yyyy-MM-dd", true)
处理流程:
2. 精确时间戳
// 输出:23:38:03.000000
ToString("HH:mm:ss.ffffff", false)
3. 压缩日期格式
// 输出:25/7/29
ToString("yy/M/d", true)
📌 总结要点
- 状态驱动设计:双变量(symbol/size)实现高效状态跟踪
- 分层处理架构:
- 格式扫描 → 状态管理 → 分量转换 → 宽度控制 → 结果构建
- 关键处理流程:
- 流程子图:
核心算法:
- 线性扫描格式字符串 O(n)
- 状态变更触发分组处理
- 数值到字符串的精确转换
流程子图:
“日期格式化是时间数据的符号化表达——状态机驱动字符串的精密重组”