Подтвердить что ты не робот

Как добавить один день к времени, полученному из времени()

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

Добавление 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, указал мне в правильном направлении. Но мне нужно было использовать localtime вместо 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

Однако обратите внимание, что все дни - Sat. Кроме того, он отправляется в марте 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.

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

4b9b3361

Ответ 1

используйте gmtime() для преобразования time_t в структуру 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;
}

Ответ 2

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

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

Ответ 3

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

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

(цитируется спецификация mktime)

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

Ответ 4

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

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

Ответ 5

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

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

Новый ответ требует С++ 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 каждый день). Однако я использовал эту библиотеку, чтобы полностью исследовать все планетные переходы на эту дату. И есть только один часовой пояс, который имел переход в эту дату: Pacific/Easter. И этот переход должен был вернуться назад, а не вперед. Это часовой пояс, используемый для Чили в южном полушарии, где один падает в марте.

Это можно продемонстрировать, выполнив арифметику в 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