I'm working on a client-server custom application (to be run on linux), and one of the frames I send include a timestamp (ie the time at which the frame is send).
In order to make my application reliable, I used gmtime to produce the time on the client. I'm in Belgium, so right now the hour of the client VM is 2 hours later than the UTC time (because of the summer hour).
In the server side, I first convert the recieved string to a time_t type. I do this to use the difftime function to see if the timestamp is not too old.
Then, I generate a timestamp (in UTC time) again with gmtime, and I convert it to a time_t.
I compare then the two time_t to see the time difference.
I have got a problem with the conversion of the time at the server side. I use the same code as in the client, but the outputted gmtime is different...
Client Side : function to generate the timestamp, and export it to a string (time_str):
std::string getTime()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time (&rawtime); // Get time of the system
timeinfo = gmtime(&rawtime); // Convert it to UTC time
strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
std::string time_str(buffer); // Cast it into a string
cout<<"Time Stamp now (client) : "<<time_str<<endl;
return time_str;
}
And it produices this (at 9h33 local time) :
Time Stamp now : 06-04-2016 07:33:30
Server Side : fucntion to retrieve the timestamp, generate the newx time stamp, and compare them :
bool checkTimeStamp(std::string TimsStamp_str, double delay)
{
cout<<"TimeStamp recieved: "<<TimsStamp_str<<endl;
/* Construct tm from string */
struct tm TimeStampRecu;
strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
time_t t_old = mktime(&TimeStampRecu);
/* Generate New TimeStamp */
time_t rawtime;
struct tm * timeinfo;
time (&rawtime); // Get time of the system
timeinfo = gmtime(&rawtime); // convert it to UTC time_t
time_t t2 = mktime(timeinfo); // Re-Cast it to timt_t struct
/* Convert it into string (for output) */
char buffer[80];
strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
std::string time_str(buffer); // Cast it into a string
cout<<"Time Stamp now (server) : "<<time_str<<endl;
/* Comparison */
double diffSecs = difftime(t2, t_old);
cout<<diffSecs<<endl;
bool isTimeStampOK;
if (diffSecs < delay)
isTimeStampOK = true;
else
isTimeStampOK = false;
return isTimeStampOK;
}
And it produces this (at 9h33 in Belgium) :
TimeStamp recieved : 06-04-2016 07:33:30
Time Stamp now (server) : 06-04-2016 08:33:31
Why is the Server Time (8h33) neither in localtime (9h33), neither in UTC time (7h33) ?
Have I made a mistake in its generation ? I don't understand where, because these are the exact same code as in the client side...
There's a couple of errors in your code, some your fault, some not. The biggest problem here is that the C <time.h> API is so poor, confusing, incomplete and error prone that errors like this are very nearly mandatory. More on that later.
The first problem is this line:
struct tm TimeStampRecu;
It creates an uninitialized tm and then passes that into strptime. strptime may not fill in all the fields of TimeStampRecu. You should zero-initialize TimeStampRecu like this:
struct tm TimeStampRecu{};
Next problem:
strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
The 12-hour time denoted by %I is ambiguous without an AM/PM specifier. I suspect this is just a type-o as it is the only place you use it.
Next problem:
gmtime time_t -> tm UTC to UTC
mktime tm -> time_t local to UTC
That is, mtkime interprets the input tm according to the local time of the computer it is running on. What you need instead is:
timegm tm -> time_t UTC to UTC
Unfortunately timegm isn't standard C (or C++). Fortunately it probably exists anyway on your system.
With these changes, I think your code will run as expected.
If you are using C++11, there is a safer date/time library to do this here (free/open-source):
https://github.com/HowardHinnant/date
Here is your code translated to use this higher-level library:
std::string getTime()
{
using namespace std::chrono;
auto rawtime = time_point_cast<seconds>(system_clock::now());
auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
std::cout<<"Time Stamp now (client) : "<<time_str<< '\n';
return time_str;
}
bool checkTimeStamp(std::string TimsStamp_str, std::chrono::seconds delay)
{
using namespace std::chrono;
std::cout<<"TimeStamp recieved: "<<TimsStamp_str<< '\n';
/* Construct tm from string */
std::istringstream in{TimsStamp_str};
time_point<system_clock, seconds> t_old;
date::parse(in, "%d-%m-%Y %H:%M:%S", t_old);
/* Generate New TimeStamp */
auto rawtime = time_point_cast<seconds>(system_clock::now());
auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
in.str(time_str);
time_point<system_clock, seconds> t2;
date::parse(in, "%d-%m-%Y %H:%M:%S", t2);
cout<<"Time Stamp now (server) : "<<time_str<<endl;
/* Comparison */
auto diffSecs = t2 - t_old;
cout<<diffSecs.count()<<endl;
bool isTimeStampOK;
if (diffSecs < delay)
isTimeStampOK = true;
else
isTimeStampOK = false;
return isTimeStampOK;
}
A timestamp is an absolute value. It doesn't depend on timezones or DST. It represents the number of seconds since a fixed moment in the past. The value returned by time() is the same, no matter what the timezone of the server is.
Your code doesn't produce timestamps but dates formatted for human consumption.
I recommend you use timestamps internally and format them to dates readable by humans only in the UI. However, if you need to pass dates as strings between various components of your application, also put the timezone in them.
I would write your code like this (using only timestamps):
// The client side
time_t getTime()
{
return time(NULL);
}
// The server side
bool checkTimeStamp(time_t clientTime, double delay)
{
time_t now = time(NULL);
return difftime(now, clientTime) < delay;
}
Update
If you have to use strings to communicate between client and server then all you have to do is to update the format string used to format the timestamps as dates (on the client) and parse the dates back to timestamps (on the server), to include the timezone you used to format the date.
This means add %Z into the format everywhere in your code. Use "%d-%m-%Y %H:%M:%S %Z". Btw, the code you posted now reads "%d-%m-%Y %I:%M:%S" in the call to strptime() and that will bring you another problem in the afternoon.
Remark
I always use "%Y-%m-%d %H:%M:%S %Z" because it is unambiguous and locale-independent. 06-04-2016 can be interpreted as April 6 or June 4, depending of the native language of the reader.
Related
I have attempted to find an answer to this question but the sources I have found do not properly convert the time. Originally I have the timestamp in a string as I am getting the timestamp from a .json web scraping program but want to convert it to a readable date/time. My latest attempt to do the conversion was to convert the string into a long and the long to time_t and then use strftime() but that does not produce the correct answer.
Here is the code where I converted the string to a long and then to time_t and then used strftime()
std::string timeStampToRead(const time_t rawtime)
{
struct tm *dt;
char buffer [30];
dt = localtime(&rawtime);
strftime(buffer, sizeof(buffer), "%m%d %H%M", dt);
return std::string(buffer);
}
int main()
{
std::string epochT = "1604563859000";
long e = atol(epochT.c_str());
const time_t rawtime = (const time_t)e;
std::string time = timeStamptToRead(rawtime);
return 0;
}
The result of this code is 0808 1703
I also attempted to do something very similar to above but changed timeStampToRead to the following
std::string timeStampToRead(const time_t rawtime)
{
struct tm *dt;
char buffer [30];
dt = localtime(&rawtime);
return asctime(dt);
}
This returns Mon Aug 8 17:03:20 which is also incorrect
The correct answer should be Thursday, November 5, 2020 8:10:59 UTC or Thursday, November 5, 2020 1:10:59 locally (not necessarily in that exact format just the correct month, day, and time is important)
I have tested the outcome of converting the string to a long and that is working correctly so I am thinking the error is either in converting the long to time_t or the process of using the time_t to get a readable format of the time. Of the two options, I think it is more likely the conversion from long to time_t is the issue but can't find another way to do the conversion.
I was hoping there was an easy way to directly take the string and convert it but I cannot find any information on that anywhere so that is why I resulted to converting the string to long and then to time_t. Basically, I can't figure out how to convert a string or long unix epoch timestamp to time_t to convert it to a readable format, or at least that is where I am assuming the error is. If anyone could point me in the right direction to get this conversion code working I would greatly appreciate it.
After some further digging, I have found my error and determined the best route
std::string epochT = "1604563859000";
long e atol(epochT.c_str());
const time_t rawtime = e/1000;
struct tm * dt;
dt = localtime(&rawtime);
std::string readable = asctime(dt);
This code will be inside a for loop that iterates through vectors of strings so epochT will be dependent on the elements in the vector and the current element, but this works and outputs the correct answer of Thu Nov 5 01:10:59 2020 local time.
please consider following code, it compiles and run on ESP32 board:
unsetenv("TZ");
String payload = http.getString();
payload.replace("\"", "");
Serial.print("Payload: ");
Serial.println(payload);
const char* format = "%Y-%m-%dT%H:%M:%S";
strptime(payload.c_str(), format,& _time);
//debug only
Serial.print("Chamber time(UTC): ");
char chDate[11] = "";
char chTime[9] = "";
strftime(chDate, 11, "%m/%d/%Y", &_time);
strftime(chTime, 9, "%H:%M:%S", &_time);
Serial.print(chDate);
Serial.print(" ");
Serial.println(chTime);
int epoch_time = mktime(&_time);
timeval epoch = { epoch_time, 0 };
const timeval* tv = &epoch;
settimeofday(tv, NULL);
int rcode = setenv("TZ", "EST+5", 1);
tzset();
Serial.print("SetEnv reply");
Serial.println(rcode);
//VERIFICA
struct tm now;
getLocalTime(&now, 0);
Serial.println(&now, " %B %d %Y %H:%M:%S (%A)");
producing the following output:
Payload: 2020-04-08T21:59:10.736+0000
Chamber time(UTC): 04/08/2020 21:59:10
SetEnv reply0
April 08 2020 21:59:10 (Wednesday)
I expected the last date to be a local time according to "EST+5" timezone, in this example. Infact, I followed this readme, as I am using a ESP32 board, that says:
To set local timezone, use setenv and tzset POSIX functions. First,
call setenv to set TZ environment variable to the correct value
depending on device location. Format of the time string is described
in libc documentation. Next, call tzset to update C library runtime
data for the new time zone. Once these steps are done, localtime
function will return correct local time, taking time zone offset and
daylight saving time into account
What am I missing/doing wrong, apart from my rusty C++? Perfect solution would be to use : format like ":Europe/Rome" Thanks
The TZ string "EST+5" might be unknown to the OS, supported by the fact your output showed UTC time. EST and EDT have been used for US Eastern or America/New York. Assuming this is a Linux-like OS, take a look in the /usr/share/zoneinfo/ path for the zones available, or try the tzselect command to see if you can find the correct TZ string.
Try this sequence of C functions:
time_t tnow;
time(&tnow);
struct tm when;
errno_t ret = localtime_s(&when, &tnow);
// struct tm *when = localtime(&tnow); // deprecated for some compilers
The getLocalTime() function is not standard C and may not honor tzset().
I've got an 8601 format time string like so:
std::string strTime("1601-01-01T00:01:53.537Z");
I want to increment the hour and change its value to
"1601-01-01T01:01:53.537Z"
I think the steps are: convert string to a time object, increment the hour by 1, convert the object back to a string.
Of course, it would be helpful if all the normal time considerations and boundaries were taken into account (such as adding an hour to 11:30 pm will move to the next day, etc...). I've been looking at strftime, strptime, std::get_time and others but have not been able to work it out yet.
I'm using VS2012 on Windows. Thanks.
Some pseudo code to get you started.
char *AddHour(char *dest, size_t size, const char *timestamp) {
Initialize struct tm
Scan timestamp into tm
Check scanning success
Check year range validity
Bring into range acceptable by mktime()
Add hour
Call mktime()
Undo range adjustment made beforehand
Print tm to dest
return dest;
}
Even though mktime() uses local timezone, by keeping the tm_isdst == 0 we can use this for the Z timezone (UTC) for all practical purposes.
Simply scan the string into various parts. Add 1 hour and then reform the string
string --> struct tm tm;
// Add hour
if (++tm.tm_hour >= 24) {
tm.tm_hour = 0;
if (++tm.tm_mday > EOM(tm.tm_year, tm.tm_mon)) {
tm.tm_mday = 1;
if (++tm.tm_mon > 12) {
tm.tm_mon = 1;
tm.tm_year++;
}
}
}
struct tm tm --> string
I have written a C library that parses and formats a ISO 8601 calendar date with time and zone designator in extended format. It's available on Github, c-timestamp.
{
timestamp_t ts;
char *str = "1601-01-01T00:01:53.537Z";
timestamp_parse(str, strlen(str), &ts);
ts.sec += 1*60*60;
timestamp_format(str, strlen(str), &ts);
puts(str);
}
I appended date to the mongodb like this
bson_append_date(b,"uploadDate",(bson_date_t)1000*time(NULL));
Do remember that this will append "milliseconds since epoch UTC" and saved as 2014-06-27 06:11:56
Now i am reading it out and it is giving milliseconds (1403852029) which is exactly right. Now i want to convert it into local time. I tried to use the localtime function of C++ but did get success as the time returned by mongodb is in int64_t.
if(bson_iterator_type(&it)==BSON_DATE)
bson_date_t date_it = bson_iterator_date( &it );
where bson_date_t is typedef int64_t bson_date_t;. Can anyone tell me how i can get the local time from the milliseconds.
Getting a valid time_t that would work with localtime should be exactly the opposite of what you are doing in the forward conversion:
bson_append_date(b,"uploadDate",(bson_date_t)1000*time(NULL));
To have a workable time_t, you should do following:
time_t rawTime = (time_t)(bson_iterator_date( &it ) / 1000);
struct tm * timeinfo = localtime (&rawTime);
One more method.
bson_date_t date_it = bson_iterator_date( &it );
struct tm* ts;
time_t epoch_time_as_time_t= date_it/1000;
ts=localtime(&epoch_time_as_time_t);
strftime(upload_Date,sizeof(upload_date),"%a %Y-%m-%d %H:%M:%S %Z",ts);
My question is pretty much in the title. I have a function call with a parameter of type time_t, and I need to initialize a variable to today's date, month, and year, and send it via the argument. For example,
void WebCall(time_t TodaysDate)
Where TodaysDate is the populated variable with the format DD/MM/YYYY with the slashes included. Is this possible? I can't change the data type from time_t to SYSTEMTIME or anything else. This is coded in C++. Any Ideas?
If you mean time_t, you can format it using gmtime and strftime:
time_t TodaysDate= ...;
struct tm * ptm= gmtime(&time);
char buffer[80];
strftime(buffer, 80, "%d/%m/%Y", ptm);
time_t is "unix time" and is the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC. As MSN answered you can convert that to a date using gmtime, for most common purposes, UTC is synonymous with GMT. You didn't specify in the question but if you need the local date use localtime instead of gmtime. Here's a function that'll do that for you and return a std::string:
#include <time.h>
#include <string>
std::string time_to_local_date( time_t utc )
{
struct tm *now = localtime(&utc);
char buffer[80];
strftime(buffer, 80, "%d/%m/%Y", now);
return std::string(buffer);
}