Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Select a language | 选择语言

简体中文
English

DateTime for C++

A C++ header-only library that mimics the C# DateTime and TimeSpan classes, providing nearly identical functionality and usage.

Features

  • 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 TimeSpan class mirroring System.TimeSpan (arithmetic, multiply/divide, comparison, formatting, microseconds)
  • DateTime arithmetic with TimeSpan — DateTime ± TimeSpan = DateTime, DateTime - DateTime = TimeSpan
  • UTC/Local conversion — ToUniversalTime() / ToLocalTime() with DateTimeKind tracking
  • Comparison operators — Full set: ==, !=, <, <=, >, >=
  • Stream operators — Direct << / >> support for cin/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

Quick Start

#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;
}

Building

This is a header-only library — just include the header:

g++ -std=c++20 -Iinclude your_program.cpp -o your_program

Or use the provided Makefile to build the test:

mingw32-make test    # Build test/test.cpp
./build/test.exe      # Run tests

API Reference

DateTime Constructors

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)

DateTime Static Methods

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

DateTime Properties (Getters)

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

DateTime Arithmetic

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

DateTime Conversion

Method Description
ToUniversalTime() Convert to UTC (updates Kind)
ToLocalTime() Convert to local time (updates Kind)

ToString Format

Uses C#-style format strings, including both predefined shorthand (single character) and custom format specifiers.

Predefined 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)

Custom Format Specifiers

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". Use ToString("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

Parse / TryParse

// 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 with AM/PM is 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 / R formats are likewise converted to local time
  • Parse throws std::runtime_error on failure (TryParse does not)

DateTime Operators

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 equal

Stream Operators

DateTime dt = DateTime::Now();
std::cout << dt << std::endl;         // Outputs: 2026-06-07 18:24:20

std::cin >> dt;                        // Input: 2024-06-07 14:30:00

TimeSpan

A complete TimeSpan class mirroring System.TimeSpan.

TimeSpan Constructors

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

TimeSpan Properties

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 Static Methods

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+)

TimeSpan Arithmetic

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)  // comparison

Overflow, NaN and division by zero throw (mirroring C# OverflowException / ArgumentException); dividing two TimeSpans (ts1 / ts2) returns a double and yields ±∞ when the divisor is zero (no exception).

TimeSpan Operators

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 equal

TimeSpan Formatting

TimeSpan ts(1, 2, 30, 0);    // 1 day, 2 hours, 30 minutes
std::cout << ts;             // Output: 1.02:30:00

ToString() 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"

DateTimeKind

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 by DateTime::Now()
  • Utc — ticks are UTC time, produced by DateTime::UtcNow()
  • Unspecified — not specified, used as-is (e.g. built from components or returned by Parse)

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 hour

Only 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).

Localization

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 by setlocale. Accordingly, Parse only recognizes English month/weekday names (see "Differences from C# DateTime" below).

Project Structure

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

Differences from C# DateTime

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) and ToShortDateString() / ToLongDateString() / ToShortTimeString() / ToLongTimeString() use fixed invariant-culture patterns, while C# uses the current culture (under zh-CN, ToString("d") yields 06/07/2024 instead of 2024/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 unquoted T/Z literals, so the lenient behavior is kept on purpose)
  • Time zone conversion relies on the platform's mktime / timegm and 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 and ToTime_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 / TryParseExact
  • TimeSpan.Parse / TryParse / ToString(format)
  • ToBinary / FromBinary, ToFileTime / FromFileTime, ToOADate / FromOADate, IsDaylightSavingTime
  • Nanosecond / AddNanoseconds
  • DateTimeOffset

License

This project is open source under the MIT License. See LICENSE.

About

A C++ header-only library that mimics the C# DateTime class, providing nearly identical functionality and usage. 一个模仿 C# DateTime 类的 C++ 头文件库,提供近乎一致的功能和用法。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages