A modern C++17 header-only library for parsing and manipulating human-readable time durations.
- Header-only: Easy integration into any C++ project
 - Modern C++17: Uses standard library chrono types
 - Flexible parsing: Supports multiple time formats and units
 - Type-safe: Built on 
std::chrono::secondsfor accuracy - Zero dependencies: No external libraries required for the core functionality
 - Cross-platform: Works on Windows, Linux, and macOS
 - SQL integration: Generate SQL-compatible interval strings
 
#include <timeduration/timeduration.hpp> #include <iostream> int main() { using namespace timeduration; // Parse a time duration string CTimePeriod duration("2h 30m 15s"); // Access individual components std::cout << "Hours: " << duration.hours() << std::endl; // 2 std::cout << "Minutes: " << duration.minutes() << std::endl; // 30 std::cout << "Seconds: " << duration.seconds() << std::endl; // 15 // Get total duration in seconds std::cout << "Total: " << duration.duration().count() << " seconds" << std::endl; // 9015 // Format back to string std::cout << "Formatted: " << duration.toString() << std::endl; // "2h 30m 15s" return 0; }-  
Clone the repository:
git clone https://github.com/anime-pdf/timeduration-cpp.git cd timeduration-cpp -  
Build and install:
mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release cmake --build . sudo cmake --install .
 -  
Use in your CMake project:
find_package(timeduration REQUIRED) target_link_libraries(your_target PRIVATE timeduration::timeduration)
 
Simply copy include/timeduration/timeduration.hpp to your project and include it:
#include "timeduration.hpp"The library supports both short and long forms of time units:
| Unit | Short Form | Long Form | Seconds | 
|---|---|---|---|
| Seconds | s |  seconds |  1 | 
| Minutes | m |  minutes |  60 | 
| Hours | h |  hours |  3,600 | 
| Days | d |  days |  86,400 | 
| Months | mo |  months |  2,419,200 (28 days) | 
| Years | y |  years |  31,536,000 (365 days) | 
using namespace timeduration; // Different formats CTimePeriod p1("5s"); // 5 seconds CTimePeriod p2("10m"); // 10 minutes  CTimePeriod p3("2h"); // 2 hours CTimePeriod p4("1d"); // 1 day // Combined formats CTimePeriod p5("1h 30m"); // 1 hour 30 minutes CTimePeriod p6("2d 5h 30m 15s"); // 2 days 5 hours 30 minutes 15 seconds // Long form CTimePeriod p7("1hours 30minutes 45seconds"); // Large units CTimePeriod p8("1y 6mo 15d"); // 1 year 6 months 15 days// From string CTimePeriod duration1("2h 30m"); // From components (seconds, minutes, hours, days) CTimePeriod duration2(15, 30, 2, 1); // 1d 2h 30m 15s // From std::chrono::seconds CTimePeriod duration3(std::chrono::seconds(3661)); // 1h 1m 1s // Using factory method auto duration4 = CTimePeriod::ParseFactory("5h 15m"); // Default constructor (zero duration) CTimePeriod duration5; // 0sCTimePeriod duration("1d 5h 30m 45s"); std::cout << "Days: " << duration.days() << std::endl; // 1 std::cout << "Hours: " << duration.hours() << std::endl; // 5 std::cout << "Minutes: " << duration.minutes() << std::endl; // 30 std::cout << "Seconds: " << duration.seconds() << std::endl; // 45 // Total duration as std::chrono::seconds auto total = duration.duration(); std::cout << "Total seconds: " << total.count() << std::endl; // 106245 // Check if zero if (duration.isZero()) { std::cout << "Duration is zero" << std::endl; }CTimePeriod duration("2h 30m 15s"); // Human-readable string std::cout << duration.toString() << std::endl; // "2h 30m 15s" // SQL interval format std::cout << duration.asSqlInterval() << std::endl; // "interval 9015 second"CTimePeriod short_duration("30m"); CTimePeriod long_duration("1h"); // Equality if (short_duration == long_duration) { /* ... */ } if (short_duration != long_duration) { /* ... */ } // Relational if (short_duration < long_duration) { /* true */ } if (short_duration > long_duration) { /* false */ } if (short_duration <= long_duration) { /* true */ } if (short_duration >= long_duration) { /* false */ }// Handle normalization automatically CTimePeriod duration(75); // 75 seconds becomes 1m 15s std::cout << duration.toString() << std::endl; // "1m 15s" // Accumulating durations CTimePeriod total; std::vector<std::string> durations = {"1h", "30m", "45s"}; for (const auto& d : durations) { CTimePeriod current(d); total = CTimePeriod(total.duration() + current.duration()); } std::cout << total.toString() << std::endl; // "1h 30m 45s" // Parse large numbers CTimePeriod huge("999h 123456s"); std::cout << huge.toString() << std::endl; // Normalized outputThe CScanner class handles the low-level tokenization of input strings:
- Token Recognition: Identifies numbers and unit suffixes
 - Flexible Parsing: Handles both "5m" and "5 minutes" formats
 - Accumulation: Combines multiple instances of the same unit (e.g., "5m 10m" = "15m")
 - Default Units: Numbers without units default to minutes
 
