Skip to content

Commit 7a95ca1

Browse files
committed
refactor!: consolidate formatting API
BREAKING CHANGE: Rename Format enum values (Iso8601Basic→Iso8601, Iso8601Extended→Iso8601Precise, DateOnly→Iso8601Date, TimeOnly→Iso8601Time), fractional seconds now always 7 digits, DateTimeOffset uses explicit offset format, removed toIso8601Extended()
1 parent 9100046 commit 7a95ca1

File tree

11 files changed

+81
-195
lines changed

11 files changed

+81
-195
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16-
- NIL
16+
- **BREAKING**: DateTime::Format enum renamed (`Iso8601Basic``Iso8601`, `Iso8601Extended``Iso8601Precise`, `DateOnly``Iso8601Date`, `TimeOnly``Iso8601Time`)
17+
- **BREAKING**: Fractional seconds always display exactly 7 digits (tick precision) in `Iso8601Precise` format
18+
- **BREAKING**: DateTimeOffset offset always explicit (`±HH:MM`), removed `Z` notation for UTC
19+
- **BREAKING**: toString() consolidated to single method with default parameter
1720

1821
### Deprecated
1922

2023
- NIL
2124

2225
### Removed
2326

24-
- NIL
27+
- **BREAKING**: Standalone `toIso8601Extended()` method (use `toString(Format::Iso8601Precise)`)
2528

2629
### Fixed
2730

@@ -31,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3134

3235
- NIL
3336

37+
3438
## [0.1.1] - 2025-11-27
3539

3640
### Changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ TimeSpan oneHour = TimeSpan::fromHours(1);
221221
DateTime later = dt1 + oneHour;
222222

223223
// Formatting
224-
std::string iso = dt1.toIso8601Extended(); // "2025-01-24T05:42:00.0000000Z"
224+
std::string iso = dt1.toString(DateTime::Format::Iso8601); // "2025-01-24T05:42:00Z"
225+
std::string precise = dt1.toString(DateTime::Format::Iso8601Precise); // "2025-01-24T05:42:00.0000000Z"
225226

226227
// Epoch timestamp conversions
227228
std::int64_t epochSeconds = dt1.toEpochSeconds();
@@ -279,8 +280,8 @@ std::int64_t epochMillis = dto1.toEpochMilliseconds();
279280
DateTimeOffset fromEpoch = DateTimeOffset::fromEpochSeconds(epochSeconds);
280281
281282
// Formatting
282-
std::string iso = dto1.toString(); // "2025-01-24T05:42:00+02:00"
283-
std::string extended = dto1.toIso8601Extended(); // "2025-01-24T05:42:00.0000000+02:00"
283+
std::string iso = dto1.toString(); // "2025-01-24T05:42:00+02:00"
284+
std::string precise = dto1.toString(DateTime::Format::Iso8601Precise); // "2025-01-24T05:42:00.0000000+02:00"
284285
```
285286

286287
### TimeSpan - Duration Calculations
@@ -355,12 +356,12 @@ int main()
355356
356357
// Get current UTC time
357358
DateTime now = DateTime::utcNow();
358-
std::cout << "Current UTC time: " << now.toIso8601Extended() << std::endl;
359+
std::cout << "Current UTC time: " << now.toString(DateTime::Format::Iso8601Precise) << std::endl;
359360
360361
// Calculate future date using TimeSpan
361362
TimeSpan oneWeek = TimeSpan::fromDays(7);
362363
DateTime nextWeek = now + oneWeek;
363-
std::cout << "One week from now: " << nextWeek.toIso8601Extended() << std::endl;
364+
std::cout << "One week from now: " << nextWeek.toString(DateTime::Format::Iso8601Precise) << std::endl;
364365
365366
// Work with timezones
366367
DateTimeOffset localNow = DateTimeOffset::now();
@@ -510,4 +511,4 @@ All dependencies are automatically fetched via CMake FetchContent when building
510511

511512
---
512513

513-
_Updated on November 17, 2025_
514+
_Updated on January 05, 2026_

benchmark/BM_DateTime.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ namespace nfx::time::benchmark
118118
}
119119
}
120120

121-
static void BM_DateTime_toIso8601Extended( ::benchmark::State& state )
121+
static void BM_DateTime_toIso8601Precise( ::benchmark::State& state )
122122
{
123123
auto dt{ DateTime::utcNow() };
124124

125125
for ( auto _ : state )
126126
{
127-
auto str{ dt.toIso8601Extended() };
127+
auto str{ dt.toString( DateTime::Format::Iso8601Precise ) };
128128
::benchmark::DoNotOptimize( str );
129129
}
130130
}
@@ -261,7 +261,7 @@ namespace nfx::time::benchmark
261261
//----------------------------------------------
262262

263263
BENCHMARK( BM_DateTime_ToString_ISO8601 );
264-
BENCHMARK( BM_DateTime_toIso8601Extended );
264+
BENCHMARK( BM_DateTime_toIso8601Precise );
265265

266266
//----------------------------------------------
267267
// Arithmetic

include/nfx/datetime/DateTime.h

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@
6565
* │ 2024-01-15T12:30:45Z │ Jan 15, 2024, 12:30:45 UTC │
6666
* │ 2024-06-20T00:00:00Z │ Jun 20, 2024, midnight UTC │
6767
* │ 2024-12-31T23:59:59Z │ Dec 31, 2024, 23:59:59 UTC │
68-
* │ 2024-01-15T12:30:45.123Z │ With milliseconds (123 ms)
69-
* │ 2024-01-15T12:30:45.123456Z │ With microseconds (123.456 ms) │
68+
* │ 2024-01-15T12:30:45.1230000Z │ With 7-digit fractions (123 ms) │
69+
* │ 2024-01-15T12:30:45.1234560Z │ With microseconds (123.456 ms) │
7070
* │ 2024-01-15T12:30:45.1234567Z │ Full precision (100 ns ticks) │
7171
* │ 1970-01-01T00:00:00Z │ Unix epoch (start of Unix time) │
7272
* │ 0001-01-01T00:00:00Z │ Minimum DateTime value │
@@ -178,8 +178,8 @@
178178
* │ DateTime fromEpoch{ DateTime::fromEpochSeconds(epochSecs) }; │
179179
* │ │
180180
* │ // Format output │
181-
* │ std::string basic{ dt.toString() }; // Basic ISO 8601
182-
* │ std::string precise{ dt.toIso8601Extended() }; // With fractions
181+
* │ std::string iso{ dt.toString(Format::Iso8601) };
182+
* │ std::string precise{ dt.toString(Format::Iso8601Precise) };
183183
* └──────────────────────────────────────────────────────────────────────┘
184184
* @endcode
185185
*
@@ -233,25 +233,28 @@ namespace nfx::time
233233
*/
234234
enum class Format : std::uint8_t
235235
{
236-
/** @brief ISO 8601 basic format: "2024-01-01T12:00:00Z" */
237-
Iso8601Basic,
236+
/** @brief ISO 8601 with seconds precision: "2024-01-01T12:00:00Z" */
237+
Iso8601,
238238

239-
/** @brief ISO 8601 extended format with fractional seconds: "2024-01-01T12:00:00.1234567Z" */
240-
Iso8601Extended,
239+
/** @brief ISO 8601 with full tick precision (7 decimal digits): "2024-01-01T12:00:00.1234567Z" */
240+
Iso8601Precise,
241241

242-
/** @brief Date and time with timezone: "2024-01-01T12:00:00+02:00" */
242+
/** @brief ISO 8601 with numeric offset (always +00:00 for DateTime): "2024-01-01T12:00:00+00:00" */
243243
Iso8601WithOffset,
244244

245-
/** @brief Date only format: "2024-01-01" */
246-
DateOnly,
245+
/** @brief ISO 8601 compact form without separators: "20240101T120000Z" */
246+
Iso8601Compact,
247247

248-
/** @brief Time only: "12:00:00" */
249-
TimeOnly,
248+
/** @brief ISO 8601 date only: "2024-01-01" */
249+
Iso8601Date,
250250

251-
/** @brief Epoch timestamp format: "1704110400" (seconds since epoch) */
251+
/** @brief ISO 8601 time only: "12:00:00" */
252+
Iso8601Time,
253+
254+
/** @brief Unix epoch seconds (integer): "1704110400" */
252255
UnixSeconds,
253256

254-
/** @brief Epoch timestamp with milliseconds: "1704110400123" */
257+
/** @brief Unix epoch milliseconds (integer): "1704110400000" */
255258
UnixMilliseconds,
256259
};
257260

@@ -632,27 +635,13 @@ namespace nfx::time
632635
// String formatting
633636
//----------------------------------------------
634637

635-
/**
636-
* @brief Convert to ISO 8601 string (basic format)
637-
* @return String representation in ISO 8601 basic format (e.g., "2024-01-01T12:00:00Z")
638-
* @note This function is marked [[nodiscard]] - the return value should not be ignored
639-
*/
640-
[[nodiscard]] std::string toString() const;
641-
642638
/**
643639
* @brief Convert to string using specified format
644640
* @param format The format to use for string conversion
645641
* @return String representation using the specified format
646642
* @note This function is marked [[nodiscard]] - the return value should not be ignored
647643
*/
648-
[[nodiscard]] std::string toString( Format format ) const;
649-
650-
/**
651-
* @brief Convert to ISO 8601 extended format with full precision
652-
* @return String representation in ISO 8601 extended format with fractional seconds (e.g., "2024-01-01T12:00:00.1234567Z")
653-
* @note This function is marked [[nodiscard]] - the return value should not be ignored
654-
*/
655-
[[nodiscard]] std::string toIso8601Extended() const;
644+
[[nodiscard]] std::string toString( Format format = Format::Iso8601 ) const;
656645

657646
//----------------------------------------------
658647
// std::chrono interoperability

include/nfx/datetime/DateTimeOffset.h

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,23 @@
5757
*
5858
* @par Format Examples:
5959
* @code
60-
* ┌────────────────────────────────┬─────────────────────────────────┐
61-
* │ Input String │ Meaning │
62-
* ├────────────────────────────────┼─────────────────────────────────┤
63-
* │ 2024-01-15T12:30:45Z │ UTC time (Z = zero offset) │
64-
* │ 2024-01-15T12:30:45+02:00 │ Local: 12:30, UTC+2 (East) │
65-
* │ 2024-01-15T12:30:45-05:00 │ Local: 12:30, UTC-5 (West) │
66-
* │ 2024-01-15T12:30:45.123+01:00 │ With milliseconds, UTC+1
67-
* │ 2024-06-20T08:15:00+05:30 │ India Standard Time (UTC+5:30) │
68-
* │ 2024-12-25T00:00:00-08:00 │ Pacific Standard Time (UTC-8) │
69-
* └────────────────────────────────┴─────────────────────────────────┘
60+
* ┌──────────────────────────────────────┬─────────────────────────────────┐
61+
* │ Input String │ Meaning │
62+
* ├──────────────────────────────────────┼─────────────────────────────────┤
63+
* │ 2024-01-15T12:30:45+00:00 │ UTC time (explicit offset) │
64+
* │ 2024-01-15T12:30:45+02:00 │ Local: 12:30, UTC+2 (East) │
65+
* │ 2024-01-15T12:30:45-05:00 │ Local: 12:30, UTC-5 (West) │
66+
* │ 2024-01-15T12:30:45.1230000+01:00 │ With 7-digit fractions, UTC+1 │
67+
* │ 2024-06-20T08:15:00+05:30 │ India Standard Time (UTC+5:30) │
68+
* │ 2024-12-25T00:00:00-08:00 │ Pacific Standard Time (UTC-8) │
69+
* └──────────────────────────────────────┴─────────────────────────────────┘
7070
* @endcode
7171
*
7272
* @par Timezone Offset Rules:
7373
* - Valid range: **±14:00:00** (±840 minutes, ±50,400 seconds)
7474
* - Positive offset: East of UTC (e.g., +09:00 for Japan)
7575
* - Negative offset: West of UTC (e.g., -05:00 for US Eastern)
76-
* - **Z** notation: Represents UTC (zero offset), equivalent to +00:00
77-
* - Offset format: Always ±HH:MM in string representation
76+
* - Offset format: Always **±HH:MM** in string representation (explicit, no 'Z' notation)
7877
*
7978
* @par Internal Composition:
8079
* @code
@@ -626,27 +625,13 @@ namespace nfx::time
626625
// String formatting
627626
//----------------------------------------------
628627

629-
/**
630-
* @brief Convert to ISO 8601 string with offset
631-
* @return String representation in ISO 8601 format with timezone offset (e.g., "2024-01-01T12:00:00+02:00")
632-
* @note This function is marked [[nodiscard]] - the return value should not be ignored
633-
*/
634-
[[nodiscard]] std::string toString() const;
635-
636628
/**
637629
* @brief Convert to string using specified format
638630
* @param format The format to use for string conversion
639631
* @return String representation using the specified format
640632
* @note This function is marked [[nodiscard]] - the return value should not be ignored
641633
*/
642-
[[nodiscard]] std::string toString( DateTime::Format format ) const;
643-
644-
/**
645-
* @brief Convert to ISO 8601 extended format with full precision and offset
646-
* @return String representation in ISO 8601 extended format with fractional seconds and offset (e.g., "2024-01-01T12:00:00.1234567+02:00")
647-
* @note This function is marked [[nodiscard]] - the return value should not be ignored
648-
*/
649-
[[nodiscard]] std::string toIso8601Extended() const;
634+
[[nodiscard]] std::string toString( DateTime::Format format = DateTime::Format::Iso8601 ) const;
650635

651636
//----------------------------------------------
652637
// Comparison methods

include/nfx/detail/datetime/DateTime.inl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ namespace std
299299

300300
auto format( const nfx::time::DateTime& dt, std::format_context& ctx ) const
301301
{
302-
return format_to( ctx.out(), "{}", dt.toString() );
302+
return format_to( ctx.out(), "{}", dt.toString( nfx::time::DateTime::Format::Iso8601 ) );
303303
}
304304
};
305305
} // namespace std

samples/Sample_DateTime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ int main()
9898
std::cout << "----------------------------------\n";
9999

100100
DateTime dt{ 2024, 6, 15, 14, 30, 45, 123 };
101-
std::cout << "DateTime: " << dt.toIso8601Extended() << "\n";
101+
std::cout << "DateTime: " << dt.toString( DateTime::Format::Iso8601Precise ) << "\n";
102102
std::cout << " Year: " << dt.year() << "\n";
103103
std::cout << " Month: " << dt.month() << "\n";
104104
std::cout << " Day: " << dt.day() << "\n";

src/DateTime.cpp

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,6 @@ namespace nfx::time
373373
// String formatting
374374
//----------------------------------------------
375375

376-
std::string DateTime::toString() const
377-
{
378-
return toString( Format::Iso8601Basic );
379-
}
380-
381376
std::string DateTime::toString( Format format ) const
382377
{
383378
std::int32_t y, mon, d, h, min, s, ms;
@@ -388,86 +383,61 @@ namespace nfx::time
388383

389384
switch ( format )
390385
{
391-
case Format::Iso8601Basic:
386+
case Format::Iso8601:
392387
{
393388
oss << std::setfill( '0' ) << std::setw( 4 ) << y << "-" << std::setw( 2 ) << mon << "-" << std::setw( 2 ) << d << "T" << std::setw( 2 ) << h << ":"
394389
<< std::setw( 2 ) << min << ":" << std::setw( 2 ) << s << "Z";
395-
396390
break;
397391
}
398-
case Format::Iso8601Extended:
392+
case Format::Iso8601Precise:
399393
{
400394
std::int32_t fractionalTicks{ static_cast<std::int32_t>( m_ticks % constants::TICKS_PER_SECOND ) };
401-
402-
// Format fractional seconds and strip trailing zeros
403-
std::ostringstream fractionOss;
404-
fractionOss << std::setfill( '0' ) << std::setw( 7 ) << fractionalTicks;
405-
std::string fractionStr = fractionOss.str();
406-
407-
// Strip trailing zeros (but keep at least one digit if all zeros)
408-
auto lastNonZero = fractionStr.find_last_not_of( '0' );
409-
if ( lastNonZero != std::string::npos )
410-
{
411-
fractionStr = fractionStr.substr( 0, lastNonZero + 1 );
412-
}
413-
else
414-
{
415-
fractionStr = "0";
416-
}
417-
418395
oss << std::setfill( '0' ) << std::setw( 4 ) << y << "-" << std::setw( 2 ) << mon << "-" << std::setw( 2 ) << d << "T" << std::setw( 2 ) << h
419-
<< ":" << std::setw( 2 ) << min << ":" << std::setw( 2 ) << s << "." << fractionStr << "Z";
420-
396+
<< ":" << std::setw( 2 ) << min << ":" << std::setw( 2 ) << s << "." << std::setw( 7 ) << fractionalTicks << "Z";
421397
break;
422398
}
423399
case Format::Iso8601WithOffset:
424400
{
425-
// UTC DateTime always has +00:00 offset
426401
oss << std::setfill( '0' ) << std::setw( 4 ) << y << "-" << std::setw( 2 ) << mon << "-" << std::setw( 2 ) << d << "T" << std::setw( 2 ) << h
427402
<< ":" << std::setw( 2 ) << min << ":" << std::setw( 2 ) << s << "+00:00";
428-
429403
break;
430404
}
431-
case Format::DateOnly:
405+
case Format::Iso8601Compact:
406+
{
407+
oss << std::setfill( '0' ) << std::setw( 4 ) << y << std::setw( 2 ) << mon << std::setw( 2 ) << d << "T" << std::setw( 2 ) << h
408+
<< std::setw( 2 ) << min << std::setw( 2 ) << s << "Z";
409+
break;
410+
}
411+
case Format::Iso8601Date:
432412
{
433413
oss << std::setfill( '0' ) << std::setw( 4 ) << y << "-" << std::setw( 2 ) << mon << "-" << std::setw( 2 ) << d;
434-
435414
break;
436415
}
437-
case Format::TimeOnly:
416+
case Format::Iso8601Time:
438417
{
439418
oss << std::setfill( '0' ) << std::setw( 2 ) << h << ":" << std::setw( 2 ) << min << ":" << std::setw( 2 ) << s;
440-
441419
break;
442420
}
443421
case Format::UnixSeconds:
444422
{
445423
oss << toEpochSeconds();
446-
447424
break;
448425
}
449426
case Format::UnixMilliseconds:
450427
{
451428
oss << toEpochMilliseconds();
452-
453429
break;
454430
}
455431
default:
456432
{
457-
oss << toString( Format::Iso8601Basic );
458-
433+
oss << toString( Format::Iso8601 );
459434
break;
460435
}
461436
}
462437

463438
return oss.str();
464439
}
465440

466-
std::string DateTime::toIso8601Extended() const
467-
{
468-
return toString( Format::Iso8601Extended );
469-
}
470-
471441
//----------------------------------------------
472442
// Validation methods
473443
//----------------------------------------------
@@ -742,7 +712,7 @@ namespace nfx::time
742712

743713
std::ostream& operator<<( std::ostream& os, const DateTime& dateTime )
744714
{
745-
os << dateTime.toString();
715+
os << dateTime.toString( nfx::time::DateTime::Format::Iso8601 );
746716

747717
return os;
748718
}

0 commit comments

Comments
 (0)