4

Is there a contemporary way to convert unix timestamps to a human readable date? Since I want to circumnavigate the year 2038 problem, I want to use int64s. I target to convert e. g. 1205812558 to

year = 2008, month = 3, day = 18, hour = 17, minute = 18, second = 36

All I have is now

auto year = totalSeconds / secondsPerYear + 1970;
// month and day missing
auto hours = totalSeconds / 3600 % 24;
auto minutes = totalSeconds / 60 % 60;
auto seconds = totalSeconds % 60; 
mrflash818
  • 930
  • 13
  • 24
  • Yes it is either `std::localtime()` or `std::gmtime()` – Slava Aug 23 '18 at 20:36
  • 1
    [`std::strftime`](https://en.cppreference.com/w/cpp/chrono/c/strftime)? Or [`std::put_time`](https://en.cppreference.com/w/cpp/io/manip/put_time)? – Some programmer dude Aug 23 '18 at 20:36
  • 1
    How are you getting the timestamp? C++ has a time library and a chrono library to help deal with dates and times. – NathanOliver Aug 23 '18 at 20:36
  • @Slava & Some programmer dude: But these functions use struct tm from which contains normal 32 bit ints. I want to use 64 bit ints. –  Aug 23 '18 at 20:37
  • @NathanOliver from user input, more or less –  Aug 23 '18 at 20:39
  • @DanielH 32 bits int for year is not enough for you? That is 2 billion years. Hmm then I am afraid you cannot work with unix timestamps. – Slava Aug 23 '18 at 20:40
  • Why would you need 64 bits to represent (say) seconds mod 60, which can only have the range 0 to 59? –  Aug 23 '18 at 20:41
  • @Slava No, I mean the input `totalSeconds` should be in 64 bit to prevent bugs on 2038-01-19. –  Aug 23 '18 at 20:42
  • 1
    @DanielH `time_t` is already 64bits on most platforms. Which one do you use? – Slava Aug 23 '18 at 20:43
  • Uhh okay. Didn't know that it works with 64 bit integers too. But how do I use std::localtime and std::gmtime since I want a variable in std::time instead of std::time_t = std::time(nullptr)? –  Aug 23 '18 at 20:49
  • @DanielH what do you mean by "variable in `std::time`"? – Slava Aug 23 '18 at 20:54
  • Possibly relevant https://stackoverflow.com/questions/24686846/get-current-time-in-milliseconds-or-hhmmssmmm-format (`std::put_time`) https://stackoverflow.com/questions/31281293/timestamps-for-embedded-system (`std::strftime`) https://stackoverflow.com/questions/38034033/c-localtime-this-function-or-variable-may-be-unsafe (safe localtime) – Galik Aug 23 '18 at 20:57
  • totalSeconds is a int64 passed as parameter. I now have: `time_t t = totalSeconds; std::tm* tm = std::localtime(&t);` But this leads to struct tm being nullptr. –  Aug 23 '18 at 20:57
  • @DanielH what value do you have in `totalSeconds` ? – Slava Aug 23 '18 at 20:59
  • @Slava For example 32879409516 –  Aug 23 '18 at 21:00
  • @DanielH cannot reproduce: https://ideone.com/fHEGeo what platform do you use? – Slava Aug 23 '18 at 21:03
  • If I try an integer less than 2^31-1, it works but tm_mhour and tm_mon is wrong. –  Aug 23 '18 at 21:04
  • @DanielH I think you should create [mcve] and open another question, either localy on RH or ideone works just fine for me. – Slava Aug 23 '18 at 21:05
  • @Slava I use MSVC++ x64 compiler, If I try your example given, it only prints 0000000000000000 –  Aug 23 '18 at 21:08
  • Okay then, is there a way to get month and day, the two missing variables in my original post? –  Aug 23 '18 at 21:10
  • @DanielH you can convert your timestamp into windows system time and then use MSDN or convert day number to year/month/day of month using gregorian day formula – Slava Aug 23 '18 at 21:14
  • How do I convert the timestamp to windows system time? –  Aug 23 '18 at 21:17

4 Answers4

6

In C++20 (according to the draft-spec for C++20 as it stands today), you will be able to say:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    cout << sys_seconds{1205812558s} << '\n';
    cout << sys_seconds{32879409516s} << '\n';
}

and it will output:

2008-03-18 03:55:58
3011-11-28 17:18:36

These are datetimes in UTC.

You can use Howard Hinnant's date library to experiment with this extended <chrono> functionality today by adding:

#include "date/date.h"

and

    using namespace date;

to the above program. You can experiment online with this program here.


