Проблема при преобразовании из time_t в tm и обратно во time_t - PullRequest
0 голосов
/ 08 ноября 2018

У меня есть time_t значение 1530173696, которое представляет Thursday, June 28, 2018 8:14:56 AM.

Я хочу округлить время до ближайшего часа. В частности, до 1530172800, которые представляют Thursday, June 28, 2018 8:00:00 AM. Итак, моя идея состоит в том, чтобы преобразовать эту time_t в tm структуру, а затем присвоить ее sec и min значениям 0.

Тем не менее, после того, как я это сделаю и после преобразования измененного tm обратно в значение time_t, полученное значение будет далеко. Я получаю значение 1530158400, которое представляет Thursday, June 28, 2018 4:00:00 AM. Это 4 часа. Даже проверка значений до 8:59:59 AM все равно дает округленное значение 4:00:00 AM.

Я написал код ниже, чтобы продемонстрировать проблему. Я использую VisulStudio 2017.

Я не понимаю, что я делаю неправильно. Я ценю любую помощь. Спасибо.

#include <iostream>
#include <time.h>

bool equalTMs(tm& tm1, tm& tm2);
void printTM(tm& myTM);

int main()
{
    tm myTM;
    time_t datetime = 1530173696;
    //datetime = 1530176399; // to check the time_t value of 8:59 AM
    gmtime_s(&myTM, &datetime);
    myTM.tm_sec = 0;
    myTM.tm_min = 0;
    time_t myTime_T = mktime(&myTM);

    tm sanityCheckTM;
    time_t roundedDownToNearestHour = 1530172800;
    gmtime_s(&sanityCheckTM, &roundedDownToNearestHour);
    time_t sanityCheckTimeT = mktime(&sanityCheckTM);

    std::cout << "datetime: " << datetime << std::endl;
    std::cout << "myTime_T: " << myTime_T << std::endl;
    std::cout << std::endl;
    std::cout << "roundedDownToNearestHour: " << roundedDownToNearestHour << std::endl;
    std::cout << "sanityCheckTimeT: " << sanityCheckTimeT << std::endl;
    std::cout << std::endl;
    std::cout << "myTM and sanityCheckTM equal? " << (equalTMs(myTM, sanityCheckTM) ? "true" : "false") << std::endl;

    std::cout << "\nmyTM:-\n\n";
    printTM(myTM);
    std::cout << "\nsanityCheckTM:-\n\n";
    printTM(sanityCheckTM);
    std::cout << "\n";

    time_t _time_t = 1530158400;
    tm _tm;
    gmtime_s(&_tm, &_time_t);
    std::cout << "_time_t: " << _time_t << std::endl;
    std::cout << "_tm and sanityCheckTM equal? " << (equalTMs(_tm, sanityCheckTM) ? "true" : "false") << std::endl;

    std::cout << "\n_tm:-\n\n";
    printTM(_tm);

}

void printTM(tm& myTM)
{
    std::cout << "tm_sec: " << myTM.tm_sec << std::endl;
    std::cout << "tm_min: " << myTM.tm_min << std::endl;
    std::cout << "tm_hour: " << myTM.tm_hour << std::endl;
    std::cout << "tm_mday: " << myTM.tm_mday << std::endl;
    std::cout << "tm_mon: " << myTM.tm_mon << std::endl;
    std::cout << "tm_year: " << myTM.tm_year << std::endl;
    std::cout << "tm_wday: " << myTM.tm_wday << std::endl;
    std::cout << "tm_yday: " << myTM.tm_yday << std::endl;
    std::cout << "tm_isdst: " << myTM.tm_isdst << std::endl;
}

bool equalTMs(tm& tm1, tm& tm2)
{
    return (tm1.tm_sec == tm2.tm_sec)
        && (tm1.tm_min == tm2.tm_min)
        && (tm1.tm_hour == tm2.tm_hour)
        && (tm1.tm_mday == tm2.tm_mday)
        && (tm1.tm_mon == tm2.tm_mon)
        && (tm1.tm_year == tm2.tm_year)
        && (tm1.tm_wday == tm2.tm_wday)
        && (tm1.tm_yday == tm2.tm_yday)
        && (tm1.tm_isdst == tm2.tm_isdst);
}

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Поскольку 1530173696 используется в качестве Unix Time (UTC без учета високосных секунд), это можно решить без использования часовых поясов.

Библиотека даты / времени Говарда Хиннанта может быть использована для решения этой проблемы и проверки правильности полученного ответа. Однако перейдите к концу этого ответа, если вы хотите увидеть, как это сделать очень просто, без использования какой-либо библиотеки.

1530173696 - количество секунд с 1970-01-01 UTC. Если вы хотите преобразовать это в удобочитаемую дату / время, можно:

#include "date/date.h"
#include <iostream>

int
main()
{
    time_t datetime = 1530173696;
    date::sys_seconds tp{std::chrono::seconds{datetime}};
    using date::operator<<;
    std::cout << tp << '\n';
}

который выводит:

2018-06-28 08:14:56

Это не делает ничего, кроме проверки ввода. Более того, tp - это не что иное, как std::chrono::time_point, основанное на system_clock, но с точностью до seconds. Вы можете округлить это до часа с помощью:

tp = floor<std::chrono::hours>(tp);

Здесь floor можно получить из "date.h" в namespace date или, если у вас C ++ 17 или более поздняя версия, вы можете использовать std::chrono::floor. Вы можете использовать "date.h", чтобы снова напечатать tp, и вы получите:

2018-06-28 08:00:00

(по желанию). Чтобы превратить это обратно в time_t, просто извлеките duration, а затем count:

time_t myTime_T = tp.time_since_epoch().count();

Это будет иметь значение 1530172800, как и ожидалось.

Наконец, если вам не нужно распечатывать эти метки времени в удобочитаемой для человека форме, вы можете довольно легко самостоятельно выполнить математику:

time_t myTime_T = datetime / 3600 * 3600;

По сути, это та же операция, что и:

tp = floor<std::chrono::hours>(tp);

за исключением того, что версия floor продолжит получать правильный ответ, когда ввод будет отрицательным (отметка времени до 1970-01-01 00:00:00 UTC). «Ручная» реализация округляет вверх до следующего часа, если дан отрицательный вход.

0 голосов
/ 08 ноября 2018

gmtime_s() возвращает tm, выраженное в UTC время . Вы передаете это mktime(), которое ожидает, что вместо tm будет выражено МЕСТНОЕ время . В вашем профиле StackOverflow говорится, что вы находитесь в Абу-Даби, часовой пояс которого GMT + 4 . Вот почему у вас есть 4-х часовое расхождение.

Используйте localtime_s() вместо gmtime_s().

...