C++ custom time date struct to utc epoch - c++

I use a library that uses the following struct to define a start timestamp as follows.
struct SYSTEMTIME {
/** year */
WORD year;
/** month */
WORD month;
/** day of week */
WORD dayOfWeek;
/** day */
WORD day;
/** hour */
WORD hour;
/** minute */
WORD minute;
/** second */
WORD second;
/** milliseconds */
WORD milliseconds;
};
For every log entry after this time is specified in nanoseconds difference from this first timestamp.
Lets say its UTC 2017-12-19 14:44:00
And the first following log entries are 397000ns after this.
How do I create a chronos, time_t or unix time from epoch from the first SYSTEMTIME struct and then add the nanoseconds to it.
Printout should be for this first entry
2017-12-19 14:44:00.000397
Best regards

Updated
I've slightly modified the code below to convert between SYSTEMTIME and date::sys_time<std::chrono::milliseconds>, instead of date::sys_time<std::chrono::nanoseconds>.
Rationale: So that there is no implicit precision loss in to_SYSTEMTIME. Clients of to_SYSTEMTIME can explicitly truncate precision in any way that they desire (floor, round, ceil, etc.). And failure to truncate precision (if needed) won't be a silent run time error.
The client code (in main) is not impacted by this change.
You could use Howard Hinnant's free, open-source, header-only date/time library for this:
#include "date/date.h"
#include <iostream>
using WORD = int;
struct SYSTEMTIME {
/** year */
WORD year;
/** month */
WORD month;
/** day of week */
WORD dayOfWeek;
/** day */
WORD day;
/** hour */
WORD hour;
/** minute */
WORD minute;
/** second */
WORD second;
/** milliseconds */
WORD milliseconds;
};
date::sys_time<std::chrono::milliseconds>
to_sys_time(SYSTEMTIME const& t)
{
using namespace std::chrono;
using namespace date;
return sys_days{year{t.year}/t.month/t.day} + hours{t.hour} +
minutes{t.minute} + seconds{t.second} + milliseconds{t.milliseconds};
}
int
main()
{
using namespace std::chrono;
using namespace date;
SYSTEMTIME st{2017, 12, 2, 19, 14, 44, 0, 0};
auto t = to_sys_time(st) + 397000ns;
std::cout << floor<microseconds>(t) << '\n';
}
Output:
2017-12-19 14:44:00.000397
This converts a SYSTEMTIME to a std::chrono::time_point<system_clock, milliseconds> (which has a type-alias named date::sys_time<milliseconds>) by collecting the different parts out of the SYSTEMTIME. It then simply adds nanoseconds to that time_point, truncates it to the desired precision of microseconds, and streams it out.
If it would be helpful, here is how you could use the same library to do the opposite conversion:
SYSTEMTIME
to_SYSTEMTIME(date::sys_time<std::chrono::milliseconds> const& t)
{
using namespace std::chrono;
using namespace date;
auto sd = floor<days>(t);
year_month_day ymd = sd;
auto tod = make_time(t - sd);
SYSTEMTIME x;
x.year = int{ymd.year()};
x.month = unsigned{ymd.month()};
x.dayOfWeek = weekday{sd}.c_encoding();
x.day = unsigned{ymd.day()};
x.hour = tod.hours().count();
x.minute = tod.minutes().count();
x.second = tod.seconds().count();
x.milliseconds = tod.subseconds().count();
return x;
}

You can use mktime() from <ctime> to convert tm, to time_t, which is an integer type.
tm is very simmilar to your SYSTEMTIME struct. Therefore you sould be able to translate them easily back and forth.
translate your struct into tm and then use gmtime() like this.
#include <ctime>
struct tm time = fromSystemtime(...);
time_t timestamp;
timestamp = mktime(&time);
For more details see the following links:
struct tm
time_t
mktime

Related

How to convert a Pascal TDateTime(double) time to a Unix epoch in C++

