Std:: gmtime не возвращает время UTC

Привет!

Следующий код должен вывести текущее время в формате UTC и местное время соответственно:

#pragma (disable : 4996)

#include <ctime>
using namespace std;

int main()
{
 tm* p_current_tm_0 = gmtime(&current_time);
 tm* p_current_tm_ = localtime(&current_time);

 char* current_tm_0 = asctime(p_current_tm_0);
 cout << "Current UTC date and time: " << current_tm_0;

 char* current_tm_ = asctime(p_current_tm_);
 cout << "Current local date and time: " << current_tm_;
}

Однако, когда я запускаю программу, на выходе дважды повторяется местное время.

Я не понимаю, почему это так.

Однако когда я запускаю программу - в опубликованном вами коде нет программы. Нет main, нет объявления и настройки current_time и т. д. Другими словами, опубликуйте минимальный воспроизводимый пример.

PaulMcKenzie 16.06.2024 20:19

возможно, ваше местное время — UTC. Но если нет, системе необходимо сообщить местный часовой пояс. Попробуйте вызвать setlocale(LC_ALL, NULL); выяснить

gbjbaanb 16.06.2024 20:21

@PaulMcKenzie Приношу извинения, я отредактировал вопрос по вашему запросу.

Ali Kamel Ali 16.06.2024 20:33

@gbjbaanb Мое местное время не UTC, но проблема оказалась в том, что возвращаемые значения вызовов gmtime и localtime хранятся в одном и том же адресе памяти, что приводит к перезаписи (см. ответ ниже). Спасибо за помощь :)

Ali Kamel Ali 16.06.2024 20:35

как указано в других ответах, вы используете один и тот же буфер tm для вызовов gmtime и localtime, а также asctime возвращает статическую строку символов. Проблема в том, что эта функция не является потокобезопасной. Вместо этого вы можете рассмотреть strftime, который является потокобезопасным.

Gene 16.06.2024 21:00

@Джин отметил. Спасибо :)

Ali Kamel Ali 16.06.2024 21:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
80
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема здесь в том, что gmtime и localtime имеют один глобальный struct tm и возвращают на него указатель. Таким образом, вызов localtime перезаписывает структуру.

Вместо этого попробуйте это, чтобы получить копию структуры:

#include <iostream>
#include <ctime>
using namespace std;

int main()
{
    time_t current_time = time(NULL);
    tm p_current_tm_0 = * gmtime(&current_time);
    tm p_current_tm_ = * localtime(&current_time);

    char* current_tm_0 = asctime(&p_current_tm_0);
    cout << "Current UTC date and time: " << current_tm_0;

    char* current_tm_ = asctime(&p_current_tm_);
    cout << "Current local date and time: " << current_tm_;
}

(Да, имена переменных больше не точны, поскольку они не являются указателями.)

Альтернативно измените порядок:

    tm * p_current_tm_0 = gmtime(&current_time);
    char* current_tm_0 = asctime(p_current_tm_0);
    cout << "Current UTC date and time: " << current_tm_0;

    tm * p_current_tm_ = localtime(&current_time);
    char* current_tm_ = asctime(p_current_tm_);
    cout << "Current local date and time: " << current_tm_;

Кажется, в этом была проблема. Спасибо :)

Ali Kamel Ali 16.06.2024 20:35

Это немного сложно. Эти функции древние. И пара gmtime()/localtime(), и asctime() используют статическую память. Оба повторно используют существующую память для последующих вызовов, перезаписывая результат предыдущих вызовов! Ваши (кстати, плохо названные) указатели p_current_tm_0 и p_current_tm_ относятся к одной и той же памяти! То же самое делают указатели на символьные строки current_tm_0 и current_tm_: ваши указатели на символы всегда относятся к одному и тому же тексту. Вот тестовая программа:

#define _CRT_SECURE_NO_WARNINGS // visual C peculiarity
#include <iostream>
#include <ctime>
using namespace std;

int main()
{
    time_t current_time;
    time(&current_time);
    tm* p_current_tm_0 = gmtime(&current_time);
    tm* p_current_tm_ = localtime(&current_time);

    char* current_tm_0 = asctime(p_current_tm_0);
    cout << "Current UTC date and time: " << current_tm_0;

    char* current_tm_ = asctime(p_current_tm_);
    cout << "Current local date and time: " << current_tm_;

    cout << "gmt time struct mem:   " << (void*)p_current_tm_0 << "\n";
    cout << "local time struct mem: " << (void*)p_current_tm_ <<  "\n";
    cout << "\n";
    cout << "UTC date and time string: " << (void*)current_tm_0 << "\n";
    cout << "local date and time:      " << (void*)current_tm_  << "\n";
    cout << "\n";
}

Результат:

Current UTC date and time: Sun Jun 16 20:26:20 2024
Current local date and time: Sun Jun 16 20:26:20 2024
gmt time struct mem:   000001DCF4B8F920
local time struct mem: 000001DCF4B8F920

UTC date and time string: 000001DCF4B8FF20
local date and time:      000001DCF4B8FF20

Как видите, как и вы, я получаю ту же «ошибку». Но понятно почему: обе функции используют одну и ту же память как для преобразования секунд с начала эпохи в структурированное значение времени, так и для форматирования его в виде текста! Второй вызов (localtime()) просто перезаписывает первый вызов, получивший время UTC, а сверху второй вызов asctime() перезаписывает текст, созданный первым.

Решение состоит в том, чтобы либо скопировать эту информацию куда-нибудь, либо использовать ее до того, как она будет перезаписана, что я и делаю здесь:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <ctime>
#include <cstring>

using namespace std;

int main()
{
    time_t seconds_since_1970;
    time(&seconds_since_1970);
    tm* ptr_to_static_tm_memory = gmtime(&seconds_since_1970);

    char* ptr_to_static_time_text = asctime(ptr_to_static_tm_memory);
    cout << "Current UTC date and time string: " << ptr_to_static_time_text;

    // Note: No assignment necessary, function uses its built-in static memory!!
    localtime(&seconds_since_1970);

    // Note: No assignment necessary, function uses its built-in static memory!!
    asctime(ptr_to_static_tm_memory);
    cout << "Current local date and time:      " << ptr_to_static_time_text;
}

Я не присваиваю указатели, возвращаемые функциями, когда вызываю их второй раз! Я знаю, где они это напишут...

Дело в том, что раньше многопоточность не была чем-то особенным. (Подобная статическая память, которая используется повторно, не является потокобезопасной.) Авторы библиотек в 1970-х годах считали, что пользователю легко не обременять их, заставляя передавать указатель буфера или заставляя их удалять возвращенную память. (что было бы необходимо, если бы функции распределяли его динамически).

Кстати, передача указателя на буфер сразу же подняла бы вопрос, сколько памяти там доступно, что потребовало бы дополнительного параметра. На самом деле память ограничена, но ее объем неизвестен; крайние случаи могут привести к записи за пределами допустимого диапазона. Учитывая мудрость 45 лет, прошедших с момента создания функции, можно было бы выбрать другой дизайн. В C++ 23 они окончательно объявлены устаревшими, если верить cppreference.

Спасибо за этот довольно информативный ответ :) Я следую учебнику, и репетитор использует некоторые из этих функций (учебник обучает основам программирования с использованием C++, а не самого C++, поэтому репетитор не вдается глубоко в детали)

Ali Kamel Ali 16.06.2024 21:01

Другие вопросы по теме