// Internal scanner usage (normally automatic) CTimePeriod::TokenHolder tokens = { {"s", 1L}, {"seconds", 1L}, {"m", 60L}, {"minutes", 60L}, {"h", 3600L}, {"hours", 3600L}, // ... more units }; CTimePeriod::CScanner scanner("2h 30m 15s", tokens); auto result = scanner.ScanTokens(); // result contains: {3600: 2, 60: 30, 1: 15}The parser converts scanner results into std::chrono::seconds:
- Multiplier Application: Applies time unit multipliers to values
 - Accumulation: Sums all components into total seconds
 - Normalization: Breaks down total seconds into days, hours, minutes, seconds
 
- Input: 
"2h 30m 15s" - Tokenization: 
{3600: 2, 60: 30, 1: 15} - Calculation: 
2×3600 + 30×60 + 15×1 = 9015 seconds - Normalization: 
9015s → 0d 2h 30m 15s 
- C++17 compatible compiler
 - CMake 3.14 or later
 - Git
 
# Configure with options cmake -B build \ -DCMAKE_BUILD_TYPE=Release \ -DTIMEDURATION_BUILD_TESTS=ON \ -DTIMEDURATION_BUILD_EXAMPLES=ON # Build cmake --build build --config Release # Run tests cd build && ctest --output-on-failure # Install cmake --install build --prefix /usr/local| Option | Default | Description | 
|---|---|---|
TIMEDURATION_BUILD_TESTS |  OFF |  Build unit tests | 
TIMEDURATION_BUILD_EXAMPLES |  OFF |  Build example programs | 
TIMEDURATION_DOWNLOAD_GTEST |  ON |  Auto-download Google Test if not found | 
The library includes comprehensive unit tests covering:
- Scanner functionality: Tokenization and parsing logic
 - Parser accuracy: Duration calculation and normalization
 - Edge cases: Zero durations, large numbers, malformed input
 - API completeness: All public methods and operators
 - Integration: Round-trip conversions and complex scenarios
 
Run tests with:
# Build with tests enabled cmake -B build -DTIMEDURATION_BUILD_TESTS=ON cmake --build build # Run all tests cd build && ctest --output-on-failure # Run specific test patterns cd build && ctest -R "Scanner" --verbose- Header-only: No runtime linking overhead
 - Parse caching: Consider caching parsed durations for repeated use
 - Memory efficient: Uses standard integers and chrono types
 - Stack allocated: No dynamic memory allocation during normal operation
 
The library follows a simple error handling strategy:
- Malformed input: Returns zero duration
 - Unknown units: Ignored during parsing
 - Overflow: Undefined behavior for extremely large values (same as standard integers)
 
// These all result in zero or partial parsing CTimePeriod invalid1(""); // Empty string → 0s CTimePeriod invalid2("invalid"); // No numbers → 0s CTimePeriod partial("5h invalid"); // Partial parse → 5h 0m 0s#include <chrono> #include <thread> CTimePeriod delay("5s"); std::this_thread::sleep_for(delay.duration());CTimePeriod retention("30d"); std::string query = "DELETE FROM logs WHERE created_at < NOW() - " + retention.asSqlInterval();#include <fstream> #include <sstream> std::ifstream config("config.txt"); std::string line; while (std::getline(config, line)) { if (line.starts_with("timeout=")) { std::string duration_str = line.substr(8); CTimePeriod timeout(duration_str); // Use timeout.duration() for network operations } }- Fork the repository
 - Create a feature branch: 
git checkout -b feature-name - Make your changes and add tests
 - Ensure all tests pass: 
cmake --build build && cd build && ctest - Submit a pull request
 
- Follow the existing code style
 - Use meaningful variable names
 - Add unit tests for new functionality
 - Update documentation for API changes
 
MIT License - see LICENSE file for details.
Q: Why use this instead of std::chrono parsing? A: std::chrono doesn't provide built-in parsing for human-readable formats like "2h 30m". This library bridges that gap.
Q: Are months and years exact? A: For simplicity, months are 28 days and years are 365 days. For precise calendar arithmetic, use a dedicated date/time library.
Q: Can I extend the supported units? A: Currently, units are hardcoded. Future versions may support custom unit definitions.
Q: What about negative durations? A: Negative durations are not currently supported. All parsed values are treated as positive.
Q: Is it thread-safe? A: Yes, the library is thread-safe for read operations. Multiple threads can safely parse durations simultaneously.