имеет ли C++ локаль связанный часовой пояс? И если да, то как вы к нему обращаетесь?

Я провел небольшое исследование по этому вопросу, и у меня есть довольно убедительные доказательства ответа ДА, и ответ НЕТ. Я не уверен, с какой стороны верить.

Во-первых, документация, которую я нашел на cppreference.com, и http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf, похоже, ничего не говорит об этом. Я считаю это доказательством того, что локали НЕ поддерживают часовой пояс.

Но https://en.cppreference.com/w/cpp/locale/time_get/get и https://en.cppreference.com/w/cpp/locale/time_put/put говорят:

% z записывает смещение от UTC в формате ISO 8601 (например, -0430) или без символов, если информация о часовом поясе недоступна.% Z записывает имя или аббревиатуру часового пояса или без символов, если информация о часовом поясе недоступна ( зависит от локали)

который, по-видимому, указывает на то, что существует временной интервал SOMETIMES, связанный с объектом locale().

Теперь, если вы возьмете локаль en_US.utf8 (один из моих фаворитов ;-)), на самом деле не существует разумного часового пояса для связи (Соединенные Штаты содержат не менее 4 или более часовых поясов).

Итак, время для эмпирического.

Я запустил код:

#include <iostream>
#include <cstdlib>
#include <locale>
#include <sstream>
using namespace std;
int main ()
{
    //  locale l;
    locale                l = locale::classic ();
    tm                    when{};
    const time_put<char>& tmput = use_facet<time_put<char>> (l);
    ostringstream         oss;
    oss.imbue (l);
    static const string   kTZOffsetPattern_{"%z"};
    tmput.put (oss, oss, ' ', &when, kTZOffsetPattern_.c_str (), kTZOffsetPattern_.c_str () + kTZOffsetPattern_.length ());
    cout << oss.str ();
    return 0;
}

В Linux (ubuntu) это дало ответ, который я ожидал, +0000 (ОК, я также не был бы удивлен ошибкой или пустой строкой).

Но в Windows (visual studio.net 2k17 - 15.8.7) - это дает: -0500

Да, как вы, возможно, догадались, я тестирую это в восточном часовом поясе. Но я все равно ожидал бы 0 или пустую строку (особенно для случая locale :: classic()).

Ответы

Ответ 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++ знает:

  1. Единый стандарт времени: UTC, который тесно моделируется Unix Time.

  2. Единый часовой пояс: "локальный часовой пояс", установленный пользователем или администратором компьютера. 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 временные зоны и локали не связаны. Просто не имеет смысла это делать.

Ответ 2

имеет ли C++ локаль связанный часовой пояс?

Все аспекты текущего часового пояса определяются реализацией.

Точная формулировка спецификатора %Z из C99 (C++ делегирует функции библиотеки C для стандарта C):

заменяется именем или аббревиатурой часового пояса локалей или без символов, если часовой пояс не определен.

Это кажется немного неоднозначным. Одна интерпретация действительно такова, что локаль может повлиять на часовой пояс. Другой, который не совсем соответствует формулировке, будет заключаться в том, что локаль влияет на имя или аббревиатуру часового пояса. Независимо от того, что, по-видимому, нет гарантии того, что языковой стандарт не затронет часовой пояс, хотя я бы не ожидал, что это так.


как вы к нему обращаетесь?

Насколько я знаю, вы не можете использовать стандартные утилиты библиотек. В любом случае, это не так, и никак не изменить его.

Способ печати текущего часового пояса заключается в использовании спецификаторов формата %z или %Z для strftime/put_time/time_put как вы показали.

Существует также способ получить разницу в зоне как целое. std::mktime распаковывает структуру std::tm в метку времени по языку, а std::gmtime анализирует std::gmtime метку в std::tm соответствии с UTC, поэтому, если вы начинаете с эпохи и объединяете эти два, вы получите разницу текущего часового пояса локали и UTC в секундах.

std::time_t t = 0;
std::cout << -1 * std::mktime(std::gmtime(&t));

Ответ 3

Соответствующий отрывок стандарта C (который использует стандарт C++) говорит

% z заменяется смещением от UTC в формате ISO 8601 '' -0430 (что означает 4 часа 30 минут позади UTC, к западу от Гринвича) или без символов, если часовой пояс не определен. [Tm_isdst]

% Z заменяется именем или аббревиатурой часового пояса локали или без символов, если часовой пояс не определен. [Tm_isdst]

Обратите внимание, что имя часового пояса, как говорят, зависит от локали, но смещение часового пояса - нет.

Cppreference нужно исправить свои неряшливые формулировки.