Ответ 1
Прямой ответ на ваш вопрос
Имеется ли в C++ локали связанный часовой пояс?
Нет.
И это не будет в будущем. Как правильно отмечено в вопросе, для многих локалей это не имело бы смысла, поскольку географическая область, представленная локали, может иметь более одного часового пояса.
Стандарт C говорит в спецификации для strftime
:
%Z
заменяется именем или аббревиатурой часового пояса локали или без символов, если часовой пояс не определен.[tm_isdst]
Но спецификация C для struct lconv
предоставляет такого члена для хранения этой информации. Спецификация позволяет реализациям добавлять такие элементы, но на практике реализации не сохраняют эту информацию в локали C.
Фрагменты языка C++ time_put
и time_get
определяют себя в терминах спецификации C для strftime
, спецификации POSIX для strptime
и некоторых дополнений, которые не включают имя часового пояса или аббревиатуру.
Спецификация POSIX для strftime
намного более подробно, чем спецификация C, и удаляет связь с "locale":
Z
Заменяется именем или аббревиатурой часового пояса или байтами, если информация о часовом поясе отсутствует.[ tm_isdst]
Спецификация POSIX для struct lconv
также намного более подробно, чем спецификация C, но по-прежнему не обеспечивает хранения имени или аббревиатуры часового пояса.
Но будущее действительно дает надежду на более легкий и эффективный доступ к информации о часовых поясах, по крайней мере, в C++.
До C++ 20, C++ знает:
-
Единый стандарт времени: UTC, который тесно моделируется Unix Time.
-
Единый часовой пояс: "локальный часовой пояс", установленный пользователем или администратором компьютера. UTC также может использоваться как локальный часовой пояс.
Как указано выше, локальный часовой пояс не является частью данных языка C++ (или C). Данные локали включают некоторые календарные данные, такие как:
- Полные и сокращенные названия дней недели.
- Полные и сокращенные имена месяцев.
- Локальные обычные форматы для отображения даты и времени (например, год, месяц, день заказа).
Смещение UTC (%z
) и сокращение времени (%Z
) могут быть доступны, но будут храниться как часть данных локального часового пояса, а не с текущими данными локали, в основном из-за отсутствия - одно отображение между часовыми поясами и локалями.
Объяснение того, что произошло с кодом, представленным в вопросе OP
В вашем примере: tm when{};
обнуляет всех членов tm
, включая tm_isdst
. Когда tm_isdst
равно нулю, это означает, что летнее время, как известно, не действует, для этого конкретного tm
.
tm
также разрешено иметь членов, не указанных стандартом. Популярным расширением является член tm_gmtoff
который удерживает смещение UTC в секундах. Если в вашей реализации Linux есть такой член, tm when{};
установил бы его на 0 секунд. Если в вашей реализации Windows нет такого члена, смещение UTC локального часового пояса будет храниться в другом месте. Это объясняет различия, которые вы видите, и обе реализации соответствуют.
Полезная информация о том, как получить доступ к часовым поясам, поскольку локали C++ не предоставляют доступ
В спецификации C++ 20 существует новый тип, называемый std::chrono::time_zone
. Одной из функций-членов time_zone
является:
template<class Duration> sys_info get_info(const sys_time<Duration>& st) const;
sys_time<Duration>
- это просто system_clock::time_point
, но любой точности. Таким образом, вы даете time_zone
time_point
, и вы возвращаете sys_info
которая содержит все виды полезной информации об этом time_zone
в то time_point
:
struct sys_info
{
sys_seconds begin;
sys_seconds end;
seconds offset;
minutes save;
string abbrev;
};
- Диапазон
[begin, end)
сообщает вам, во сколько раз эта информация действительна (это точки времени UTC). -
offset
- этоtime_zone
UTC текущего времени вseconds
. - Если
save != 0min
, то в настоящее время считается, чтоtime_zone
находится в летнее время. -
time_zone
текущего времени сохраняется вabbrev
.
Кроме того, существует функция, не являющаяся членом:
const time_zone* current_zone();
который возвращает указатель на текущий локальный часовой пояс. Объединив все это, вот программа C++ 20, которая выводит интересную информацию о вашем текущем локальном часовом поясе:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
std::cout << current_zone()->get_info(system_clock::now()) << '\n';
}
Это просто для меня:
2018-03-11 07:00:00
2018-11-04 06:00:00
-04:00:00
01:00
EDT
Если вам нравится, вы можете поэкспериментировать с этой частью C++ 20, используя C++ 11, 14 или 17, используя библиотеку часовых поясов Howard Hinnant. Эта библиотека помещает все в date
пространства имен вместо std::chrono
.
Вы также можете получить информацию о любом часовом поясе IANA, например:
#include "date/tz.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << locate_zone("Australia/Sydney")->get_info(system_clock::now()) << '\n';
}
которые просто выводят для меня:
2018-10-06 16:00:00
2019-04-06 16:00:00
11:00:00
01:00
AEDT
Обратите внимание, что даже в C++ 20 временные зоны и локали не связаны. Просто не имеет смысла это делать.