DateTime::ToString 日期时间文本格式化深度解析(C++)

发布于:2025-07-30 ⋅ 阅读:(66) ⋅ 点赞:(0)

⚙️ DateTime::ToString 日期时间文本格式化深度解析(C++)

——结构、流程、原理与完整实现

引用

  1. ppp/DateTime.cpp#L139
  2. 家山别矣 想念天涯子 怅然老矣 我共二三子 惶然梦矣 梦也徘徊起
  3. 暮暮朝朝 老去吟游 离魂无据 梦里痴痴语
🏗️ 一、整体结构
DateTime
-int year
-int month
-int day
-int hour
-int minute
-int second
-int millisecond
-int64_t microseconds
-int64_t total_hours
+ToString(const char* format, bool fixed) : noexcept
#PaddingLeft()

组成模块

  1. 格式解析器:扫描格式字符串,识别yMdHmsfuT格式符
  2. 状态追踪器:使用symbolsymbol_size跟踪当前格式组
  3. 组件处理器:将时间分量转换为字符串
  4. 宽度控制器:实现fixed模式下的截断/补零
  5. 字符串构建器:拼接最终结果

🔄 二、处理流程
非空
相同
不同
循环
循环
开始
format
非空?
返回空字符串
初始化状态
遍历格式字符串
读取字符ch
是否格式符?
与当前
symbol相同?
增加symbol_size
处理当前组
设置新symbol
ProcessGroup
添加字面字符
结束?
返回结果
⏱️ 三、格式处理原理

关键算法

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); // 处理剩余组

符号处理器行为

00 00 01 01 02 02 03 03 04 获取分量值 fixed宽度调整 添加字面字符 重置状态 处理流程 symbol_exec处理流程

🛠️ 四、完整代码实现
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模式控制逻辑
直接输出
seg.size > symbol_size
seg.size < symbol_size
CheckLength
过长
截断方向
低位
过短
填充方向
左侧
填充字符
'0'
相等
3. 时间分量映射关系
y
年份
M
月份
d
日期
H
小时
m
分钟
s
秒钟
f
毫秒
u
微秒
T
总小时数

💡 六、应用场景示例

1. 标准日期格式

// 输出:2025-07-29
ToString("yyyy-MM-dd", true)

处理流程

格式解析器 状态追踪 结果生成 "yyyy" → symbol='y', size=4 '-' → 非符号 提交组 → "2025" 添加"-" "MM" → symbol='M', size=2 '-' → 非符号 提交组 → "07"(补齐) 添加"-" "dd" → symbol='d', size=2 提交组 → "29" 格式解析器 状态追踪 结果生成

2. 精确时间戳

// 输出:23:38:03.000000
ToString("HH:mm:ss.ffffff", false)

3. 压缩日期格式

// 输出:25/7/29
ToString("yy/M/d", true)

📌 总结要点

  1. 状态驱动设计:双变量(symbol/size)实现高效状态跟踪
  2. 分层处理架构
    • 格式扫描 → 状态管理 → 分量转换 → 宽度控制 → 结果构建
  3. 关键处理流程
  4. 流程子图
1
格式解析
格式解析
1
识别格式符 (5%)
识别格式符 (5%)
分量获取
分量获取
1
调用成员函数 (5%)
调用成员函数 (5%)
字符串处理
字符串处理
1
fixed模式调整 (4%)
fixed模式调整 (4%)
结果构建
结果构建
1
拼接最终字符串 (4%)
拼接最终字符串 (4%)
数据转换流程
  1. 核心算法

    • 线性扫描格式字符串 O(n)
    • 状态变更触发分组处理
    • 数值到字符串的精确转换
  2. 流程子图

格式符
非格式符
过长
过短
相等
开始解析
扫描格式字符串
是否与当前
符号相同
增加计数器
提交当前组
处理字符
获取时间分量
转字符串
fixed模式?
调整宽度
加入结果
截断末尾
左侧补零
添加为字面字符
继续扫描
是否结束
提交末组
返回结果

“日期格式化是时间数据的符号化表达——状态机驱动字符串的精密重组”