I need to convert from a Pascal TDateTime object which is a double value to Unix epoch using c++.
A possible solution is proposed (https://forextester.com/forum/viewtopic.php?f=8&t=1000):
unsigned int UnixStartDate = 25569;
unsigned int DateTimeToUnix(double ConvDate)
{
return((unsigned int)((ConvDate - UnixStartDate) * 86400.0));
}
However, this conversion code produces errors such as:
TDateTime time value = 37838.001388888886 (05.08.2003 02:00)
which converts to Unix epoch 1060041719 (05.08.2003 00:01:59) which is clearly incorrect.
How is it possible to convert this TDateTime value accurately?
The Delphi/C++Builder RTL has a DateTimeToUnix() function for this exact purpose.
In a TDateTime, the integral portion is the number of days from December 30 1899, and the fractional portion is the time of day from 00:00:00.000. Using raw math involving more than just whole days can be a little tricky, since floating-point math is inaccurate.
For instance, 0.001388888886 is not exactly 00:02:00, it is closer to 00:01:59.999. So you are encountering a rounding issue, which is exactly what you have to watch out for. TDateTime has milliseconds precision, and there are 86400000 milliseconds in a day, so .001388888886 is 119999.9997504 milliseconds past 00:00:00.000, which is 00:01:59 if those milliseconds are truncated to 119999, or 00:02:00 if they are rounded up to 120000.
The RTL stopped using floating-point arithmetic on TDateTime years ago due to subtle precision loss. Modern TDateTime operations do a round-trip through TTimeStamp now to avoid that.
Since you are trying to do this from outside the RTL, you will need to implement the relevant algorithms in your code. The algorithm you have shown is how the RTL used to convert a TDateTime to a Unix timestamp many years ago, but that is not how it does so anymore. The current algorithm looks more like this now (translated to C++ from the original Pascal):
#include <cmath>
#define HoursPerDay 24
#define MinsPerHour 60
#define SecsPerMin 60
#define MSecsPerSec 1000
#define MinsPerDay (HoursPerDay * MinsPerHour)
#define SecsPerDay (MinsPerDay * SecsPerMin)
#define SecsPerHour (SecsPerMin * MinsPerHour)
#define MSecsPerDay (SecsPerDay * MSecsPerSec)
#define UnixDateDelta 25569 // Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970)
#define DateDelta 693594 // Days between 1/1/0001 and 12/31/1899
const float FMSecsPerDay = MSecsPerDay;
const int IMSecsPerDay = MSecsPerDay;
struct TTimeStamp
{
int Time; // Number of milliseconds since midnight
int Date; // One plus number of days since 1/1/0001
};
typedef double TDateTime;
TTimeStamp DateTimeToTimeStamp(TDateTime DateTime)
{
__int64 LTemp = std::round(DateTime * FMSecsPerDay); // <-- this might require tweaking!
__int64 LTemp2 = LTemp / IMSecsPerDay;
TTimeStamp Result;
Result.Date = DateDelta + LTemp2;
Result.Time = std::abs(LTemp) % IMSecsPerDay;
return Result;
}
__int64 DateTimeToMilliseconds(const TDateTime ADateTime)
{
TTimeStamp LTimeStamp = DateTimeToTimeStamp(ADateTime);
return (__int64(LTimeStamp.Date) * MSecsPerDay) + LTimeStamp.Time;
}
__int64 SecondsBetween(const TDateTime ANow, const TDateTime AThen)
{
return std::abs(DateTimeToMilliseconds(ANow) - DateTimeToMilliseconds(AThen)) / MSecsPerSec;
}
__int64 DateTimeToUnix(const TDateTime AValue)
{
__int64 Result = SecondsBetween(UnixDateDelta, AValue);
if (AValue < UnixDateDelta)
Result = -Result;
return Result;
}
Note my comment in DateTimeToTimeStamp(). I'm not sure if std::round() produces exactly the same result as Delphi's System::Round() for all values. You will have to experiment with it.
Here's a very simple way to do it using my free, open-source, header-only C++20 chrono preview library (works with C++11/14/17):
#include "date/date.h"
#include <iostream>
date::sys_seconds
convert(double d)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
return round<seconds>(sys_days{1899_y/12/30} + ddays{d});
}
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto tp = convert(37838.001388888886);
cout << tp << " = " << (tp-sys_seconds{})/1s << '\n';
}
Output:
2003-08-05 00:02:00 = 1060041720
An IEEE 64bit double has plenty of precision to round to the nearest second in this range. The precision is finer than 1µs until some time in the year 2079. And the precision will stay below 10µs for another thousand years.
Fwiw, here's the reverse conversion:
double
convert(date::sys_seconds tp)
{
using namespace date;
using namespace std::chrono;
using ddays = duration<double, days::period>;
return (tp - sys_days{1899_y/12/30})/ddays{1};
}
So with that, this:
cout << setprecision(17) << convert(convert(37838.001388888886)) << '\n';
outputs:
37838.001388888886
Good round trip.
Update C++20
This can now be done in C++20 (assuming your std::lib vendor has updated <chrono> to C++20).
#include <chrono>
#include <iostream>
std::chrono::sys_seconds
convert(double d)
{
using namespace std::chrono;
using ddays = duration<double, days::period>;
return round<seconds>(sys_days{1899y/12/30} + ddays{d});
}
double
convert(std::chrono::sys_seconds tp)
{
using namespace std::chrono;
using ddays = duration<double, days::period>;
return (tp - sys_days{1899y/12/30})/ddays{1};
}

