April 6, 2026

bsl::format for bdlt value types

bdlt value types are now supported by bsl::format. bsl::format is more concise than streaming via ostream, and more type-safe and high-level than output via printf.

bsl::format is similar to printf in that output is controlled by a format string, where formatting one variable is controlled by a substring enclosed by “{}”. format returns a bsl::string by value. The substring “{}” itself means to format the value identically to streaming it to an ostream.

bdlt::Date  DATE(1776, 7, 4);
bdlt::Time  TIME(16, 45, 21, 123, 456);
const bsl::string& a = bsl::format("{}, {}", DATE, TIME);

will set a to "04JUL1776, 16:45:21.123456".

For more detailed formatting, following the ‘{’ with a ‘:’, and then various ‘%’ sequences allows one to specify the whole field or parts of it. A small subset of ‘%’ sequences is:

  • “%D” – date like streaming to ostream

  • “%T” – time like streaming to ostream

  • “%i” – formats any bdlt value type in Iso8601 format

  • “%b” – 3-letter abbreviation of month

  • “%d” – day of month

  • “%H” – hour (00 - 24)

  • “%I” – hour (01 - 12)

  • “%M” – minute (00 - 59)

  • “%S” – second (including fraction)

  • “%p” – “AM” or “PM”

  • “%z” – time zone as 2-digit hours and 2 digit minutes

const bsl::string& b = bsl::format("{:%D}, {:%T}, {:%i} & {:%i}",
                                           DATE, TIME, (DATE + 2), TIME);
const bsl::string& c = bsl::format("{:%I:%M %p} on {:%b %d}", TIME, DATE);

will set b to "04JUL1776, 16:45:21.123456, 1776-07-06 & 16:45:21.123", and c to "04:45 PM on JUL 04".

Note that all of the above sequences will also work for a Datetime or DatetimeTz. While “%i” has an individual meaning for each bdlt value type, all other ‘%’ sequences have the same meaning across bdlt. For example:

bdlt::DatetimeTz DATETIMETZ(bdlt::Datetime(DATE, TIME), 300);
const bsl::string& d = bsl::format("{:%I:%M %p on %b %d}", DATETIMETZ);

will produce a value of d that exactly matches c above.

Between the opening ‘:’ and the first ‘%’ in a “{}” sequence, modifiers may occur. For example, a period followed by an unsigned integral value specifies the number of fractional digits of a second that are to be formatted, and a ‘:’ indicates that the hours and minutes of a time zone are to be separated by a ‘:’.

const bsl::string& e = bsl::format(   "{:%T%z}", DATETIMETZ);
const bsl::string& f = bsl::format("{:.2:%T%z}", DATETIMETZ);

will set e to "16:45:21.123456+0500" and f to "16:45:21.12+05:00".

Text that is not interpreted by format will be echoed to the output string without modification if it occurs outside of “{}” sequences or after the first ‘%’ sequence within a “{}” sequence, but such text occurring between the opening ‘:’ of a “{}” sequence and the first ‘%’ character will result in an error.

It is also an error to specify a ‘%’ sequence that does not apply to the type being formatted.

All such errors result in compile-time errors in C++20 or later, and in exceptions thrown at run-time in C++17 or earlier.

To format a bdlt type, it is necessary to include the file bdlt_<type>formatter.h, where <type> is the value type being formatted. For example, formatting a Date requires bdlt_dateformatter.h and formatting a DatetimeTz requires bdlt_datetimetzformatter.h. So the examples above would require

#include <bdlt_date.h>
#include <bdlt_dateformatter.h>
#include <bdlt_time.h>
#include <bdlt_timeformatter.h>
#include <bdlt_datetimetz.h>
#include <bdlt_datetimetzformatter.h>

For a more thorough example, see the [bdlt_formatdoc component]