Как добавить один день к времени, полученному из времени()
У меня есть время, представленное как количество секунд, прошедших с полуночи, 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.
Спасибо вам за вашу помощь и советы....
Ответы
Ответ 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