C++ UnixTimestamp and readable time format

1.5k views Asked by At

I get a timestamp as an AnsiString from my database in my C++ application. It looks like this "2017-09-12 09:35:10".

Now I want to parse it back into an Unix Timestamp.

AnsiString myDate = Query->Fields->FieldByName("MyDates")->AsString;
TDateTime = StrToDateTime(myDate);

In my specific case I get the following date: 2017-08-10 08:43:35

But the Unixtimestamp I get is: 42957.363599537

Which is 01-01-1970 12:55:57 calculated back into readable format.

What am I missing here?!...

3

There are 3 answers

2
Howard Hinnant On

You could try Howard Hinnant's free, open source, header-only, date/time library which extends the C++11 <chrono> header to handle calendrical computations.

#include "date.h"
#include <iostream>
#include <sstream>

int
main()
{
    std::istringstream in{"2017-08-10 08:43:35"};
    date::sys_seconds Unixtimestamp;
    in >> date::parse("%F %T", Unixtimestamp);
    std::cout << Unixtimestamp.time_since_epoch().count() << '\n';
    std::cout << date::format("%F %T\n", Unixtimestamp);
}

Output:

1502354615
2017-08-10 08:43:35
0
Remy Lebeau On

C++Builder 6 and later have a DateTimeToUnix() function in the DateUtils unit:

#include <SysUtils.hpp>
#include <DateUtils.hpp>

AnsiString myDateStr = Query->Fields->FieldByName("MyDates")->AsString;
TDateTime myDate = StrToDateTime(myDateStr);
__int64 myTimestamp = DateTimeToUnix(myDate);

If you are using C++Builder 5 or earlier, you can implement your own DateTimeToUnix() function like this:

#include <SysUtils.hpp>

// Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970)
const __int64 UnixDateDelta = 25569;

__int64 DateTimeToMilliseconds(const TDateTime &ADateTime)
{
    TTimeStamp LTimeStamp = DateTimeToTimeStamp(ADateTime);
    return (__int64(LTimeStamp.Date) * MSecsPerDay) + LTimeStamp.Time;
}

__int64 DateTimeToUnix(const TDateTime &AValue)
{
    __int64 Result = abs(DateTimeToMilliseconds(UnixDateDelta) - DateTimeToMilliseconds(AValue)) / MSecsPerSec;
    if (AValue < UnixDateDelta)
        Result = -Result;
    return Result;
}

Also note that the version of StrToDateTime() you are using is subject to the user's current locale settings for date/time. Since you are dealing with a very specific date/time format, I suggest you parse it manually and not rely on any particular locale, eg:

#include <SysUtils.hpp>
#include <stdio.h>

AnsiString myDateStr = Query->Fields->FieldByName("MyDates")->AsString;

Word wYear, wMonth, wDay, wHour, wMin, wSec;
sscanf(myDateStr.c_str(), "%hu-%hu-%hu %hu:%hu:%hu", &wYear, &wMonth, &wDay, &wHour, &wMin, &wSec);

TDateTime myDate = EncodeDate(wYear, wMonth, wDay) + EncodeTime(wHour, wMin, wSec, 0);
__int64 myTimestamp = DateTimeToUnix(myDate);

If you are using C++Builder 2006 or later, you can use the overloaded version of StrToDateTime() that accepts a TFormatSettings struct as input:

#include <SysUtils.hpp>
#include <DateUtils.hpp>

String myDateStr = Query->Fields->FieldByName("MyDates")->AsString;

TFormatSettings myFmt = TFormatSettings::Create();
myFmt.ShortDateFormat = _D("yyyy-mm-dd hh:nn:ss");
myFmt.DateSeparator = _D('-');
myFmt.TimeSeparator = _D(':');

TDateTime myDate = StrToDateTime(myDateStr, myFmt);
__int64 myTimestamp = DateTimeToUnix(myDate);
0
borisbn On

You got not Unix TimeStamp, but TDateTime. The following is from Builder's documentation :

The integral part of a TDateTime value is the number of days that have passed since 12/30/1899. The fractional part of a TDateTime value is the time of day.

In other case, the Unix TimeStump is a number of seconds from 01.01.1970