A comment below asks for what this looks like if the value is stored in uint64_t. The answer is that you need to convert the integral type to seconds, and then the seconds to sys_seconds:

uint64_t i = 1205812558;
cout << sys_seconds{seconds(i)} << '\n';

There do exist limits on this contemporary functionality, but they live out near the years +/-32K (far beyond the limits of the accuracy of the current civil calendar).

To be completely transparent, there do exist ways of doing this using only C++98/11/14/17, but they are more complicated than this, and are subject to multithreading bugs. This is due to the use of an antiquated C API that was designed before things like multithreading and C++ were on the horizon, and when the year 2001 was only associated with science fiction (e.g. gmtime).

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Is there an equivalent of `sys_second` to convert a timestamp in `std::chrono::nanoseconds` to human readable form? – an4s Apr 08 '19 at 17:49
  • Yes: `cout << sys_time{1554747471859236000ns} << '\n';` – Howard Hinnant Apr 08 '19 at 18:18
  • Thanks for the follow up. I notice that `sys_time` will work with timestamps generated with `system_clock`. This does not seem to work with timestamps generated with `steady_clock`. Is there an equivalent for the latter? Note that `system_clock` timestamps are not the same length as `steady_clock` timestamps. See here: https://wandbox.org/permlink/QQ3T8TRvvfT2oUq4 – an4s Apr 09 '19 at 19:57
  • Nope. There is no relationship between `steady_clock` and a human calendar. One could stream out `steady_clock::now().time_since_epoch()` and get the underlying duration. And you can format that duration however you want. But there is no portable epoch with which that duration is measured against. – Howard Hinnant Apr 09 '19 at 20:04
  • I think the epoch is not guaranteed to match the unix epoch. – user1095108 May 17 '21 at 22:58
  • You are correct for C++11/14/17. However all three major implementations _do_ match the unix epoch. This universal existing practice was standardized for C++20: http://eel.is/c++draft/time.clock.system#overview-1. – Howard Hinnant May 18 '21 at 00:11
  • The code you wrote at the beginning of your answer does not compile https://onlinegdb.com/Pdwm5-OOp – user2565010 Dec 24 '22 at 19:58
  • Only the latest MSVC is shipping this part of C++20. The others are getting close, but not there yet. https://gcc.godbolt.org/z/1zYGnrbGc, https://www.reddit.com/r/cpp/comments/zt1o9k/libstdc13_gets_c20_chrono/ – Howard Hinnant Dec 24 '22 at 20:32
  • Looks like gcc-13 will have it: https://wandbox.org/permlink/8URclqiZkAtlfOjQ – Howard Hinnant Dec 24 '22 at 20:53
3

Wrapper

#include <chrono>
char* get_time(time_t unix_timestamp)
{
char time_buf[80];
struct tm ts;
ts = *localtime(&unix_timestamp);
strftime(time_buf, sizeof(time_buf), "%a %Y-%m-%d %H:%M:%S %Z", &ts);

return time_buf;
}
  • Nice solution and it is not complicated and is not dependent on C++20; I have tested it and it works! :) – dariush Oct 04 '21 at 19:36
  • 1
    it's returning pointer to `time_buf` which is local variable which will SEGV violation – user5858 Jan 09 '23 at 18:42
  • not directly a segv, but it refers to the memory that no longer is a valid area and will certainly be overwritten soon, then string may no longer have termination character that would then eventually lead to segv. – no one special Mar 29 '23 at 16:08
1

Howard Hinnant's date library makes things pretty easy:

#include "date.h"

int main()
{
    using namespace date;
    time_t time = 32879409516;
    auto sysTime = std::chrono::system_clock::from_time_t(time);
    auto date = year_month_day(floor<days>(sysTime));
    std::cout << date << "\n";
}
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
0

A good straight forward solution but could do with some minor changes:

uint32_t days = (uint32_t)floor(subt / 86400);
uint32_t hours = (uint32_t)floor(((subt - days * 86400) / 3600) % 24);
uint32_t minutes = (uint32_t)floor((((subt - days * 86400) - hours * 3600) / 60) % 60);
uint32_t seconds = (uint32_t)floor(((((subt - days * 86400) - hours * 3600) - minutes * 60)) % 60);
printf("Time remaining: %u Days, %u Hours, %u Minutes, %u Seconds\n", days, hours, minutes, seconds);
TKA
  • 183
  • 2
  • 4
  • That might be an helpful answer for some people but could you improve it by showing a sample output? That would make it a lot more helpful. – Patrick Dec 07 '20 at 09:42