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

StackOverflow https://stackoverflow.com/questions/310363

  •  10-07-2019
  •  | 
  •  

Вопрос

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

Добавление 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 вместо gmt.

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.

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

Это было полезно?

Решение

использование gmt время() чтобы преобразовать time_t к a структура tm

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

использование mktime() чтобы преобразовать структура tm вернемся к time_t

видишь время.ч для получения дополнительной информации

Редактировать:

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

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;
}

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

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

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

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

Положительное значение tm_isdst или 0 заставляет 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

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

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

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

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

Для нового ответа требуется 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top