选择语言 | Select a language
一个模仿 C# DateTime 和 TimeSpan 类的 C++ 头文件库,提供近乎一致的功能和用法。
- 单头文件 — 仅需包含
GL_DateTime.hpp,无额外依赖 - C# 风格格式化 — 使用
yyyy-MM-dd HH:mm:ss等格式字符串 - 全面的解析支持 —
Parse()/TryParse()自动推断常见格式或指定格式解析 - 日期时间运算 — 添加/减少 年、月、日、时、分、秒、毫秒、微秒
- TimeSpan 支持 — 完整的
TimeSpan类(算术、乘除、比较、格式化、微秒) - DateTime 与 TimeSpan 运算 —
DateTime ± TimeSpan = DateTime,DateTime - DateTime = TimeSpan - UTC/本地转换 —
ToUniversalTime()/ToLocalTime(),附带DateTimeKind追踪 - 比较运算 — 完整的
==、!=、<、<=、>、>=操作符 - 流操作符 — 支持
<</>>直接与cin/cout交互 - Ticks 存储 — 内部使用 100 纳秒间隔的 ticks(自 0001-01-01 起),与 C# 完全一致
- 完整日期范围 — 支持公元 1 年至 9999 年
- C++20 — 使用 C++20 标准
#include "GL_DateTime.hpp"
#include <iostream>
int main() {
// 当前时间
DateTime now = DateTime::Now();
std::cout << "Now: " << now << std::endl;
// 构造指定时间
DateTime dt(2024, 12, 25, 10, 30, 0, 123);
std::cout << dt.ToString("yyyy-MM-dd HH:mm:ss.fff") << std::endl;
// 解析字符串
DateTime parsed = DateTime::Parse("2024-06-07 14:30:00");
// 使用 TimeSpan 做日期运算
DateTime tomorrow = now.AddDays(1);
TimeSpan diff = tomorrow - now;
std::cout << "距离明天还有 " << diff.GetTotalHours() << " 小时" << std::endl;
return 0;
}本项目仅需包含头文件即可使用:
g++ -std=c++20 -Iinclude your_program.cpp -o your_program也可使用 Makefile 编译测试:
mingw32-make test # 编译 test/test.cpp
./build/test.exe # 运行测试| 构造函数 | 说明 |
|---|---|
DateTime() |
默认构造,初始化为 MinValue (0001-01-01),与 C# 一致 |
DateTime(std::time_t t) |
从 Unix 时间戳构造(换算为本地墙钟时间,Kind=Local) |
DateTime(int64_t ticks, DateTimeKind kind = Unspecified) |
从 ticks 和可选 Kind 构造 |
DateTime(year, month, day, hour=0, minute=0, second=0, ms=0, kind=Unspecified) |
从各分量构造(超出范围抛异常) |
| 方法 | 说明 |
|---|---|
DateTime::Now() |
当前本地时间 |
DateTime::UtcNow() |
当前 UTC 时间 |
DateTime::Today() |
当天日期(时分秒归零) |
DateTime::MinValue() |
最小值 (0001-01-01) |
DateTime::MaxValue() |
最大值 (9999-12-31 23:59:59.9999999) |
DateTime::DaysInMonth(year, month) |
指定月份的天数 |
DateTime::IsLeapYear(year) |
指定年份是否为闰年 |
DateTime::Compare(dt1, dt2) |
比较两个 DateTime (-1 / 0 / 1) |
DateTime::Equals(dt1, dt2) |
判断两个 DateTime 是否相等 |
DateTime::SpecifyKind(dt, kind) |
返回具有指定 Kind 的新 DateTime |
| 方法 | 说明 |
|---|---|
GetYear() / GetMonth() / GetDay() |
年/月/日 |
GetHour() / GetMinute() / GetSecond() |
时/分/秒 |
GetMillisecond() |
毫秒 |
GetMicrosecond() |
微秒(.NET 7+) |
GetDayOfWeek() |
星期几 (0=周日,与 C# 一致) |
GetDayOfYear() |
一年中的第几天(从 1 开始) |
GetDate() |
返回日期部分(时间归零)的新 DateTime |
GetTimeOfDay() |
返回当天时间作为 TimeSpan |
GetTicks() |
自 0001-01-01 以来的 100 纳秒间隔数 |
GetKind() |
DateTimeKind (Unspecified / Utc / Local) |
ToTime_t() |
转换为 Unix time_t |
| 方法 | 说明 |
|---|---|
Add(TimeSpan) |
添加时间间隔 |
AddDays(n) |
添加 n 天(n 为 double,支持小数) |
AddHours(n) |
添加 n 小时(支持小数) |
AddMinutes(n) |
添加 n 分钟(支持小数) |
AddSeconds(n) |
添加 n 秒(支持小数) |
AddMilliseconds(n) |
添加 n 毫秒(支持小数) |
AddTicks(n) |
添加 n 个刻度(100 纳秒) |
AddMicroseconds(n) |
添加 n 微秒(.NET 7+,支持小数) |
AddMonths(n) |
添加 n 月(自动处理月末对齐) |
AddYears(n) |
添加 n 年(自动处理闰年2月29) |
Subtract(TimeSpan) → DateTime |
减去一个 TimeSpan |
Subtract(DateTime) → TimeSpan |
减去另一个 DateTime,返回 TimeSpan 差 |
| 方法 | 说明 |
|---|---|
ToUniversalTime() |
转换为 UTC(更新 Kind) |
ToLocalTime() |
转换为本地时间(更新 Kind) |
使用 C# 风格的格式字符串,同时支持预定义简写(单字符)和自定义格式说明符。
| 说明符 | 含义 | 展开格式 |
|---|---|---|
d |
短日期 | MM/dd/yyyy |
D |
长日期 | dddd, MMMM dd, yyyy |
f |
完整日期+短时间 | dddd, MMMM dd, yyyy HH:mm |
F |
完整日期+长时间 | dddd, MMMM dd, yyyy HH:mm:ss |
g |
常规+短时间 | MM/dd/yyyy HH:mm |
G |
常规+长时间 | MM/dd/yyyy HH:mm:ss |
M / m |
月/日 | MMMM dd |
O / o |
往返(Round-trip) | yyyy-MM-ddTHH:mm:ss.fffffff + Kind 后缀(Z / +08:00) |
R / r |
RFC1123 | ddd, dd MMM yyyy HH:mm:ss 'GMT' |
s |
可排序(Sortable) | yyyy-MM-ddTHH:mm:ss |
t |
短时间 | HH:mm |
T |
长时间 | HH:mm:ss |
u |
通用可排序 | yyyy-MM-dd HH:mm:ss'Z' |
U |
通用完整 | 先转换 UTC,再使用 F 格式 |
Y / y |
年/月 | yyyy MMMM |
dt.ToString("d"); // "06/07/2024"
dt.ToString("D"); // "Friday, June 07, 2024"
dt.ToString("o"); // "2024-06-07T14:30:45.7890000"
dt.ToString("s"); // "2024-06-07T14:30:45"
dt.ToString("U"); // "Friday, June 07, 2024 06:30:45" (UTC)| 格式符 | 说明 | 示例 |
|---|---|---|
y / yy |
短年份 | 24 |
yyyy |
完整年份 | 2024 |
M / MM |
数字月份 | 6 / 06 |
MMM |
缩写月份名 | Jun |
MMMM |
完整月份名 | June |
d / dd |
数字日期 | 7 / 07 |
ddd |
缩写星期名 | Fri |
dddd |
完整星期名 | Friday |
H / HH |
24小时制 | 14 / 14 |
h / hh |
12小时制 | 2 / 02 |
m / mm |
分钟 | 5 / 05 |
s / ss |
秒钟 | 3 / 03 |
f … fffffff |
秒的小数部分(100ns 精度) | 7 / 78 / 789 / 7890123 |
F … FFFFFFF |
同上,但省略尾随零 | 0.1 秒 → 1,整秒 → 空 |
t / tt |
AM/PM 指示符(首字符 / 完整) | P / PM |
z / zz / zzz |
本地时区偏移 | +8 / +08 / +08:00 |
K |
时区说明符 | Z(Utc)/ +08:00(Local)/ 空(Unspecified) |
'text' |
文本引号 | '年' → 年 |
\x |
单字符转义 | \d → d |
%x |
强制单字符说明符 | %d → 7 |
注意: 与 .NET 一致,单字符格式串总是预定义格式,因此
ToString("f")表示“完整日期/短时间”而非“十分之一秒”;需要小数秒请写在更长的格式里,如ToString("ss.fff")。
dt.ToString("yyyy-MM-dd HH:mm:ss"); // 2024-06-07 14:05:03
dt.ToString("hh:mm tt"); // 02:05 PM
dt.ToString("dddd, MMMM d, yyyy"); // Friday, June 7, 2024
dt.ToString("yyyy'年'MM'月'dd'日'"); // 2024年06月07日
dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); // 2024-06-07 14:05:03.789
dt.ToShortDateString(); // 2024-06-07
dt.ToLongDateString(); // Friday, June 07, 2024
dt.ToShortTimeString(); // 14:05
dt.ToLongTimeString(); // 14:05:03// 自动推断常见格式
DateTime dt = DateTime::Parse("2024-06-07 14:30:00");
DateTime dt = DateTime::Parse("06/07/2024");
// 指定格式解析
DateTime dt = DateTime::Parse("2024年06月07日", "yyyy'年'MM'月'dd'日'");
DateTime dt = DateTime::Parse("06/07/2024 02:30:00 PM", "MM/dd/yyyy hh:mm:ss tt");
// 使用预定义格式简写
DateTime dt = DateTime::Parse("06/07/2024", "d"); // 短日期
DateTime dt = DateTime::Parse("2024-06-07T14:30:00", "s"); // 可排序格式
// TryParse(安全版本)
DateTime result;
if (DateTime::TryParse("2024-06-07", result)) {
// 解析成功
}
// 解析失败时会返回 false,并把 result 置为 DateTime::MinValue(与 C# 一致)解析行为与 C# 保持一致:
- 忽略首尾空白;支持小数秒(
2024-06-07 14:30:00.1234567)与纯时间(14:30,日期取今天) - 格式中没有年份时取当前年(如
June 07);12 小时制 + PM/AM也可解析 - 字符串带时区标记(
Z/GMT/+08:00/-0500)时,会换算为本地时间(Kind=Local) - 使用预定义
u/R等 UTC 格式时同样换算为本地时间 - 解析失败会抛出
std::runtime_error(TryParse不会)
dt1 + ts // DateTime + TimeSpan → DateTime
ts + dt1 // TimeSpan + DateTime → DateTime
dt1 - ts // DateTime - TimeSpan → DateTime
dt1 - dt2 // DateTime - DateTime → TimeSpan
dt1 == dt2 // 相等
dt1 != dt2 // 不等
dt1 < dt2 // 小于
dt1 <= dt2 // 小于等于
dt1 > dt2 // 大于
dt1 >= dt2 // 大于等于DateTime dt = DateTime::Now();
std::cout << dt << std::endl; // 输出: 2026-06-07 18:24:20
std::cin >> dt; // 输入: 2024-06-07 14:30:00完整的 TimeSpan 类,与 System.TimeSpan 一致。
| 构造函数 | 说明 |
|---|---|
TimeSpan() |
零值 |
TimeSpan(int64_t ticks) |
从刻度数构造(100 纳秒间隔) |
TimeSpan(hours, minutes, seconds) |
从时间分量构造 |
TimeSpan(days, hours, minutes, seconds) |
从天 + 时间分量构造 |
TimeSpan(days, hours, minutes, seconds, milliseconds) |
完整构造函数 |
| 方法 | 说明 |
|---|---|
GetDays() / GetHours() / GetMinutes() / GetSeconds() / GetMilliseconds() / GetMicroseconds() |
各分量值(微秒对应 .NET 7+) |
GetTicks() |
总刻度数 |
GetTotalDays() / GetTotalHours() / GetTotalMinutes() / GetTotalSeconds() / GetTotalMilliseconds() / GetTotalMicroseconds() |
以 double 表示的总值 |
TimeSpan::Zero() // TimeSpan(0)
TimeSpan::MinValue() // 最小可能的 TimeSpan
TimeSpan::MaxValue() // 最大可能的 TimeSpan
TimeSpan::FromDays(1.5) // 1.5 天 → TimeSpan
TimeSpan::FromHours(2.5) // 2.5 小时 → TimeSpan
// ... FromMinutes, FromSeconds, FromMilliseconds, FromMicroseconds(.NET 7+)ts1 + ts2 // 加法
ts1 - ts2 // 减法
-ts1 // 取反
ts1.Multiply(2) // 乘法(也可写 ts1 * 2.0)
ts1.Divide(2) // 除法(也可写 ts1 / 2.0)
ts1.Divide(ts2) // 比值,返回 double(也可写 ts1 / ts2)
ts1.Duration() // 绝对值
ts1.CompareTo(ts2) // 比较溢出 / NaN / 除以 0 会抛异常(与 C# 的
OverflowException、ArgumentException对应);ts1 / ts2这种两个 TimeSpan 相除则返回double,除数为零时得到 ±∞(不抛异常)。
ts1 + ts2 // 加法
ts1 - ts2 // 减法
-ts1 // 取反
ts1 * 2.0 // 乘以倍数
ts1 / 2.0 // 除以倍数
ts1 / ts2 // 比值(double)
ts1 == ts2 // 相等
ts1 != ts2 // 不等
ts1 < ts2 // 小于
ts1 <= ts2 // 小于等于
ts1 > ts2 // 大于
ts1 >= ts2 // 大于等于TimeSpan ts(1, 2, 30, 0); // 1天2小时30分钟
std::cout << ts; // 输出: 1.02:30:00ToString() 与 C# 的 "c" 格式一致:不足 1 天时不输出天数部分,有小数秒时固定输出 7 位:
TimeSpan::FromSeconds(1.5).ToString(); // "00:00:01.5000000"
TimeSpan(0, 0, 1).ToString(); // "00:00:01"enum class DateTimeKind {
Unspecified = 0, // 默认
Utc = 1,
Local = 2
};DateTime 内部的 ticks 保存的始终是 Kind 所对应时区下的“墙钟时间”,Kind 只说明该值属于哪个时区:
Local— ticks 表示本地时间,DateTime::Now()产生这种值Utc— ticks 表示 UTC 时间,DateTime::UtcNow()产生这种值Unspecified— 未指定,按字面值使用(如由年月日构造、Parse得到的值)
因此所有取值/格式化接口(GetYear()、GetHour()、ToString()、GetDayOfWeek() 等)都直接按字面值分解 ticks,不做任何时区换算:
DateTime dt(2024, 6, 7, 14, 5, 3); // Unspecified
std::cout << dt.ToString(); // 2024-06-07 14:05:03(不受本地时区影响)
std::cout << dt.GetHour(); // 14
std::cout << DateTime::Now().GetHour(); // 本地小时
std::cout << DateTime::UtcNow().GetHour(); // UTC 小时只有 ToUniversalTime() / ToLocalTime() 会真正进行时区换算,并把结果标记为对应的 Kind。ToTime_t() 同样按 Kind 换算(Local/Unspecified 视为本地时间)。
默认情况下,ToString 中的月份名和星期名使用系统语言。若要切换语言,调用 <locale.h> 的 setlocale 函数:
#include <locale.h>
// Windows - 简体中文
setlocale(LC_ALL, "Chinese (Simplified)_China.UTF-8");
// Windows - 美国英语
setlocale(LC_ALL, "English_United States.1252");注意:
"o"/"R"/"s"/"u"属于不变区域性格式,其中的月份/星期名固定为英文缩写, 不受setlocale影响;相应地,Parse解析月份/星期名时也只识别英文(详见下方“与 C# DateTime 的差异”)。
DateTimeForCpp/
├── include/
│ └── GL_DateTime.hpp # 主文件
├── test/
│ └── test.cpp # 测试程序
├── Makefile # 构建配置
├── CHANGELOG.md # 英文更新日志
├── CHANGELOG_cn.md # 中文更新日志
├── README.md # 英文文档
├── README_cn.md # 中文文档
└── LICENSE
已经对齐的行为不再赘述(详见 CHANGELOG_cn.md),当前仍需注意的差异如下:
行为差异
- 预定义格式(
d/D/f/F/g/G/t/T/M/Y)与ToShortDateString()/ToLongDateString()/ToShortTimeString()/ToLongTimeString()使用固定的不变区域性模式,而 C# 使用当前区域性 (例如 zh-CN 下ToString("d")得到06/07/2024而非2024/6/7);"o"/"R"/"s"/"u"与 C# 一样固定使用不变区域性 - 无法识别的格式说明符会原样输出,C# 会抛
FormatException(本库的预定义格式与文档示例中T/Z是未加引号的字面量,故保留宽松行为) - 时区换算依赖平台的时间转换函数(
mktime/timegm),仅在平台支持的范围(Windows 上约为 1970–3000 年) 内生效;范围外(如 1970 年之前、5000 年之后)ToUniversalTime()/ToLocalTime()不做偏移换算、ToTime_t()返回 0;而 C# 使用自带的时区数据,可覆盖完整的 0001–9999 年 - 月份名/星期名的格式化跟随
setlocale,而解析只识别英文名, 因此非英文 locale 下存在“能格式化、但解析不回来”的情况 ("R"等不变区域性格式不受影响)
尚未实现的 API
ParseExact/TryParseExactTimeSpan.Parse/TryParse/ToString(format)ToBinary/FromBinary、ToFileTime/FromFileTime、ToOADate/FromOADate、IsDaylightSavingTimeNanosecond/AddNanosecondsDateTimeOffset
本项目基于 MIT 许可证开源。详见 LICENSE。