Как добавить один день ко времени, полученному из time () - PullRequest
17 голосов
/ 22 ноября 2008

У меня есть время, представленное в виде количества секунд, прошедших с полуночи 1 января 1970 года по Гринвичу (результаты более раннего вызова времени ()). Как мне добавить один день к этому времени?

Добавление 24 * 60 * 60 работает в большинстве случаев, но не работает, если летнее время включается или выключается между ними. Другими словами, я в основном хочу добавить 24 часа, но иногда 23 или 25 часов.

Для иллюстрации - программа:

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

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    time_t time = base + i * 24 * 60 * 60;
    std::cout << ctime(&time);
  }
  return 0;

}

Производит:

Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006

Я хочу, чтобы время для 12, 13 марта ... тоже было 8 утра.


Ответ, предоставленный FigBug, указал мне правильное направление. Но мне пришлось использовать местное время вместо gmtime.

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    std::cout << asctime(tm);
 }
 return 0;
}

Дайте мне:

Sat Mar 11 08:00:00 2006
Sat Mar 12 08:00:00 2006
Sat Mar 13 08:00:00 2006
Sat Mar 14 08:00:00 2006

Что я и хочу. Использование gmtime дает мне время в 14:00: 00

Однако обратите внимание, что все дни субботы. Кроме того, это относится к 32, 33 марта и т. Д. Если я добавлю функцию mktime, я вернусь к тому, с чего начал:

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

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    time_t time = mktime(tm);
    std::cout << asctime(tm);
 }
 return 0;
}

дает мне:

Sat Mar 11 08:00:00 2006
Sun Mar 12 09:00:00 2006
Mon Mar 13 09:00:00 2006
Tue Mar 14 09:00:00 2006

Чего мне не хватает ???


ОК, я опробовал последнее предложение FigBug, которое будет использовать:

 std::cout << ctime(&time);

вместо asctime, но я получаю те же результаты. Поэтому я предполагаю, что моя библиотека и / или компилятор испорчены. Я использую G ++ 3.4.4 на Cygwin. Я скопировал файлы в Solaris 5.8 и использовал g ++ 3.3 для компиляции. Я получаю правильные результаты там! Фактически я получаю правильные результаты, использую ли я ctime или asctime для вывода:

Sat Mar 11 08:00:00 2006
Sun Mar 12 08:00:00 2006
Mon Mar 13 08:00:00 2006
Tue Mar 14 08:00:00 2006

Я также получаю правильные результаты (с обеими функциями вывода) в Red Hut Linux с g ++ 3.4.6.

Так что я думаю, что я столкнулся с ошибкой Cygwin.

Спасибо за вашу помощь и совет ....

Ответы [ 5 ]

19 голосов
/ 22 ноября 2008

используйте gmtime () для преобразования time_t в struct tm

добавить один день ( tm_mday )

используйте mktime () для преобразования struct tm обратно в time_t

см. time.h для получения дополнительной информации

Edit:

Я только что попробовал, это работает:

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    time_t next = mktime(tm);
    std::cout << ctime(&next);
 }
 return 0;
}
5 голосов
/ 13 марта 2009

Решение FigBug будет работать почти каждый раз, но требует исправления DST: tm-> tm_isdst = -1

Положительное или 0 значение для tm_isdst вызывает mktime () изначально предполагать что летнее время, соответственно действует или не действует на указанное время. Отрицательный значение для tm_isdst заставляет mktime () попытаться определить, есть ли дневной свет Экономия времени действует для указанное время.

(цитата из mktime spec )

int main()
{
  time_t base = 1142085600;
  for(int i = 0; i < 4; ++i) {
    struct tm* tm = localtime(&base);
    tm->tm_mday += i;
    tm->tm_isdst = -1;        // don't know if DST is in effect, please determine
                              // this for me
    time_t next = mktime(tm);
    std::cout << ctime(&next);
 }
 return 0;
}

В противном случае будет ошибка (пример для летнего времени в Москве, которое начинается 29 марта 2009 г. 01:59:59):

int main()
{
    // 28 March 2009 05:00:00 GMT ( local - 08:00 (MSK) )
    time_t base = 1238216400;

    std::time_t start_date_t = base;
    std::time_t end_date_t = base;

    std::tm start_date = *std::localtime(&start_date_t);
    std::tm end_date = *std::localtime(&end_date_t);

    end_date.tm_mday += 1;
//    end_date.tm_isdst = -1;

    std::time_t b = mktime(&start_date);
    std::time_t e = mktime(&end_date);

    std::string start_date_str(ctime(&b));
    std::string stop_date_str(ctime(&e));

    cout << " begin (MSK) (DST is not active): " << start_date_str;
    cout << " end   (MSD) (DST is active):     " << stop_date_str;
}

Выход:

begin (MSK) (DST is not active): Sat Mar 28 08:00:00 2009
end   (MSD) (DST is active):     Sun Mar 29 09:00:00 2009
5 голосов
/ 22 ноября 2008

Просто добавьте 24 * 60 * 60. Он не должен выходить из строя во время летнего времени, поскольку UTC никогда не будет использовать летнее время.

Если это не удается, значит, вы не используете UTC где-то в вашем коде. Удалить зависимость от часового пояса.

3 голосов
/ 22 ноября 2008

У меня всегда был лучший результат с сохранением меток времени в формате UTC и преобразованием их в указанный часовой пояс (включая переход на летнее время), когда вы хотите отобразить значения.

Это избавит вас от многих хлопот (и сделает вашу программу независимой от часовых поясов.

1 голос
/ 30 мая 2016

Новый ответ на очень старый вопрос.

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

Новый ответ требует C ++ 11/14, <chrono>, и это бесплатная, с открытым исходным кодом, библиотека часовых поясов .

Вот код:

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

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto base = make_zoned("Pacific/Easter", sys_seconds{1142085600s});
    for (int i = 0; i < 4; ++i)
    {
        std::cout << format("%a %b %d %T %Y %Z", base) << '\n';
        base = base.get_local_time() + days{1};
    }
}

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

Это форматируется в любом желаемом формате.

И добавление 1 дня производится в местной системе часовых поясов, которая учитывает переход на летнее время. Выход:

Sat Mar 11 09:00:00 2006 -05
Sun Mar 12 09:00:00 2006 -06
Mon Mar 13 09:00:00 2006 -06
Tue Mar 14 09:00:00 2006 -06

Как оказалось, этот выходной сигнал не совсем соответствует заявленному OP, который он желал (запрошенный выходной сигнал - в 08:00:00 каждый день). Однако я использовал эту библиотеку, чтобы полностью исследовать временные переходы всей планеты в эту дату. И есть только один часовой пояс, который имел переход на эту дату: Тихий океан / Пасха. И этот переход должен был двигаться назад в час, а не вперед . Это часовой пояс, используемый для Чили, в южном полушарии, где один падает назад в мартовские временные рамки.

Это можно продемонстрировать, выполнив арифметику в UTC, а не по местному времени. Это небольшая корректировка вышеуказанной программы в одной строке:

        base = base.get_sys_time() + days{1};

Использование base.get_sys_time(), в отличие от base.get_local_time(), приводит к выполнению арифметики за «системное время», которое по UTC не учитывает високосных секунд. И теперь вывод изменится на:

Sat Mar 11 09:00:00 2006 -05
Sun Mar 12 08:00:00 2006 -06
Mon Mar 13 08:00:00 2006 -06
Tue Mar 14 08:00:00 2006 -06
...