Как получить дату и время в формате ISO 8601 в Windows?

Каков стандартный способ получить дату и время в формате ISO8601 в Windows с использованием C++? В частности, я хотел бы, чтобы он был отформатирован как:

2017-02-22T10:00:00.123-05:00 2017-02-22T10:00:00.123 >>> -05:00 <<< # how to print the offset?

Я пытался объединить вывод GetLocalTime и GetTimeZoneInformation, но это выглядит эзотерически. На SO есть похожие вопросы, однако я не нашел ни одного, который бы печатал смещение UTC в нужном формате. Есть ли лучший подход?


person oleksii    schedule 22.02.2017    source источник
comment
Как создать дату и время ISO 8601 в C++?   -  person phuclv    schedule 22.02.2017
comment
@LưuVĩnhPhúc Я видел, однако, что есть только один ответ, который печатает смещение в нужном формате 2017-02- 22T10:00:00.123 -05:00 и в этом ответе используется Qt, который я не использую. Может быть, мне просто не хватает спецификатора формата - в большинстве ответов используется Z для обозначения использования времени UTC. Однако мне нужно местное время и смещение от UTC.   -  person oleksii    schedule 22.02.2017


Ответы (4)


Я не думаю, что для С++ в Windows есть готовое решение. Самое близкое, что вы можете получить, это InternetTimeFromSystemTime, но документально подтверждено, что он поддерживает только RFC1123.

Вероятно, вам придется кодировать его самостоятельно с помощью GetLocalTime + GetTimeZoneInformation + wsprintf (или GetTimeZoneInformationForYear, если вы не имеете дело с текущим временем).

person Anders    schedule 22.02.2017
comment
GetSystemTime возвращает время в формате UTC, поэтому нет необходимости добавлять к нему смещение UTC. - person mpiatek; 22.02.2017

Может быть, что-то вроде этого. Мы вызываем GetLocalTime и GetTimeZoneInformation, а затем передаем их функции, которая возвращает форматированную строку.

Это написано быстро, не проверено, за исключением того факта, что он возвращает правильный результат на моей машине сейчас. Он работает на том факте, что SYSTEMTIME имеет элемент Bias, где UTC = Localtime + Bias и Bias задаются в минутах. Так что получите часы, разделив на 60 и приняв абсолютное значение этого. Затем аналогичным образом получаем минуты и устанавливаем знак в зависимости от того, Bias > 0

#include <Windows.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <cmath>

std::string format_system_time(const SYSTEMTIME& sys_time, const TIME_ZONE_INFORMATION& time_zone)
{
    std::ostringstream formatted_date_time;
    formatted_date_time << std::setfill('0');
    formatted_date_time << sys_time.wYear <<  "-" << std::setw(2) << sys_time.wMonth << "-" <<
        std::setw(2) << sys_time.wDay << "T" << std::setw(2) << sys_time.wHour << ":" <<
        std::setw(2) << sys_time.wMinute << ":" << std::setw(2) << sys_time.wSecond << "." <<
        std::setw(3) << sys_time.wMilliseconds;

    //UTC = localtime + bias; bias is in minutes
    int utc_offset_hours = time_zone.Bias / 60;
    int utc_offset_minutes = std::abs(time_zone.Bias - (utc_offset_hours * 60));
    char offset_sign = time_zone.Bias > 0 ? '-' : '+';
    formatted_date_time << offset_sign << std::setw(2) << std::abs(utc_offset_hours) << ":" << utc_offset_minutes;

    return formatted_date_time.str();
}

int main(int argc, char* argv[])
{
    SYSTEMTIME date_and_time;
    GetLocalTime(&date_and_time);

    TIME_ZONE_INFORMATION time_zone;
    GetTimeZoneInformation(&time_zone);

    auto& formatted_date_time = format_system_time(date_and_time, time_zone);

    return 0;
}
person mpiatek    schedule 22.02.2017
comment
Часовые пояса могут иметь ненулевое минутное смещение. - person Anders; 22.02.2017
comment
@Anders Теперь, когда я смотрю, я вижу, что их много, спасибо за информацию - person mpiatek; 22.02.2017
comment
Отредактировано с учетом ненулевых минутных смещений. - person mpiatek; 22.02.2017

Спецификатор формата %z указывает смещение часового пояса, как описано в документации (например, MSDN on strftime), но пропускает ':'. Вы можете использовать его так, чтобы получить ':' в свою строку:

struct tm tmNow;

time_t now = time(NULL);    // Get the current time
_localtime64_s(&tmNow, &now);

char bufferTime[26];
char bufferTimezoneOffset[6];
size_t tsizTime =  strftime(bufferTime, 26, "%Y-%m-%dT%H:%M:%S", &tmNow);   // The current time formatted "2017-02-22T10:00:00"
size_t tsizOffset =  strftime(bufferTimezoneOffset, 6, "%z", &tmNow);       // The timezone offset -0500
strncpy_s(&bufferTime[tsizTime], 26, bufferTimezoneOffset, 3);              // Adds the hour part of the timezone offset
bufferTime[tsizTime + 3] = ':';                                             // insert ':'
strncpy_s(&bufferTime[tsizTime + 4], 26, &bufferTimezoneOffset[3], 3);      // Adds the minutes part of the timezone offset
puts(bufferTime);   // Your output: "2017-02-22T10:00:00-05:00"

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

person Florian Haupt    schedule 24.02.2017

Использование бесплатной библиотеки часовых поясов Говарда Хиннанта с открытым исходным кодом, которая работает на VS-2013 и позже, но требует некоторой установки:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;
    auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
    cout << format("%FT%T%Ez\n", zt);
}

Это просто вывод для меня:

2017-02-22T17:29:03.859-05:00
person Howard Hinnant    schedule 22.02.2017