Select a language | 选择语言
A C++ header-only library that mimics the C# DateTime and TimeSpan classes, providing nearly identical functionality and usage.
- Single header — Just
#include "GL_DateTime.hpp", no extra dependencies - C#-style formatting — Use familiar format strings like
yyyy-MM-dd HH:mm:ss - Comprehensive parsing —
Parse()/TryParse()with automatic format detection or explicit format specification - Date/time arithmetic — Add/subtract years, months, days, hours, minutes, seconds, milliseconds, microseconds
- TimeSpan support — Full
TimeSpanclass mirroringSystem.TimeSpan(arithmetic, multiply/divide, comparison, formatting, microseconds) - DateTime arithmetic with TimeSpan —
DateTime ± TimeSpan = DateTime,DateTime - DateTime = TimeSpan - UTC/Local conversion —
ToUniversalTime()/ToLocalTime()withDateTimeKindtracking - Comparison operators — Full set:
==,!=,<,<=,>,>= - Stream operators — Direct
<</>>support forcin/cout - Ticks-based — Internal storage uses 100-nanosecond ticks since 0001-01-01, matching C# exactly
- Full date range — Supports years 0001 through 9999
- C++20 — Requires C++20
#include "GL_DateTime.hpp"
#include <iostream>
int main() {
// Current time
DateTime now = DateTime::Now();
std::cout << "Now: " << now << std::endl;
// Construct with specific date/time
DateTime dt(2024, 12, 25, 10, 30, 0, 123);
std::cout << dt.ToString("yyyy-MM-dd HH:mm:ss.fff") << std::endl;
// Parse from string
DateTime parsed = DateTime::Parse("2024-06-07 14:30:00");
// Date arithmetic with TimeSpan
DateTime tomorrow = now.AddDays(1);
TimeSpan diff = tomorrow - now;
std::cout << "Hours until tomorrow: " << diff.GetTotalHours() << std::endl;
return 0;
}This is a header-only library — just include the header:
g++ -std=c++20 -Iinclude your_program.cpp -o your_programOr use the provided Makefile to build the test:
mingw32-make test # Build test/test.cpp
./build/test.exe # Run tests| Constructor | Description |
|---|---|
DateTime() |
Default — initializes to MinValue (0001-01-01), matching C# |
DateTime(std::time_t t) |
From Unix timestamp (converted to local wall-clock time, Kind=Local) |
DateTime(int64_t ticks, DateTimeKind kind = Unspecified) |
From ticks and optional Kind |
DateTime(year, month, day, hour=0, minute=0, second=0, ms=0, kind=Unspecified) |
From components (throws on invalid values) |
| Method | Description |
|---|---|
DateTime::Now() |
Current local date and time |
DateTime::UtcNow() |
Current UTC date and time |
DateTime::Today() |
Today's date (time at 00:00:00) |
DateTime::MinValue() |
Minimum value (0001-01-01) |
DateTime::MaxValue() |
Maximum value (9999-12-31 23:59:59.9999999) |
DateTime::DaysInMonth(year, month) |
Number of days in the specified month |
DateTime::IsLeapYear(year) |
Whether the specified year is a leap year |
DateTime::Compare(dt1, dt2) |
Compares two DateTimes (-1 / 0 / 1) |
DateTime::Equals(dt1, dt2) |
Whether two DateTimes are equal |
DateTime::SpecifyKind(dt, kind) |
Returns new DateTime with specified Kind |
| Method | Description |
|---|---|
GetYear() / GetMonth() / GetDay() |
Year / Month / Day |
GetHour() / GetMinute() / GetSecond() |
Hour / Minute / Second |
GetMillisecond() |
Millisecond component |
GetMicrosecond() |
Microsecond component (.NET 7+) |
GetMicrosecond() |
Microsecond component (.NET 7+) |
GetDayOfWeek() |
Day of week (0=Sunday, matching C#) |
GetDayOfYear() |
Day of year (1-based) |
GetDate() |
New DateTime with time set to 00:00:00 |
GetTimeOfDay() |
Time since midnight as TimeSpan |
GetTicks() |
Number of 100-nanosecond ticks since 0001-01-01 |
GetKind() |
DateTimeKind (Unspecified / Utc / Local) |
ToTime_t() |
Convert to Unix time_t |
| Method | Description |
|---|---|
Add(TimeSpan) |
Add a TimeSpan |
AddDays(n) |
Add n days (n is double, fractional values supported) |
AddHours(n) |
Add n hours (fractional supported) |
AddMinutes(n) |
Add n minutes (fractional supported) |
AddSeconds(n) |
Add n seconds (fractional supported) |
AddMilliseconds(n) |
Add n milliseconds (fractional supported) |
AddTicks(n) |
Add n ticks (100 ns each) |
AddMicroseconds(n) |
Add n microseconds (.NET 7+, fractional supported) |
AddMonths(n) |
Add n months (auto-clamps to month end) |
AddYears(n) |
Add n years (handles Feb 29 in leap years) |
Subtract(TimeSpan) → DateTime |
Subtract a TimeSpan |
Subtract(DateTime) → TimeSpan |
Subtract another DateTime, return TimeSpan |
| Method | Description |
|---|---|
ToUniversalTime() |
Convert to UTC (updates Kind) |
ToLocalTime() |
Convert to local time (updates Kind) |
Uses C#-style format strings, including both predefined shorthand (single character) and custom format specifiers.
| Specifier | Meaning | Expanded Format |
|---|---|---|
d |
Short date | MM/dd/yyyy |
D |
Long date | dddd, MMMM dd, yyyy |
f |
Full date/time (short time) | dddd, MMMM dd, yyyy HH:mm |
F |
Full date/time (long time) | dddd, MMMM dd, yyyy HH:mm:ss |
g |
General (short time) | MM/dd/yyyy HH:mm |
G |
General (long time) | MM/dd/yyyy HH:mm:ss |
M / m |
Month/day | MMMM dd |
O / o |
Round-trip | yyyy-MM-ddTHH:mm:ss.fffffff + Kind suffix (Z / +08:00) |
R / r |
RFC1123 | ddd, dd MMM yyyy HH:mm:ss 'GMT' |
s |
Sortable | yyyy-MM-ddTHH:mm:ss |
t |
Short time | HH:mm |
T |
Long time | HH:mm:ss |
u |
Universal sortable | yyyy-MM-dd HH:mm:ss'Z' |
U |
Universal full | Converts to UTC, then uses F format |
Y / y |
Year/month | 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)| Specifier | Description | Example |
|---|---|---|
y / yy |
Short year | 24 |
yyyy |
Full year | 2024 |
M / MM |
Numeric month | 6 / 06 |
MMM |
Abbreviated month name | Jun |
MMMM |
Full month name | June |
d / dd |
Numeric day | 7 / 07 |
ddd |
Abbreviated weekday | Fri |
dddd |
Full weekday | Friday |
H / HH |
24-hour | 14 / 14 |
h / hh |
12-hour | 2 / 02 |
m / mm |
Minute | 5 / 05 |
s / ss |
Second | 3 / 03 |
f … fffffff |
Fractional seconds (100ns precision) | 7 / 78 / 789 / 7890123 |
F … FFFFFFF |
Same as f, but trailing zeros are omitted |
1 for 0.1s, empty for 0s |
t / tt |
AM/PM indicator (first char / full) | P / PM |
z / zz / zzz |
Local time zone offset | +8 / +08 / +08:00 |
K |
Kind specifier | Z (Utc) / +08:00 (Local) / empty (Unspecified) |
'text' |
Literal text escape | 'hello' → hello |
\x |
Single character escape | \d → d |
%x |
Force a single-character specifier | %d → 7 |
Note: In .NET a single-character format string is always a standard format specifier, so
ToString("f")means "full date/time (short time)", not "tenths of a second". UseToString("ss.fff")when you want fractional seconds.
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// Auto-detect common formats
DateTime dt = DateTime::Parse("2024-06-07 14:30:00");
DateTime dt = DateTime::Parse("06/07/2024");
// Parse with explicit format
DateTime dt = DateTime::Parse("2024/06/07 14:30:00", "yyyy/MM/dd HH:mm:ss");
DateTime dt = DateTime::Parse("06/07/2024 02:30:00 PM", "MM/dd/yyyy hh:mm:ss tt");
// Parse with predefined format shorthand
DateTime dt = DateTime::Parse("06/07/2024", "d"); // short date
DateTime dt = DateTime::Parse("2024-06-07T14:30:00", "s"); // sortable
// TryParse (safe version)
DateTime result;
if (DateTime::TryParse("2024-06-07", result)) {
// Parsed successfully
}
// On failure it returns false and sets result to DateTime::MinValue (same as C#)Parsing behavior matches C#:
- Leading/trailing white space is ignored; fractional seconds (
2024-06-07 14:30:00.1234567) and time-only strings (14:30, date defaults to today) are supported - A missing year defaults to the current year (e.g.
June 07); 12-hour clock withAM/PMis supported - When the string carries a time zone designator (
Z/GMT/+08:00/-0500), the value is converted to local time (Kind=Local) - The predefined
u/Rformats are likewise converted to local time Parsethrowsstd::runtime_erroron failure (TryParsedoes not)
dt1 + ts // DateTime + TimeSpan → DateTime
ts + dt1 // TimeSpan + DateTime → DateTime
dt1 - ts // DateTime - TimeSpan → DateTime
dt1 - dt2 // DateTime - DateTime → TimeSpan
dt1 == dt2 // equality
dt1 != dt2 // inequality
dt1 < dt2 // less than
dt1 <= dt2 // less than or equal
dt1 > dt2 // greater than
dt1 >= dt2 // greater than or equalDateTime dt = DateTime::Now();
std::cout << dt << std::endl; // Outputs: 2026-06-07 18:24:20
std::cin >> dt; // Input: 2024-06-07 14:30:00A complete TimeSpan class mirroring System.TimeSpan.
| Constructor | Description |
|---|---|
TimeSpan() |
Zero value |
TimeSpan(int64_t ticks) |
From ticks (100-ns intervals) |
TimeSpan(hours, minutes, seconds) |
From time components |
TimeSpan(days, hours, minutes, seconds) |
From day + time components |
TimeSpan(days, hours, minutes, seconds, milliseconds) |
Full constructor |
| Method | Description |
|---|---|
GetDays() / GetHours() / GetMinutes() / GetSeconds() / GetMilliseconds() / GetMicroseconds() |
Component values (microseconds: .NET 7+) |
GetTicks() |
Total ticks |
GetTotalDays() / GetTotalHours() / GetTotalMinutes() / GetTotalSeconds() / GetTotalMilliseconds() |
Total value as double |
TimeSpan::Zero() // TimeSpan(0)
TimeSpan::MinValue() // Minimum possible TimeSpan
TimeSpan::MaxValue() // Maximum possible TimeSpan
TimeSpan::FromDays(1.5) // 1.5 days → TimeSpan
TimeSpan::FromHours(2.5) // 2.5 hours → TimeSpan
// ... FromMinutes, FromSeconds, FromMilliseconds, FromMicroseconds (.NET 7+)ts1 + ts2 // addition
ts1 - ts2 // subtraction
-ts1 // negation
ts1.Multiply(2) // multiplication (also ts1 * 2.0)
ts1.Divide(2) // division (also ts1 / 2.0)
ts1.Divide(ts2) // ratio as double (also ts1 / ts2)
ts1.Duration() // absolute value
ts1.CompareTo(ts2) // comparisonOverflow, NaN and division by zero throw (mirroring C#
OverflowException/ArgumentException); dividing two TimeSpans (ts1 / ts2) returns adoubleand yields ±∞ when the divisor is zero (no exception).
ts1 + ts2 // addition
ts1 - ts2 // subtraction
-ts1 // negation
ts1 * 2.0 // multiply by factor
ts1 / 2.0 // divide by divisor
ts1 / ts2 // ratio (double)
ts1 == ts2 // equality
ts1 != ts2 // inequality
ts1 < ts2 // less than
ts1 <= ts2 // less than or equal
ts1 > ts2 // greater than
ts1 >= ts2 // greater than or equalTimeSpan ts(1, 2, 30, 0); // 1 day, 2 hours, 30 minutes
std::cout << ts; // Output: 1.02:30:00ToString() matches the C# "c" format: the days element is omitted for intervals shorter than one day, and fractional seconds are always written with 7 digits:
TimeSpan::FromSeconds(1.5).ToString(); // "00:00:01.5000000"
TimeSpan(0, 0, 1).ToString(); // "00:00:01"enum class DateTimeKind {
Unspecified = 0, // Default
Utc = 1,
Local = 2
};The ticks stored inside a DateTime always represent the wall-clock time in the time zone indicated by its Kind, and the Kind only tells which time zone that value belongs to:
Local— ticks are local time, produced byDateTime::Now()Utc— ticks are UTC time, produced byDateTime::UtcNow()Unspecified— not specified, used as-is (e.g. built from components or returned byParse)
Therefore every accessor/formatter (GetYear(), GetHour(), ToString(), GetDayOfWeek(), ...) decomposes the ticks literally, without any time zone conversion:
DateTime dt(2024, 6, 7, 14, 5, 3); // Unspecified
std::cout << dt.ToString(); // 2024-06-07 14:05:03 (independent of the local time zone)
std::cout << dt.GetHour(); // 14
std::cout << DateTime::Now().GetHour(); // local hour
std::cout << DateTime::UtcNow().GetHour(); // UTC hourOnly ToUniversalTime() / ToLocalTime() actually perform a time zone conversion and tag the result with the corresponding Kind. ToTime_t() is Kind-aware as well (Local/Unspecified are treated as local time).
By default, month and weekday names in ToString use the system language. To switch languages, call setlocale from <locale.h>:
#include <locale.h>
// Windows - US English
setlocale(LC_ALL, "English_United States.1252");
// Windows - Simplified Chinese
setlocale(LC_ALL, "Chinese (Simplified)_China.UTF-8");Note:
"o"/"R"/"s"/"u"are invariant formats — their month/weekday names are always English abbreviations and are not affected bysetlocale. Accordingly,Parseonly recognizes English month/weekday names (see "Differences from C# DateTime" below).
DateTimeForCpp/
├── include/
│ └── GL_DateTime.hpp # Main file
├── test/
│ └── test.cpp # Test program
├── Makefile # Makefile
├── CHANGELOG.md # English changelog
├── CHANGELOG_cn.md # Chinese changelog
├── README.md # English Document
├── README_cn.md # Chinese Document
└── LICENSE
Behaviors that have already been aligned are not repeated here (see CHANGELOG.md); the remaining differences are:
Behavioral differences
- The predefined formats (
d/D/f/F/g/G/t/T/M/Y) andToShortDateString()/ToLongDateString()/ToShortTimeString()/ToLongTimeString()use fixed invariant-culture patterns, while C# uses the current culture (under zh-CN,ToString("d")yields06/07/2024instead of2024/6/7);"o"/"R"/"s"/"u"are invariant in both, as in C# - Unrecognized format specifiers are emitted literally, whereas C# throws
FormatException(the library's predefined formats and examples use unquotedT/Zliterals, so the lenient behavior is kept on purpose) - Time zone conversion relies on the platform's
mktime/timegmand only applies within the range those functions support (roughly 1970–3000 on Windows). Outside that range (for example before 1970 or after year 5000)ToUniversalTime()/ToLocalTime()do no offset adjustment andToTime_t()returns 0, whereas C# ships its own time zone data and covers the full 0001–9999 range - Month/weekday names are formatted according to
setlocale, while parsing only accepts English names, so under a non-English locale you can produce a string you cannot parse back ("R"and other invariant formats are unaffected)
Not implemented yet
ParseExact/TryParseExactTimeSpan.Parse/TryParse/ToString(format)ToBinary/FromBinary,ToFileTime/FromFileTime,ToOADate/FromOADate,IsDaylightSavingTimeNanosecond/AddNanosecondsDateTimeOffset
This project is open source under the MIT License. See LICENSE.