Creating a `std::chrono::time_point` from a calendar date known at compile time

This answer shows how to parse a string to a std::chrono::time_point, as follows:
std::tm tm = {};
std::stringstream ss("Jan 9 2014 12:35:34");
ss >> std::get_time(&tm, "%b %d %Y %H:%M:%S");
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
If I want to create a std::chrono::time_point from a (Gregorian) calendar date whose year, month, and day-of-month are known at compile time, is there any simpler way than parsing it from a string as suggested above?
Yes, you can do the entire computation at compile time, creating a constexpr system_clock::time_point using Howard Hinnant's date/time library.
#include "date/date.h"
#include <chrono>
int
main()
{
using namespace date;
using namespace std::chrono;
constexpr system_clock::time_point tp = sys_days{January/9/2014} + 12h + 35min + 34s;
static_assert(tp == system_clock::time_point{1389270934s}, "");
}
This is assuming that the date/time is UTC. If it isn't, you will have to manually add/subtract the UTC offset to make it so. As time zone rules are changed at the whim of politicians all the time, there is little hope in making them constexpr. Even historical time zone rules are updated when misunderstandings come to light.
Also this program will port to C++20 by dropping #include "date/date.h" and using namespace date;. Also using Howard Hinnant's date/time library requires C++14 constexpr muscle. C++11 constexpr is not sufficient (but you can do it at run-time, dropping the constexpr and static_assert).
If you have c++20, or will use Howard Hinnant date/time library, then Howard Hannant's answer is better, as it gives you a constexpr time_point.
However, if one doesn't yet have c++20 and wants to avoid adding more external libraries, then this answer is still useful.
You can set the members of the std::tm individually in the initializer, to avoid parsing a string.
// 9th January, 2014
#define DAY 9
#define MONTH 1
#define YEAR 2014
std::tm tm = { /* .tm_sec = */ 0,
/* .tm_min = */ 0,
/* .tm_hour = */ 0,
/* .tm_mday = */ (DAY),
/* .tm_mon = */ (MONTH) - 1,
/* .tm_year = */ (YEAR) - 1900,
};
tm.tm_isdst = -1; // Use DST value from local time zone
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
The designated initializers are commented out since they are only available in C++20 (though gcc has supported trivial designated initializers as an extension for some time and would work with this case). The fields initialized to zero could be omitted if one had full C++20 designated initializers and wanted midnight on the target date.
It is important to note that mktime will interpret the tm as local time, not GMT nor UTC. If tm_isdst is not set to -1, it will be local standard time, even if daylight savings (summer time) would be in use in the local time zone for the time specified.
Producing a UTC time point from a std::tm, a problem shared with your example, is addressed in other questions, such as Easy way to convert a struct tm (expressed in UTC) to time_t type
This works with C++11 and above:
#include <chrono>
std::chrono::system_clock::time_point
createDateTime(int year,
int month,
int day,
int hour,
int minute,
int second) // these are UTC values
{
tm timeinfo1 = tm();
timeinfo1.tm_year = year - 1900;
timeinfo1.tm_mon = month - 1;
timeinfo1.tm_mday = day;
timeinfo1.tm_hour = hour;
timeinfo1.tm_min = minute;
timeinfo1.tm_sec = second;
tm timeinfo = timeinfo1;
time_t tt = toUTC(timeinfo);
return std::chrono::system_clock::from_time_t(tt);
}
time_t toUTC(std::tm& timeinfo)
{
#ifdef _WIN32
std::time_t tt = _mkgmtime(&timeinfo);
#else
time_t tt = timegm(&timeinfo);
#endif
return tt;
}
Taken from ApprovalTests/utilities/DateUtils.cpp

