I have two date and time strings separately in variables. I need to calculate the difference between these 2 date and time values in milliseconds. How to get that in C. The solution should work across platforms(at least windows and unix).
char date1[] = {"26/11/2015"};
char time1[] = {"20:22:19"};
char date2[] = {"26/11/2015"};
char time2[] = {"20:23:19"};
First I need to save this into some time structure and then compare 2 time structures to get the difference. What is the time structure which is available in C Library to do this.
Use mktime() and difftime()
The mktime function returns the specified calendar time encoded as a value of type time_t. If the calendar time cannot be represented, the function returns the value (time_t)(-1). C11dr §7.27.2.3 4
The difftime function returns the difference expressed in seconds as a double §7.27.2.2 2
#include <time.h>
#include <stdlib.h>
#include <string.h>
time_t parse_dt(const char *mdy, const char *hms) {
struct tm tm;
memset(&tm, 0, sizeof tm);
if (3 != sscanf(mdy, "%d/%d/%d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year)) return -1;
tm.tm_year -= 1900;
tm.tm_mday++;
if (3 != sscanf(hms, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec)) return -1;
tm.tm_isdst = -1; // Assume local time
return mktime(&tm);
}
int main() {
// application
char date1[] = { "26/11/2015" };
char time1[] = { "20:22:19" };
char date2[] = { "26/11/2015" };
char time2[] = { "20:23:19" };
time_t t1 = parse_dt(date1, time1);
time_t t2 = parse_dt(date2, time2);
if (t1 == -1 || t2 == -1) return 1;
printf("Time difference %.3f\n", difftime(t2, t1) * 1000.0);
return 0;
}
Output
Time difference 60000.000
from the man page for mktime()
this is the prototype for mktime()
time_t mktime(struct tm *tm);
this is the description of the function mktime()
The mktime() function takes an argument representing
broken-down time which is a representation separated into year, month,
day, and so on.
Broken-down time is stored in the structure tm which is defined in
<time.h> as follows:
struct tm {
int tm_sec; /* seconds */
int tm_min; /* minutes */
int tm_hour; /* hours */
int tm_mday; /* day of the month */
int tm_mon; /* month */
int tm_year; /* year */
int tm_wday; /* day of the week */
int tm_yday; /* day in the year */
int tm_isdst; /* daylight saving time */
};
The members of the tm structure are:
tm_sec The number of seconds after the minute, normally in the range
0 to 59, but can be up to 60 to allow for leap seconds.
tm_min The number of minutes after the hour, in the range 0 to 59.
tm_hour The number of hours past midnight, in the range 0 to 23.
tm_mday The day of the month, in the range 1 to 31.
tm_mon The number of months since January, in the range 0 to 11.
tm_year The number of years since 1900.
tm_wday The number of days since Sunday, in the range 0 to 6.
tm_yday The number of days since January 1, in the range 0 to 365.
tm_isdst A flag that indicates whether daylight saving time is in
effect at the time described. The value is positive if day‐
light saving time is in effect, zero if it is not, and nega‐
tive if the information is not available.
The mktime() function converts a broken-down time structure, expressed
as local time, to calendar time representation. The function ignores
the values supplied by the caller in the tm_wday and tm_yday fields.
The value specified in the tm_isdst field informs mktime() whether or
not daylight saving time (DST) is in effect for the time supplied in
the tm structure: a positive value means DST is in effect; zero means
that DST is not in effect; and a negative value means that mktime()
should (use timezone information and system databases to) attempt to
determine whether DST is in effect at the specified time.
The mktime() function modifies the fields of the tm structure as fol‐
lows: tm_wday and tm_yday are set to values determined from the con‐
tents of the other fields; if structure members are outside their valid
interval, they will be normalized (so that, for example, 40 October is
changed into 9 November); tm_isdst is set (regardless of its initial
value) to a positive value or to 0, respectively, to indicate whether
DST is or is not in effect at the specified time. Calling mktime()
also sets the external variable tzname with information about the cur‐
rent timezone.
If the specified broken-down time cannot be represented as calendar
time (seconds since the Epoch), mktime() returns (time_t) -1 and does
not alter the members of the broken-down time structure.
==========================================
this is from the man page for difftime()
this is the prototype:
double difftime(time_t time1, time_t time0);
This is the description:
The difftime() function returns the number of seconds elapsed between
time time1 and time time0, represented as a double. Each of the times
is specified in calendar time, which means its value is a measurement
(in seconds) relative to the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
to get the result in seconds. To get the results in milliseconds, multiply by 1000.0
Related
I use mktime64 to convert a clock time into a jiffies value.
// year, mon, day, hour, min, sec
unsigned long my_jiffies = mktime64(2020, 2, 24, 3, 2, 50);
The output from the above code is: 1582513370
How can I convert that jiffies value back to clock time?
[This answer is for the Linux kernel since linux-kernel was tagged in the question.]
mktime64 is nothing to do with jiffies. It converts the date specified by its parameters to the number of seconds (ignoring leap seconds) since 1970-01-01 00:00:00 (Unix time since the epoch if the parameters are for GMT).
The returned time64_t value can be converted back to year, month, day, hours, minutes, seconds using the time64_to_tm function in the kernel. It has this prototype:
void time64_to_tm(time64_t totalsecs, int offset, struct tm *result);
The offset parameter is a local timezone offset in seconds (number of seconds east of GMT). It should be set to 0 to undo the conversion done by mktime64.
Note that the tm_year member is set to the calculated year minus 1900 and the tm_mon member is set to the calculated month minus 1, so you could implement an unmktime64 function as follows:
void unmktime64(time64_t totalsecs,
int *year, unsigned int *month, unsigned int *day,
unsigned int *hour, unsigned int *minute, unsigned int *second)
{
struct tm tm;
time64_to_tm(totalsecs, 0, &tm);
*year = tm.tm_year + 1900;
*month = tm.tm_mon + 1;
*day = tm.tm_mday;
*hour = tm.tm_hour;
*minute = tm.tm_min;
*second = tm.tm_sec;
}
I am trying to code a function which populates a struct tm from year, month, day, hour and minutes values.
The application deals with no time zone information, i.e., we assume that the input data time zone matches with the application and user time zone.
I have tried this:
void timeCreate(struct tm* pTm1, int year, int month, int day, int hour, int minute) {
pTm1->tm_year = year - 1900;
pTm1->tm_mon = month - 1;
pTm1->tm_mday = day;
pTm1->tm_hour = hour;
pTm1->tm_min = minute;
pTm1->tm_sec = 0;
pTm1->tm_isdst = -1;
mktime(pTm1);
}
If I don't do anything else, I get the CET time set by mktime (CET is my local timezone) and it seems that mktime changes the time and date, so it is no longer 2014/01/01 00:00 but the day before.
If I do this:
pTm1->tm_gmtoff = 0;
pTm1->tm_zone = "UTC";
Then I do not get any correction.
I have also seen the function gmtime() instead of mktime(). Which would be the "right" way of setting up this struct?
Also I get a random value for tm_wday, can that value be calculated automatically?
mktime only works with local time, and its behavior is constrained by the specification so that it cannot examine any nonstandard fields of struct tm like tm_gmtoff. And gmtime is not an analog of mktime but of localtime - it converts in the opposite direction. If you want a normalization/conversion of broken-down UTC time in struct tm format to time_t, you need to either use the nonstandard but widely available timegm function, or write it out yourself. Thankfully POSIX exactly specifies the formula:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
Note that if copied as written to C code, this is full of integer overflows, so some type promotions or other fixes need to be introduced to make it valid.
If you need the normalization aspect timegm also performs, but need it done portably without depending on the nonportable timegm, you can just call gmtime_r after using the above formula to invert it and get a normalized result.
Do I have to populate tm_gmtoff and tm_zone?
Which would be the "right" way of setting up this struct?
For portable code, yes.
The C spec has
"The tm structure shall contain at least the following members, in any order." (C spec) --> struct tm has at least the following 9 members:
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
And may include others like tm_gmtoff, tm_zone, tm_msec, etc.
The mktime function converts the broken-down time, expressed as local time, in the structure pointed to by timeptr into a calendar time value with the same encoding as that of the values returned by the time function. The original values of the tm_wday and tm_yday components of the structure are ignored, and the original values of the other components are not restricted to the ranges indicated above. ...
... On successful completion, the values of the tm_wday and tm_yday components of the structure are set appropriately, and the other components are set to represent the specified calendar time, but with their values forced to the ranges indicated above; the final value of tm_mday is not set until tm_mon and tm_year are determined.
C17dr § 7.27.2.3 2
Since mktime() is only specified to ignore tm_wday and tm_yday, all other members may contribute to the result. There is no C spec that limits the result to 7 of the above required members.
Good programing practice would zero out all members not explicitly initialized/assigned before calling mktime().
void timeCreate(struct tm* pTm1, int year, int month, int day, int hour, int minute) {
memset(pTm1, 0, sizeof *pTm1); // Zero all members.
pTm1->tm_year = year - 1900;
pTm1->tm_mon = month - 1;
pTm1->tm_mday = day;
pTm1->tm_hour = hour;
pTm1->tm_min = minute;
pTm1->tm_sec = 0;
pTm1->tm_isdst = -1;
mktime(pTm1);
// recommend to return the result of mktime() so caller can ID invalid timestamps.
}
If you code is used on a sub-set of platforms, it may get away without such zeroing assignments.
timeStruct #0x13b123b0 tm
tm_hour 0 int
tm_isdst 0 int
tm_mday 1 int
tm_min 0 int
tm_mon 0 int
tm_sec 33 int
tm_wday 4 int
tm_yday 0 int
tm_year 70 int
This is my struct as represented by QTcreators memory watch.
with this as parameter to a call of mktime()
struct tm *timeStruct;
//[...]
time_t timeOrigin = mktime(timeStruct);
timeOrigin becomes -1.
There are questions out here asking for the same.
But they all were solved by the hint, that the tm_year field is not years since 1970 but years since 1900. I'm aware of this and also respecting it.
What confuses me further is:
man pages like: http://linux.die.net/man/3/mktime
explain that mktime doesn't change the structs members if it returns -1. In my case it did adjust in previous cases tm_wday and tm_yday, while returning -1.
I can't find any error regarding this by reading stderr, after mktime returning -1 aswell.
So whats going on here?
mktime returns -1 because the tm structure values, interpreted as a time and date expressed in local time, fall outside the valid range. As quoted from the man page: If the specified broken-down time cannot be represented as calendar time (seconds since the Epoch), mktime() returns (time_t)-1 and does not alter the members of the broken-down time structure.
You are probably located east of Greenwich, meaning that 1/1/1970 0:00:00 local time falls before the beginning of the Unix Epoch that started on 1/1/1970 0:00:00 UTC.
mktime expects a pointer of type struct tm as it's first argument.
Here is an example, how you can use it:
#include <stdio.h>
#include <time.h>
int main(void)
{
struct tm tm = *localtime(&(time_t){time(NULL)});
printf("Today is %s", asctime(&tm));
tm.tm_mon -= 100; // tm_mon is now outside its normal range
time_t t = mktime(&tm); // recalculate tm
printf("100 months ago was %s", asctime(&tm));
}
In my embedded Linux application I have a "tick count" counter that increases 1 each 10 nanoseconds counting from 1st January 00:00:00 of 2014.
I want to be able to, picking the current value of the "tick count" counter, print it as the current date-time (year, month, day, hour, minute, second and millisecond) of my system already considering things such as leap year, February having 28/29 days, etc. and this using pure C methods (from time.h etc.).
But I don't know how to do it... For now I have the equivalent value in seconds, so I know how many seconds since the start date-time have passed, but not how to go from that to the current date-time value with everything adjusted, only in Qt which is not available (and the internet wasn't much of a help in this case till what could I understood of the explanations in cplusplus.com and etc.)
Any help appreciated.
Use gmtime().
Simply divide the tick count to get the whole number of seconds and add an offset to change the epoch from Jan 1, 2014 to Jan 1, 1970.
void print_time(unsigned long long tick_count) {
static const unsigned long ticks_per_sec = 100000000L;
static const time_t epoch_delta = 16071L*24*60*60;
time_t seconds = tick_count/ticks_per_sec + epoch_delta;
unsigned long fraction = tick_count%ticks_per_sec;
struct tm tm = *gmtime(&seconds);
printf("%4d-%02d-%02d %02d:%02d:%02d.%03lu\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
fraction/10000);
}
[After Accept Edit]
OP comments "I tried to use the methods from time.h and I was unsuccessful as well as that it considers the count of time since 1970, which is not my situation"
Another approach is to use mktime(). This is less dependent on issues brought up by #DavidEisenstat. It relies on the tm_sec field being an int of sufficient width (like 32 bits) to cover years 2014 to 2082. mktime() takes out-of-range fields and adjusts them to the expected ranges. Should tm_sec be 16-bit, some simple adjustments could be had to tm_mday, tm_hour, tm_min, tm_sec instead.
void print_time2(unsigned long long tick_count) {
static const unsigned long ticks_per_sec = 100000000L;
unsigned long fraction = tick_count%ticks_per_sec;
unsigned long long secondsFromJan12014 = tick_count/ticks_per_sec;
struct tm tm = {0};
tm.tm_year = 2014 - 1900;
tm.tm_mday = 1;
tm.tm_sec = secondsFromJan12014;
if (mktime(&tm) == (time_t)(-1)) Handle_Failure();
printf("%4d-%02d-%02d %02d:%02d:%02d.%03lu\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
fraction/10000);
}
First, converting nanoseconds to seconds are trivial.
Second, adding the right number of seconds since the right epoch 1970-01-01 you will have to know how many seconds there were from the epoch UTC 2014-01-01 00:00:00. The date command at the Linux prompt will tell you this:
date -u -d "2014-01-01 00:00" +%s
1388534400
So you will simply have to do something like:
time_t current = 1388534400+my_10_nano_time_function()/100000000;
Once you have your correct time_t value you can use all functions like localtime, gmtime and strftime.
However, as time_t is in seconds you will have to calculates the milliseconds yourself,
something like:
(my_10_nano_time_function()%/100000000)/100000
time_t rawtime;
struct tm *mytm;
time_t result;
time(&rawtime);
mytm=localtime(&rawtime);
mytm->tm_mon=month-1;
mytm->tm_mday=day;
mytm->tm_year=year-1900;
mytm->tm_sec=0;
mytm->tm_min=0;
mytm->tm_hour=0;
result = mktime(mytm);
Above code snippet,I'm expecting result to display the no.of seconds lapsed since 1970,jan-1st for the given date. DD/MM/YYYY stored in day ,month,year
But i'm getting compile error
error: dereferencing pointer to incomplete type
You need
#include <time.h>
in your file to fix the error about incomplete type.
Edit: Given a day, month, year to find the time in seconds since Jan 1 1970 to midnight on that day:
struct tm mytm = { 0 };
time_t result;
mytm.tm_year = year - 1900;
mytm.tm_mon = month - 1;
mytm.tm_mday = day;
result = mktime(&mytm);
if (result == (time_t) -1) {
/* handle error */
} else {
printf("%lld\n", (long long) result);
}
Note the in ISO C, mktime() returns an integral value of type time_t that represents the time in the struct tm * argument, but the meaning of such an integral value is not necessarily "seconds since Jan 1, 1970". It need not be in seconds at all. POSIX mandates that time(), mktime(), etc., return seconds since Jan 1, 1970, so you should be OK. I mention the above for completeness.
The "time" function returns the number of seconds since Jan 1 1970 UTC. You do not need to call any other functions. The time_t type is just an integer type, it's probably equivalent to int.
Dietrich is correct, however if you wished to add the number of seconds since the Epoch in a formatted string with other date info, you should consider using strftime().