-
Notifications
You must be signed in to change notification settings - Fork 726
Open
Description
Summary
Parsing 12‑hour times with AM/PM directly into date::sys_time ignores %p: PM inputs produce the same UTC result as AM. The C++ locale facet appears correct (std::get_time handles AM/PM). Using %r instead of %I:%M:%S %p works, so this looks specific to %I…%p in date::parse/from_stream for sys_time when combined with %z/%Ez.
Minimal repro (fails with %I:%M:%S %p)
main.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <chrono>
#include <locale>
#include <date/date.h>
template <typename Dur>
void test(const std::string& s, const std::string& fmt) {
std::istringstream in{s};
in.imbue(std::locale("en_US.utf8")); // AM/PM facet
date::sys_time<Dur> tp{};
in >> date::parse(fmt.c_str(), tp);
if (!in) {
std::cerr << "Parse failed. fmt='" << fmt << "' input='" << s << "'\n";
return;
}
std::cout << s << " -> " << date::format("%FT%TZ", tp) << '\n';
}
int main() {
using seconds = std::chrono::seconds;
test<seconds>("Sep 16, 2025 10:29:51 PM -0300",
"%b %d, %Y %I:%M:%S %p %z");
test<seconds>("Sep 16, 2025 10:29:51 PM -03:00",
"%b %d, %Y %I:%M:%S %p %Ez");
test<seconds>("Sep 16, 2025 10:29:51 AM -0300",
"%b %d, %Y %I:%M:%S %p %z");
}Actual output (incorrect)
Sep 16, 2025 10:29:51 PM -0300 -> 2025-09-16T13:29:51Z
Sep 16, 2025 10:29:51 PM -03:00 -> 2025-09-16T13:29:51Z
Sep 16, 2025 10:29:51 AM -0300 -> 2025-09-16T13:29:51Z
Expected output
Sep 16, 2025 10:29:51 PM -0300 -> 2025-09-17T01:29:51Z
Sep 16, 2025 10:29:51 PM -03:00 -> 2025-09-17T01:29:51Z
Sep 16, 2025 10:29:51 AM -0300 -> 2025-09-16T13:29:51Z
Control repro (works with %r)
main_r.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <chrono>
#include <locale>
#include <date/date.h>
template <typename Dur>
void test(const std::string& s, const std::string& fmt) {
std::istringstream in{s};
in.imbue(std::locale("en_US.utf8"));
date::sys_time<Dur> tp{};
in >> date::parse(fmt.c_str(), tp);
if (!in) {
std::cerr << "Parse failed. fmt='" << fmt << "' input='" << s << "'\n";
return;
}
std::cout << s << " -> " << date::format("%FT%TZ", tp) << '\n';
}
int main() {
using seconds = std::chrono::seconds;
test<seconds>("Sep 16, 2025 10:29:51 PM -0300",
"%b %d, %Y %r %z");
test<seconds>("Sep 16, 2025 10:29:51 PM -03:00",
"%b %d, %Y %r %Ez");
test<seconds>("Sep 16, 2025 10:29:51 AM -0300",
"%b %d, %Y %r %z");
}Actual output (correct)
Sep 16, 2025 10:29:51 PM -0300 -> 2025-09-17T01:29:51Z
Sep 16, 2025 10:29:51 PM -03:00 -> 2025-09-17T01:29:51Z
Sep 16, 2025 10:29:51 AM -0300 -> 2025-09-16T13:29:51Z
Facet sanity check (std lib handles %p correctly)
diag.cpp
#include <locale>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <iostream>
int main() {
std::locale loc("en_US.utf8");
// %p formats correctly
std::ostringstream os; os.imbue(loc);
std::tm tm{}; tm.tm_hour = 13;
os << std::put_time(&tm, "%p");
std::cout << "put_time(%p) for 13:00 -> '" << os.str() << "' (expect 'PM')\n";
// get_time parses AM/PM correctly
auto parse = [&](const char* s){
std::tm t{}; std::istringstream is(s); is.imbue(loc);
is >> std::get_time(&t, "%I:%M:%S %p");
std::cout << "get_time('" << s << "') -> tm_hour=" << t.tm_hour << "\n";
};
parse("10:29:51 AM"); // -> 10
parse("10:29:51 PM"); // -> 22
parse("12:00:00 AM"); // -> 0
parse("12:00:00 PM"); // -> 12
}Observed
put_time(%p) for 13:00 -> 'PM' (expect 'PM')
get_time('10:29:51 AM') -> tm_hour=10
get_time('10:29:51 PM') -> tm_hour=22
get_time('12:00:00 AM') -> tm_hour=0
get_time('12:00:00 PM') -> tm_hour=12
Environment
Compiler: GCC 13.3.0
$ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
libc: glibc 2.39
$ ldd --version
ldd (Ubuntu GLIBC 2.39-0ubuntu8.5) 2.39
Locales available (excerpt):
$ locale -a
C
C.utf8
…
en_US.utf8
…
en_US.utf8loads successfully and is used in the tests above.dateversion tested: v3.0.4 (also reproducible with v3.0.1).
Notes / Workarounds
- Using
%r(12‑hour time with AM/PM) instead of%I:%M:%S %pworks correctly. - Alternatively, normalize AM/PM to 24‑hour in the input and parse with
%H:%M:%S %z/%Ez.
Ask
Is %p being dropped when parsing sys_time with %I…%p along with %z/%Ez? Any fix or guidance would be appreciated.
Metadata
Metadata
Assignees
Labels
No labels