math with ctime and time_t

Does anyone know how todo math with ctime? I need to be able to get the time in sec in "time_t" (like it normally does) and then subtract a set number of seconds from it before inputting time_t into ctime to get the time and date.
so basically it would calculating the date of so many sec ago.
time_t
The most basic representation of a date and time is the type time_t. The value of a time_t variable is the number of seconds since January 1, 1970, sometimes call the Unix epoch. This is the best way to internally represent the start and end times for an event because it is easy to compare these values.
struct tm
While time_t represents a date and time as a single number, struct tm represents it as a struct with a lot of numbers:
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
};
Conversion
You can convert a time_t value to a struct tm value using the localtime function:
struct tm startTM;
time_t start;
/* ... */
startTM = *localtime(&start);
So,you can subtract subtract a set number of seconds like this
startTm.tm_sec -= somesecond;
add convert to time_t like this
struct tm startTM;
time_t start;
/* ... */
start = mktime(&startTM);
and use ctime fun to convert date
ctime(&start)
hope it can helpful!
You can try:
time_t now = time( NULL);
struct tm now_tm = *localtime( &now);
now_tm.tm_sec -= 50; // subtract 50 seconds to the time
now_tm.tm_sec +=1000; // add 1000 sec to the time
printf( "%s\n", asctime( &now_tm));
time_t is an integral type. It always represents a number of seconds, so you can freely add/subtract integers from it.
Example:
time_t now = time(nullptr);
time_t one_minute_ago = now - 60;
std::cout << ctime(&one_minute_ago) << std::endl;

Wrong time with localtime()

