Я хочу получить метку времени в формате «ГГГГ-ММ-ДД чч:: мм:: сс.ффф + zz».
Я думал, что смогу сделать это с помощью спецификатора %z для std::format, но я получаю эту ошибку в MSVC:
error C7595: 'std::basic_format_string<char,const std::chrono::time_point<
std::chrono::local_t,std::chrono::duration<std::chrono::system_clock::rep,std::chrono::system_clock::period>> &>::basic_format
_string': call to immediate function is not a constant expression
#include <chrono>
#include <format>
#include <iostream>
std::string get_current_time_and_date() {
auto const time =
std::chrono::current_zone()->to_local(std::chrono::system_clock::now());
return std::format("{:%F %T %z}", time);
}
int main() {
std::cout << get_current_time_and_date() << "\n";
return 0;
}
Если я использую "{:%F %T}" в качестве строки формата, он работает нормально (но, очевидно, не включает часовой пояс). Это говорит мне, что сообщение об ошибке бессмысленно, но есть ли способ обойти это? Это ошибка в компиляторе?
Для потомков вот обходной путь с использованием старого доброго strftime:
std::string get_current_time_and_date() {
auto time = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
time.time_since_epoch()) %
1000;
auto localT = std::chrono::system_clock::to_time_t(time);
std::tm timeStruct;
localtime_s(&timeStruct, &localT);
std::ostringstream oss;
const size_t TIME_STR_LEN = 32;
char buf[TIME_STR_LEN];
std::strftime(buf, TIME_STR_LEN, "%F %T", &timeStruct);
char timezonebuf[16];
std::strftime(timezonebuf, sizeof(timezonebuf), " %z", &timeStruct);
oss << buf << '.' << std::setfill('0') << std::setw(3) << ms.count() << timezonebuf;
return oss.str();
}





В опустошение кучи объясняет , вы не можете использовать %z с std::chrono::local_times. Однако вы можете обойти это, извлекая смещение из часового пояса std::chrono::local_info.
Пример:
std::string get_current_time_and_date() {
auto const time = std::chrono::current_zone()->to_local(std::chrono::system_clock::now());
auto zi = std::chrono::current_zone()->get_info(time);
bool neg = false;
auto offset = zi.first.offset; // see note below
if (offset < std::chrono::seconds{}) {
offset = -offset;
neg = true;
}
return std::format("{:%F %T} {}{:%H%M}", time, neg ? '-' : '+', offset);
}
Примечание. Прочтите документацию для std:.chrono::local_info. Возможно, вам тоже придется проверить zi.result.
@heapunderrun Спасибо! Это явно не так полезно, как ваш ответ, но все же в нем может быть часть, которая может быть кому-то полезна :-)
std::chrono::time_zone::to_local возвращает std::chrono::local_time, который является std::chrono::time_point, использующим std::chrono::local_t в качестве первого аргумента шаблона. Однако цитата из std::chrono::local_t на cppreference (выделено мной):
Класс
local_tпредставляет собой псевдочасы /…/, чтобы указать, что точка времени представляет местное время относительно еще не указанного часового пояса.
Другими словами, такая точка времени (которую возвращает std::chrono::time_zone::to_local) не зависит от часового пояса, она не содержит никакой информации об используемом часовом поясе.
Если вы попытаетесь изменить
return std::format("{:%F %T %z}", time);
к
return std::vformat("{:%F %T %z}", std::make_format_args(time));
вы увидите, что он выдает std::format_error с описанием «аргумент формата не содержит информацию, требуемую хроно-спецификациями».
В черновом стандарте есть примечание (#13):
Если используется
%Z,%zили модифицированная версия%z, создается исключение типаformat_error.
Другими словами, %z не поддерживается для local_time (поскольку информация о часовом поясе все равно не содержится в local_time).
Как предположил Барри, возможно, вместо этого вы имели в виду std::choro::zoned_time. Это разрешает спецификатор %z. Вот модифицированный пример:
#include <chrono>
#include <format>
#include <iostream>
#include <string>
std::string get_current_time_and_date()
{
const auto zt{ std::chrono::zoned_time{
std::chrono::current_zone(),
std::chrono::system_clock::now() } };
return std::format("{:%F %T %z}", zt);
}
int main()
{
std::cout << get_current_time_and_date() << '\n';
}
Можно добавить, что OP, вероятно, хотел не current_zone()->to_local(now), а скорее zoned_time(current_zone(), now), который работает с этим спецификатором.
@Barry Спасибо за предложение, я добавил пример.
Спасибо за подробное исследование и спасибо @Barry за предложение.
Плюс один от меня, я думаю, что в вашем ответе есть полезная информация. (Я не знаю, кто проголосовал за это, не я.)