You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何校验给定年、月、日及时间格式的合法性

问题根因

你遇到的问题来自两个API的默认行为和预期不符,不是函数本身存在bug:

  • std::get_time解析失败时不会重置tm结构体,只会在输入流上标记failbit。你代码里用struct tm tm{}做了零值初始化,解析"2018 12 aa 00:26:00"时,年、月字段已经成功读取为2018、12,读到日字段遇到aa直接触发解析失败,此时tm_mday保留初始值0,后续的时分秒字段也没被修改,全为0。
  • mktime的设计本身就支持自动归一化非法日期,不是遇到非法值就返回-1。传入的字段如果越界,它会自动换算成合法时间:比如tm_mday=0会被识别为上个月的最后一天,tm_mon=12会算成下一年1月,2月30日会自动换算成3月1日或2日(根据是否闰年调整)。你测试用例里12月0日就被自动修正为当年11月30日0点,所以输出了看似正常的时间戳。
成熟校验方案

不需要手动逐字段写闰年、大小月的硬校验规则,组合标准库能力就能实现严格校验,分两步即可:

第一步:检查流解析状态

std::get_time解析完成后立刻判断输入流的failbit,如果置位说明格式不匹配(比如出现非数字字符、字段长度不对),直接判定为非法,不进入后续逻辑。

第二步:做往返一致性校验

mktime自动修正非法日期后,会把修正后的值写回传入的tm结构。你只需要在调用mktime前保存一份原始解析得到的字段值,调用完成后逐字段比对年、月、日、时、分、秒,如果所有字段和原始输入完全一致,说明日期本身合法,没有被自动修正;如果有字段不一致,说明原始输入是非法值。

针对你处理YYYYMMDDHHMM_****.gz格式日志、清理14天以上旧文件的场景,可直接参考以下实现:

#include <iostream>
#include <iomanip>
#include <ctime>
#include <sstream>
#include <string>
#include <chrono>
#include <cctype>

bool parse_log_time(const std::string& filename, std::time_t* out_ts = nullptr) {
    // 先校验基础格式:前12位为时间数字,第13位是分隔符_
    if (filename.size() < 13 || filename[12] != '_') {
        return false;
    }
    for (int i = 0; i < 12; i++) {
        if (!std::isdigit(filename[i])) {
            return false;
        }
    }
    // 按固定格式解析时间
    struct tm raw_tm{};
    std::istringstream iss(filename.substr(0, 12));
    iss >> std::get_time(&raw_tm, "%Y%m%d%H%M");
    if (iss.fail()) {
        return false;
    }
    raw_tm.tm_sec = 0; // 日志名不含秒字段,固定置0

    // 复制一份原始值供后续比对
    struct tm normalized_tm = raw_tm;
    std::time_t ts = mktime(&normalized_tm);
    if (ts == -1) {
        return false;
    }

    // 校验归一化后的时间和原始输入完全一致,没有被自动修正
    if (normalized_tm.tm_year != raw_tm.tm_year ||
        normalized_tm.tm_mon != raw_tm.tm_mon ||
        normalized_tm.tm_mday != raw_tm.tm_mday ||
        normalized_tm.tm_hour != raw_tm.tm_hour ||
        normalized_tm.tm_min != raw_tm.tm_min ||
        normalized_tm.tm_sec != raw_tm.tm_sec) {
        return false;
    }

    if (out_ts) {
        *out_ts = ts;
    }
    return true;
}

int main() {
    const std::time_t now_ts = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    const std::time_t expire_threshold = 14 * 24 * 3600;

    // 测试用例1:合法旧日志
    std::string valid_old_log = "201812220026_app.log.gz";
    std::time_t log_ts;
    if (parse_log_time(valid_old_log, &log_ts)) {
        bool need_clean = (now_ts - log_ts) > expire_threshold;
        std::cout << valid_old_log << " 合法,生成距今" << (now_ts - log_ts)/86400 << "天,是否清理:" << (need_clean ? "是" : "否") << "\n";
    }

    // 测试用例2:含非法字符的文件名
    std::string invalid_char_log = "201812aa0026_app.log.gz";
    std::cout << invalid_char_log << " 校验结果:" << (parse_log_time(invalid_char_log) ? "合法" : "非法") << "\n";

    // 测试用例3:不存在的日期(2月30日)
    std::string invalid_date_log = "202402301200_app.log.gz";
    std::cout << invalid_date_log << " 校验结果:" << (parse_log_time(invalid_date_log) ? "合法" : "非法") << "\n";

    return 0;
}
注意事项
  • 初始化tm结构时一定要做零值初始化(写struct tm tm{}),不要留未初始化的垃圾字段,避免比对逻辑出错。
  • 如果服务跨时区部署,记得统一使用UTC时间或者固定时区做时间差计算,避免夏令时切换导致的判断误差。
  • 该方案性能足够覆盖日志清理场景,文件名遍历的IO开销远大于时间校验的计算开销,不需要额外做性能优化。

内容的提问来源于stack exchange,提问作者kadina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:45:33