Running dateon my server results in the correct time. But using localtime() in C(++) I'm getting the wrong time.
Running date: Fr 30. Nov 12:15:36 CET 2012
Using localtime(): Fr 30 Nov 2012 11:15:36 CET
What's wrong here?
OS: Debian 5.0.10
Some code:
struct tm* today;
today = localtime(...);
strftime(timeBuffer,50,myConnection.getMetaData().getDateFormat().c_str(),today);
disclaimer : This answer was written before any mention of strftime was added, and was a gut reaction to the 1 hour difference in the timestamps. Looking back at it now, that 1 hour difference couldn't have been due to DST (because the dates are not in summer), but is likely showing a UTC timestamp (1 hour difference between UTC and CET).
Unfortunately, the answer was accepted, and so I can't delete it. Even more unfortunate, is that the question as it stands is not answerable without additional information.
Leaving the original answer here for full transparency, but know that it does not address the question as asked :
The struct tm returned by localtime has a tm_isdst field that indicates whether daylight saving time (DST) is in effect. You need to take that field into account when formatting the time.
Try using asctime to format the time eg. :
puts(asctime(today));
I have experienced the same problem while writing a date adjustment routine. Adding 86400 seconds (= 1 day) to any given datetime value should result in incrementing the datetime value by one day. However in testing, the output value invariably added exactly one hour to the expected output. For instance, '2019-03-20 00:00:00' incremented by 86400 seconds resulted in '2019-03-21 01:00:00'. The reverse also occurred: '2019-03-21 00:00:00' decremented by -86400 resulted in '2019-03-20 01:00:00'.
The solution (inexplicably) was to subtract 3600 seconds (one hour) from the final interval before applying it to the input datetime.
The solution (thanks to helpful comments from #Lightness-Races-in-Orbit) was to set tm_isdst to -1 before calling mktime(). This tells mktime() that the DST status for the input datetime value is unknown, and that mktime() should use the system timezone databases to determine the correct timezone for the input datetime value.
The function (as corrected below) allows for any integer adjustment of days and now produces consistently correct results:
#include <stdio.h>
#include <string.h>
#include <time.h>
/*******************************************************************************
* \fn adjust_date()
*******************************************************************************/
int adjust_date(
char *original_date,
char *adjusted_date,
char *pattern_in,
char *pattern_out,
int adjustment,
size_t out_size)
{
/*
struct tm {
int tm_sec; // seconds 0-59
int tm_min; // minutes 0-59
int tm_hour; // hours 0-23
int tm_mday; // day of the month 1-31
int tm_mon; // month 0-11
int tm_year; // year minus 1900
int tm_wday; // day of the week 0-6
int tm_yday; // day in the year 0-365
int tm_isdst; // daylight saving time
};
*/
struct tm day;
time_t one_day = 86400;
// time_t interval = (one_day * adjustment) - 3600;
time_t interval = (one_day * adjustment);
strptime(original_date, pattern_in, &day);
day.tm_isdst = -1;
time_t t1 = mktime(&day);
if (t1 == -1) {
printf("The mktime() function failed");
return -1;
}
time_t t2 = t1 + interval;
struct tm *ptm = localtime(&t2);
if (ptm == NULL) {
printf("The localtime() function failed");
return -1;
}
strftime(adjusted_date, out_size, pattern_out, ptm);
return 0;
}
/*******************************************************************************
* \fn main()
*******************************************************************************/
int main()
{
char in_date[64] = "20190321000000" ,
out_date[64],
pattern_in[64] = "%Y%m%d%H%M%S",
pattern_out[64] = "%Y-%m-%d %H:%M:%S";
int day_diff = -1,
ret = 0;
size_t out_size = 64;
memset(out_date, 0, sizeof(out_date));
ret = adjust_date(in_date, out_date, pattern_in, pattern_out, day_diff, out_size);
if (ret == 0)
{
printf("Adjusted date: '%s'\n", out_date);
}
return ret;
}
Hopefully, this will be of some help to somebody. Your constructive comments are greatly appreciated.
handling date time is very error prone and usually badly tested. i always recommend using boost::date_time http://www.boost.org/doc/libs/1_52_0/doc/html/date_time.html
here are nice examples http://en.highscore.de/cpp/boost/datetime.html
Did you try this ? :
time_t rawtime;
struct tm * today;
time ( &rawtime );
today= localtime ( &rawtime );
puts(asctime (today));

Converting epoch time to "real" date/time

What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)
So far, I have the following algorithm, which to me feels a bit ugly:
void DateTime::splitTicks(time_t time) {
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time % 24;
time /= 24;
year = DateTime::reduceDaysToYear(time);
month = DateTime::reduceDaysToMonths(time,year);
day = int(time);
}
int DateTime::reduceDaysToYear(time_t &days) {
int year;
for (year=1970;days>daysInYear(year);year++) {
days -= daysInYear(year);
}
return year;
}
int DateTime::reduceDaysToMonths(time_t &days,int year) {
int month;
for (month=0;days>daysInMonth(month,year);month++)
days -= daysInMonth(month,year);
return month;
}
you can assume that the members seconds, minutes, hours, month, day, and year all exist.
Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.
Be careful about leap years in your daysInMonth function.
If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.
A good solution is the one in the gmtime source code:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.
see: http://www.cplusplus.com/reference/clibrary/ctime/
Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.
An easy way (though different than the format you wanted):
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
Output:
Wed Sep 21 10:27:52 2011
Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:
std::string::size_type i = res.find("\n");
if (i != std::string::npos)
res.erase(i, res.length());
Taken from: http://en.cppreference.com/w/cpp/chrono/c/time
time_t t = unixTime;
cout << ctime(&t) << endl;
This code might help you.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
To convert a epoch string to UTC
string epoch_to_utc(string epoch) {
long temp = stol(epoch);
const time_t old = (time_t)temp;
struct tm *oldt = gmtime(&old);
return asctime(oldt);
}
and then it can be called as
string temp = "245446047";
cout << epoch_to_utc(temp);
outputs:
Tue Oct 11 19:27:27 1977
If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.
All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.
In short, use gmtime (and time.h functionality in general).