C++is_am is_pm上午下午判断

2026-03-23 08:45:40 1302阅读

C++ 中判断上午(AM)与下午(PM)的实用方法:is_amis_pm 的实现与应用

在日常开发中,尤其是处理时间显示、日志归档、用户界面本地化或跨时区调度任务时,准确区分“上午”(AM)与“下午”(PM)是基础但关键的需求。C++ 标准库并未直接提供 is_am()is_pm() 这类函数,但借助 <chrono><ctime> 及标准时间转换机制,我们可以稳健、可移植地实现这一逻辑。本文将系统讲解如何在 C++ 中判断 AM/PM 状态,涵盖 12 小时制转换原理、时区注意事项、常见陷阱及生产级实现方案。

AM/PM 的基本定义与转换规则

AM(Ante Meridiem)指午夜至正午前的时间段,即 12:00:00–11:59:59;PM(post Meridiem)则覆盖正午至午夜前,即 12:00:00–11:59:59。需特别注意两个边界点:

  • 00:00:00(午夜)对应 12:00 AM
  • 12:00:00(正午)对应 12:00 PM

因此,小时值 h(24 小时制)与 AM/PM 的映射关系为:

  • h == 0 → 12 AM(即 12:xx:xx AM)
  • 1 ≤ h ≤ 11h AM
  • h == 12 → 12 PM
  • 13 ≤ h ≤ 23(h - 12) PM

该逻辑构成了所有判断函数的核心依据。

基于 std::tm 的轻量级实现

当输入为 std::tm 结构(例如由 localtime()gmtime() 返回),可直接读取 tm_hour 字段进行判断:

#include <ctime>

// 判断给定 tm 是否处于 AM 时段(即 12:00 AM 至 11:59 AM)
bool is_am(const std::tm& t) {
    int h = t.tm_hour;
    return (h == 0) || (h >= 1 && h <= 11);
}

// 判断给定 tm 是否处于 PM 时段(即 12:00 PM 至 11:59 PM)
bool is_pm(const std::tm& t) {
    int h = t.tm_hour;
    return (h == 12) || (h >= 13 && h <= 23);
}

上述函数简洁、无副作用,适用于大多数本地时间场景。注意:std::tm::tm_hour 范围为 [0, 23],已标准化,无需额外校验。

面向 std::chrono::system_clock::time_point 的现代实现

C++11 起推荐使用 <chrono> 处理时间。要判断任意 time_point 的 AM/PM 状态,需先转换为本地日历时间:

#include <chrono>
#include <ctime>
#include <iomanip>

bool is_am(const std::chrono::system_clock::time_point& tp) {
    // 转换为 time_t,再获取本地 tm
    std::time_t t = std::chrono::system_clock::to_time_t(tp);
    std::tm local_tm{};
#ifdef _WIN32
    localtime_s(&local_tm, &t);  // Windows 安全版本
#else
    localtime_r(&t, &local_tm); // POSIX 线程安全版本
#endif
    return (local_tm.tm_hour == 0) || (local_tm.tm_hour >= 1 && local_tm.tm_hour <= 11);
}

bool is_pm(const std::chrono::system_clock::time_point& tp) {
    std::time_t t = std::chrono::system_clock::to_time_t(tp);
    std::tm local_tm{};
#ifdef _WIN32
    localtime_s(&local_tm, &t);
#else
    localtime_r(&t, &local_tm);
#endif
    return (local_tm.tm_hour == 12) || (local_tm.tm_hour >= 13 && local_tm.tm_hour <= 23);
}

此实现兼顾跨平台性与线程安全性,避免了 localtime() 的静态缓冲区竞争问题。

封装为可复用工具类(含格式化输出)

为提升工程可用性,可进一步封装为 Timeformatter 类,支持 AM/PM 判断与 12 小时制字符串生成:

#include <string>
#include <iomanip>
#include <sstream>

class Timeformatter {
public:
    static bool is_am(int hour_24) {
        return (hour_24 == 0) || (hour_24 >= 1 && hour_24 <= 11);
    }

    static bool is_pm(int hour_24) {
        return (hour_24 == 12) || (hour_24 >= 13 && hour_24 <= 23);
    }

    // 转换为 12 小时制小时数(1–12)
    static int to_12hour(int hour_24) {
        if (hour_24 == 0) return 12;
        if (hour_24 <= 12) return hour_24;
        return hour_24 - 12;
    }

    // 生成如 "02:30:45 PM" 的字符串
    static std::string format_12hour(const std::tm& t) {
        std::ostringstream oss;
        int h12 = to_12hour(t.tm_hour);
        oss << std::setfill('0') << std::setw(2) << h12 << ':'
            << std::setw(2) << t.tm_min << ':'
            << std::setw(2) << t.tm_sec << ' '
            << (is_am(t) ? "AM" : "PM");
        return oss.str();
    }
};

调用示例:

std::time_t now = std::time(nullptr);
std::tm* lt = std::localtime(&now);
std::cout << TimeFormatter::format_12hour(*lt) << '\n'; // e.g., "03:22:17 PM"

注意事项与最佳实践

  1. 时区敏感性is_am/is_pm 本质依赖本地时间解释。若需 UTC 时间判断,请使用 gmtime() 替代 localtime()
  2. 无效时间处理std::tm 若未初始化(如 memset(&t, 0, sizeof(t)) 后未填充),tm_hour 可能为随机值,务必确保输入有效。
  3. 夏令时过渡期:在 DST 开始/结束当日,本地时间可能出现重复或跳变,但 is_am/is_pm 仅依赖小时值,不受影响。
  4. 性能考量:对高频调用场景(如实时日志打点),建议缓存 tm_hour 值,避免重复 localtime 调用。

结语

C++ 中实现 AM/PM 判断并非复杂操作,核心在于理解 24 小时制到 12 小时制的数学映射,并合理选用标准库接口。本文提供的函数与类封装,兼顾了可读性、可移植性与生产健壮性,可直接集成至各类时间处理模块。掌握这一基础能力,不仅有助于提升时间相关功能的准确性,也为后续实现国际化(i18n)时间格式、定时任务调度等高级特性奠定坚实基础。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

目录[+]