Commit e1d70fa
authored
Per-field chrono format wrappers: glz::date_format and glz::epoch_count (#2658)
* Add per-field chrono format wrappers: glz::date_format and glz::epoch_count
Decouple the wire format from the chrono type (issue #2640) with two JSON
field wrappers usable inside glz::object():
- glz::date_format<&T::m, "%Y-%m-%d %H:%M:%S"> renders a system_clock
time_point or year_month_day with a hand-rolled, compile-time-validated
strftime subset (%Y %m %d %H %M %S %F %T %%). UTC wall-clock; %S is integer
seconds (sub-second truncated by design).
- glz::epoch_count<&T::m, Duration> renders a system_clock time_point as a
numeric Unix count, the per-field counterpart to glz::epoch_time.
Both follow the established wrapper precedents (float_format's NTTP format_str,
quoted_t's full custom read+write) and are JSON-only: marked glaze_reflect=false
so binary backends fail to compile rather than silently miscode.
Format string parsing/formatting is hand-rolled (no std::chrono::parse/format)
reusing the existing write_digits/parse_digits primitives, for consistent
behavior across the compiler matrix. utc_time and binary backends are
intentionally out of scope for this MVP.
- Extract format_str NTTP into glaze/core/format_str.hpp (shared with float_format)
- Add strftime-subset helpers to chrono_detail in glaze/core/chrono.hpp
- New header glaze/json/chrono_format.hpp; wired via glaze/chrono.hpp
- Tests in tests/chrono_test and docs in docs/chrono.md
* Harden per-field chrono wrappers after review
Review-driven guards, tests, and docs for glz::date_format / glz::epoch_count.
No behavioral regressions for valid inputs.
- epoch_count: validate the Duration unit (is_duration) alongside the member
type via a shared validate_epoch_count() guard, so a non-duration unit gives
a friendly message instead of a cascade out of `typename Duration::rep`.
- date_format: reject an invalid/default year_month_day on write
(constraint_violated) so the writer never emits a string its own reader
rejects, and an out-of-range month/day cannot slip past write_digits<2>.
- date_format: skip the ensure_space reservation on the write_unchecked fast
path (matches to<JSON, num_t>); correct the buffer-size comments (%F, not %T,
is the widest token at 5 bytes per source char).
- docs: correct the backend-scope wording (text/JSON-family only; TOML also
fails to compile; NDJSON/stencil work) and document the epoch_count integer
input contract.
- tests: compile-time validator assertions plus runtime coverage for year
boundaries, pre-1970 instants, the %% literal, sub-second read-back, the
epoch_count cross-precision duration_cast, and the invalid-ymd write guard.
* Fix MSVC overflow building far-future instants in date_format
MSVC's std::chrono::hours and minutes use a 32-bit rep, so reconstructing a
far-future instant as `sys_days{ymd} + hours + minutes + seconds` overflows
the intermediate minutes count (days * 24 * 60 exceeds INT_MAX, ~4.2e9 at year
9999) and silently wraps to a bogus instant. libc++/libstdc++ use a 64-bit rep,
so the year-boundary test passed there but failed on the msvc/clang-cl jobs,
emitting 1833-11-15T19:43:59 for 9999-12-31T23:59:59.
Anchor the day at seconds precision (sys_seconds) before folding in the
time-of-day so the whole chain is computed in 64 bits, in both the date_format
reader and the test's input construction.
* Switch per-field chrono wrappers to value-based call syntax
date_format and epoch_count now take the member pointer as a value argument:
glz::date_format(&T::start, "%Y-%m-%d %H:%M:%S")
glz::epoch_count<std::chrono::milliseconds>(&T::logged)
Previously the member pointer and (for date_format) the format string were
non-type template parameters. Carrying the format as a runtime std::string_view
keys the view type on the member type alone, so fields that differ only in their
format string no longer each instantiate a fresh date_format_t / to / from /
closure. On a synthetic struct of N same-typed members with distinct formats this
removed ~40% of the wrapper's template instantiations and made the per-distinct-
format compile cost ~flat (it tracks a single shared format). date_format's
strftime walk was already runtime, so codegen is unchanged.
The strftime pattern stays a compile-time literal: a consteval guard in the
date_format() factory still rejects unsupported tokens, a missing calendar date,
and time tokens on a year_month_day field. float_format keeps its NTTP form
because it relies on std::format's compile-time format checking.
The reader keeps the seconds-anchored reconstruction (sys_seconds) so far-future
years stay exact under MSVC's 32-bit chrono hours/minutes rep.
* Report date_format compile errors without throw (-fno-exceptions safe)
The consteval date_format() validator used throw to turn an invalid format
string into a compile error. throw is ill-formed under -fno-exceptions even
inside an immediate function, so the chrono_test build (which compiles with
-fno-exceptions -fno-rtti) failed across the GCC/Clang matrix.
Replace the throws with calls to non-constexpr marker functions on the error
branches: when a branch is constant-evaluated the call makes the immediate
invocation non-constant, producing a hard compile error whose diagnostic names
the marker. Same compile-time rejection of unsupported tokens, a missing date,
and time tokens on a year_month_day field, now without relying on exceptions.
* docs: surface per-field chrono wrappers in the wrapper catalog
date_format and epoch_count were documented only in chrono.md and were absent
from docs/wrappers.md, the canonical wrapper list where the sibling float_format
lives, so a user browsing wrappers would not find them. Add both there (with the
glaze/chrono.hpp include note and a link to the chrono docs).
Also round out the chrono.md section: add a read/round-trip example for
date_format (previously only the write side was shown) and document the
write-side constraint_violated error for out-of-range years / non-ok dates.1 parent 8391961 commit e1d70fa
9 files changed
Lines changed: 1069 additions & 10 deletions
File tree
- docs
- include/glaze
- core
- json
- tests/chrono_test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
183 | 271 | | |
184 | 272 | | |
185 | 273 | | |
| |||
264 | 352 | | |
265 | 353 | | |
266 | 354 | | |
| 355 | + | |
| 356 | + | |
267 | 357 | | |
268 | 358 | | |
269 | 359 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
37 | 41 | | |
38 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
39 | 46 | | |
40 | 47 | | |
41 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
0 commit comments