2008-11-21 8 views
16

Mam czas reprezentowany jako liczba sekund, które upłynęły od północy, 1 stycznia 1970, UTC (wyniki wcześniejszego połączenia z czasem()). Jak dodać jeden dzień do tego czasu?Jak dodać jeden dzień do czasu uzyskanego z czasu()

Dodawanie 24 * 60 * 60 działa w większości przypadków, ale kończy się niepowodzeniem, jeśli czas letni włącza się lub wyłącza w międzyczasie. Innymi słowy, najczęściej chcę dodać 24 godziny, ale czasami 23 lub 25 godzin.

Aby zilustrować - program:

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

}

Produkuje:

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 

Chcę czasy w marcu 12, 13, ... być również 8 AM.


Odpowiedź udzielona przez FigBug wskazała mi właściwy kierunek. Ale musiałem użyć localtime zamiast 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; 
} 

Daj mi:

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 

co jest, co chcę. Korzystanie z gmtime daje mi czas o 14:00:00

Należy jednak pamiętać, że wszystkie dni są Sat. Także, to idzie do marca 32, 33, itd Jeśli rzucę w funkcji mktime jestem tam, gdzie zacząłem:

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

Daje mi:

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 

Czego mi brakuje ???


OK, starałem się najnowszą propozycję FigBug że jest użycie:

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

zamiast asctime, ale mam takie same wyniki. Tak myślę, że moja biblioteka i/lub kompilator jest pomieszany. Używam g ++ 3.4.4 na cygwin. Skopiowałem pliki do Solaris 5.8 i użyłem g ++ 3.3 do kompilacji. Dostaję poprawne wyniki! W rzeczywistości uzyskać poprawne wyniki, czy używam ctime lub asctime wyjścia:

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 

ja również uzyskać poprawne wyniki (z obu funkcji wyjściowych) na Red Hut Linux z g ++ 3.4.6.

Zgaduję, że natknąłem się na błąd Cygwina.

Dziękuję wszystkim za pomoc i radę ....

+0

Myślę, że chcesz używać ctime nie asctime w ostatecznej wersji, ponieważ chcesz czas w lokalnej strefie czasowej. time() zwraca UTC, localtime() przekształca go w twoją strefę czasową. Dodaj jeden dzień do swojej strefy czasowej i powróć do UTC. Wydrukuj go w strefie czasowej za pomocą ctime(). Takie mylące. –

+0

Jeszcze jedna linia: tm-> tm_wday = (tm-> tm_wday + i)% 7; –

+0

Nie musisz się tym martwić: od mktime docs: Pierwotne wartości członków tm_wday i tm_yday timeptr są ignorowane, a zakresy wartości dla pozostałych członków nie są ograniczone do ich normalnych wartości (jak tm_mday między 1 a 31). –

Odpowiedz

18

użycie gmtime() do konwersji time_t do tm struct

dodasz do dnia ( tm_mday)

użycie mktime() do konwersji struct tm powrót do time_t

zobaczyć time.h uzyskać więcej informacji

Edit:

Właśnie próbowałem, to działa:

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

Czy słowa z podkreśleniem mogą być pisane kursywą? –

+0

użyj some_word_with_underscores zamiast * word * – Jimmy

+0

Lub umieść odwrotny ukośnik przed podkreślnikiem. –

3

zawsze miałem najlepszy wynik z zachowaniem znaczniki czasu UTC i konwertować je do określonej strefy czasowej (w tym letni), gdy chcesz wyświetlić wartości .

Pozwala to zaoszczędzić wiele kłopotów w ten sposób (i sprawia, że ​​program jest niezależny od stref czasowych.

+0

Znaczniki czasu to UTC. –

5

Wystarczy dodać 24 * 60 * 60. Nie powinno zawieść podczas DST, ponieważ UTC nigdy nie użyje DST.

Jeśli nie działa, to nie używasz UTC gdzieś w kodzie. Usuń zależność strefy czasowej.

+1

Chodzi o to, że dodanie jednego dnia do wypowiedzenia 3/31/2008 8:00:00 AM czasu lokalnego powinno dać mi 4/1/2008 8:00:00 AM czasu lokalnego, nawet jeśli czas letni zmienia się lokalnie z dnia na dzień 3/31. –

+0

Nie rozumiem, czy korzystasz z uniksowego znacznika czasu w UTC czy nie? Unixowy znacznik czasu nie ma nic wspólnego z DST. – Pyrolistical

+1

Konwersja czasu UTC na czas lokalny to miejsce, w którym należy dodać lub odjąć godzinę czasu letniego - nie powinno to wpłynąć na czas UTC, jak pisze Pyrolistical. –

5

rozwiązanie FigBug będzie działać prawie za każdym razem, ale to wymaga czasu letniego Fix: tm> tm_isdst = -1

dodatnia lub wartość 0 dla tm_isdst przyczyn mktime(), aby wstępnie zakładają, , że czas letni, odpowiednio, , jest lub nie obowiązuje przez określony czas. Wartość ujemna dla tm_isdst powoduje, że mktime() do próbuje określić, czy czas letni obowiązuje dla określonego czasu .

(cytat z 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; 
} 

W przeciwnym razie nie będzie to błąd (przykład dla Moskwy DST który rozpoczyna 29 marca 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; 
} 

Wyjście:

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 
2

Nowa odpowiedź na bardzo stare pytania tację.

Uzasadnienie nowej odpowiedzi: Istnieją teraz lepsze narzędzia do rozwiązania tego problemu, dzięki czemu wyniki są mniej podatne na błędy, łatwiejsze do odczytania i faktycznie bardziej wydajne dzięki minimalizacji liczby konwersji <.

Nowa odpowiedź wymaga C++ 11/14, <chrono> i tej free, open source, timezone library.

Oto kod:

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

rozpoczyna się od utworzenia zoned_time przez parowanie, co jest pożądaną strefę czasową z timestamp Unix czasu.

Formatowany w dowolnym formacie.

Dodanie 1 dnia odbywa się w lokalnym systemie czasowym strefy czasowej, który uwzględnia oszczędności czasu letniego. Wyjście jest:

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 

Jak się okazuje, to wyjście nie jest dokładnie to, co PO stwierdził, że pożądane (żądana moc wynosi 08:00:00 codziennie). Jednak użyłem tej biblioteki, aby w pełni zbadać przejścia czasowe całej planety w tym dniu. Jest tylko jedna strefa czasowa, która miała przejście w tym dniu: Pacyfik/Wielkanoc. A to przejście miało przenieść z powrotem na godzinę, a nie do przodu. Jest to strefa czasowa używana w Chile na półkuli południowej, gdzie przypada w marcowym przedziale czasowym.

Można to wykazać, wykonując arytmetykę w UTC zamiast w czasie lokalnym. Jest to niewielka korekta do powyższego programu w jednym wierszu:

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

Korzystanie base.get_sys_time(), w przeciwieństwie do base.get_local_time() powoduje arytmetyczną do zrobienia w „czasie systemowym”, który jest UTC zaniedbując przestępnych sekund. Teraz wynik zmieni się na:

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 
Powiązane problemy