In my current project I have a C struct to hold a timestamp, which looks like this:
struct timestamp {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t second;
}
Now I want to calculate the difference between two of these timestamps in seconds. For now I'm converting my timestamp struct to the C standard struct struct tm (defined in <time.h>). Then I convert the struct to time_t with mtkime(), which takes a pointer to a struct tm and returns a time_t. And difftime() to calculate the difference between two time_t's in seconds.
I don't want to write my own difftime(), since I do not want to deal with leapyears or even worse leap seconds by myself, and I don't use the struct tm in my code because it holds a lot of values I don't frequently need (like week-day or year-day).
Here is an example, what I do at the moment:
void customTimestampToStructTM(struct customTimestamp *in, struct tm *out) {
out->tm_year = in->year;
out->tm_mon = in->mon;
out->tm_mday = in->day;
out->tm_hour = in->hour;
out->tm_min = in->min;
out->tm_sec = in->sec;
}
void someFunction() {
struct customTimestamp c1;
struct customTimestamp c2;
// Fill c1 and c2 with data here.
struct tm tmpC1;
struct tm tmpC2;
customTimestampToStructTM(&c1, &tmpC1);
customTimestampToStructTM(&c2, &tmpC2);
double diffInSeconds = difftime(mktime(tmpC1), mktime(tmpC2));
// Use diffInSeconds
}
This works, but seem incredibly inefficient. How can I speed this up? I read here that mktime doesn't use the other fields in the struct tm - except for isdst. Is there a convenient way to convert my struct to time_t, without using struct tm as a bridge and without the need to deal with leapyears/seconds?
To clarify: time_t holds dates in amount of milliseconds passed since a specific date (1 Jan 1970).
mktime [and localtime] are non-trivial for all edge cases. They're also highly optimized, so you're unlikely to do better speedwise.
So, just use them [doing the fast fill of (e.g.) struct tm temp you're already doing].
But, a speedup is to add time_t tod to your struct. Fill it from mktime once when you create your struct. This can save many repetitive/duplicate calls to mktime.
You can even defer the mktime call (i.e. only some of your structs may need it). Set tod to a sentinel value (e.g. -2). When you actually need to use tod, fill it from mktime if tod is the sentinel value
I shall assume the timestamps always refer to UTC time, in which case Daylight Savings Time does not apply (and you'll want to specify tm.isdst = 0).
(I suspect that it would be optimal in this case to have the time_t in UTC, but the broken-down fields in local time. Below, I'll just assume that the local timezone is UTC, with no DST changes.)
Personally, I'd save both the time_t and the split fields, and use helper functions to set/modify the timestamps.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
struct timestamp {
time_t packed;
int16_t year;
int8_t month;
int8_t day;
int8_t hour;
int8_t min;
int8_t sec;
};
static inline int set_timestamp_time(struct timestamp *const ref, const time_t when)
{
struct tm temp = { 0 };
if (!gmtime_r(&when, &temp))
return ERANGE; /* Year didn't fit. */
ref->packed = when;
ref->year = temp.tm_year + 1900;
ref->month = temp.tm_mon + 1;
ref->day = temp.tm_mday;
ref->hour = temp.tm_hour;
ref->min = temp.tm_min;
ref->sec = temp.tm_sec;
return 0;
}
static inline int set_timestamp(struct timestamp *const ref,
const int year, const int month, const int day,
const int hour, const int min, const int sec)
{
struct tm temp = { 0 };
temp.tm_year = year - 1900;
temp.tm_mon = month - 1;
temp.tm_mday = day;
temp.tm_hour = hour;
temp.tm_min = min;
temp.tm_sec = sec;
/* We assume timestamps are in UTC, and Daylight Savings Time does not apply. */
temp.tm_isdst = 0;
ref->packed = mktime(&temp);
ref->year = temp.tm_year + 1900;
ref->month = temp.tm_mon + 1;
ref->day = temp.tm_mday;
ref->hour = temp.tm_hour;
ref->min = temp.tm_min;
ref->sec = temp.tm_sec;
return 0;
}
set_timestamp() sets the timestamp based on split fields (year, month, day, hour, minute, second), whereas set_timestamp_time() sets it based on POSIX time. Both functions always update all timestamp fields.
This allows fast access to both the Unix time, and the split fields, but uses slightly more memory (8 bytes per timestamp, typically; 160 megabytes additional memory for 20 million records).
If you do not need the exact number of seconds between two timestamps, but only use the time_t to compare whether one is before or after another, then I recommend using a single int64_t to describe your timestamps:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
/* Timestamps can be compared as integers, like POSIX time_t's.
* The difference between two timestamps is at least their
* difference in seconds, but may be much larger.
*
* Zero is not a valid timestamp!
*/
typedef int64_t timestamp;
#define TIMESTAMP_YEAR(t) ((int64_t)(t) / 67108864)
#define TIMESTAMP_MONTH(t) (((uint32_t)(t) >> 22) & 15)
#define TIMESTAMP_DAY(t) (((uint32_t)(t) >> 17) & 31)
#define TIMESTAMP_HOUR(t) (((uint32_t)(t) >> 12) & 31)
#define TIMESTAMP_MIN(t) (((uint32_t)(t) >> 6) & 63)
#define TIMESTAMP_SEC(t) ((uint32_t)(t) & 63)
static inline time_t timestamp_time(const timestamp t, struct tm *const tm_to)
{
struct tm temp = { 0 };
time_t result;
uint32_t u = t & 67108863U;
temp.tm_sec = u & 63; u >>= 6;
temp.tm_min = u & 63; u >>= 6;
temp.tm_hour = u & 31; u >>= 5;
temp.tm_mday = u & 31; u >>= 5;
temp.tm_mon = u - 1;
temp.tm_year = ((int64_t)t / 67108864) - 1900;
/* UTC time, thus Daylight Savings Time does not apply. */
temp.tm_isdst = 0;
result = mktime(&temp);
if (tm_to)
*tm_to = temp;
return result;
}
static inline double difftimestamp(const timestamp t1, const timestamp t2)
{
return difftime(timestamp_time(t1, NULL), timestamp_time(t2, NULL));
}
static inline timestamp set_timestamp_time(const time_t when, struct tm *const tm_to)
{
struct tm temp = { 0 };
if (!gmtime_r(&when, &temp))
return 0;
if (tm_to)
*tm_to = temp;
return (int64_t)67108864 * ((int64_t)temp.tm_year + 1900)
+ (int64_t)((temp.tm_mon + 1) << 22)
+ (int64_t)(temp.tm_mday << 17)
+ (int64_t)(temp.tm_hour << 12)
+ (int64_t)(temp.tm_min << 6)
+ (int64_t)temp.tm_sec;
}
static inline timestamp set_timestamp(const int year, const int month, const int day,
const int hour, const int min, const int sec,
struct tm *const tm_to, time_t *const time_to)
{
struct tm temp = { 0 };
temp.tm_year = year - 1900;
temp.tm_mon = month - 1;
temp.tm_mday = day;
temp.tm_hour = hour;
temp.tm_min = min;
temp.tm_sec = sec;
temp.tm_isdst = 0; /* Since timestamps are in UTC, Daylight Savings Time does not apply. */
if (time_to)
*time_to = mktime(&temp);
if (tm_to)
*tm_to = temp;
return (int64_t)67108864 * ((int64_t)temp.tm_year + 1900)
+ (int64_t)((temp.tm_mon + 1) << 22)
+ (int64_t)(temp.tm_mday << 17)
+ (int64_t)(temp.tm_hour << 12)
+ (int64_t)(temp.tm_min << 6)
+ (int64_t)temp.tm_sec;
}
The idea here is that you can compare two timestamps trivially; a < b if and only if timestamp a is before b; a == b if and only if the timestamps refer to the same second, and a > b if and only if a is after b. At the same time, the accessor macros TIMESTAMP_YEAR(a), TIMESTAMP_MONTH(a), TIMESTAMP_DAY(a), TIMESTAMP_HOUR(a), TIMESTAMP_MIN(a), and TIMESTAMP_SEC(a) allow very fast access to the individual date and time components. (On typical Intel/AMD 64-bit hardware, it may be even faster than accessing byte-sized fields.)
The difftimestamp() function yields the exact number of seconds between two timestamps, but it is quite slow. (As I mentioned, this approach is best only if you don't need this, or only need it rarely.)
timestamp_time() converts a timestamp to a time_t, optionally saving the struct tm fields to a specified pointer (if not NULL).
set_timestamp_time() returns a timestamp based on a time_t. If the year does not fit in an int, it will return 0 (which is NOT a valid timestamp). If the second parameter is not NULL, the corresponding struct tm is stored there.
set_timestamp() returns a timestamp based on year, month, day, hour, minute, and second. If they refer to an impossible date or time, they are corrected (by mktime()). If the seventh parameter is not NULL, the resulting struct tm is stored there. If the eighth parameter is not NULL, then the resulting time_t is stored there.
Related
I am writting a wrapper function sleep_new() for clock_nanosleep() which would make thread suspension easier for me.
// POSIX.1-2017 is what compiler is confined to.
#define _XOPEN_SOURCE 700
#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
// POSIX headers.
// Other headers
#include "sleep_new.h"
void sleep_new(long value, const char unit[3]){
// Create a timespec structure and set it's members.
// Members are added together!!! So to set time "1.5 s" we set "t.tv_sec = 1" and "t.tv_sec = 500000000".
// Members ".tv_sec" and ".tv_nsec" represent unit and not value!
struct timespec sleep_time;
// Set flags i.e. TIMER_ABSTIME to 0 to use relative instead of absolute time.
int flags = 0;
// Choose the clock i.e. CLOCK_MONOTONIC is a "clock_id" for the clock started at system start.
int clock_id = CLOCK_MONOTONIC;
// Set timespec structure's members according to the chosen unit.
if (!strcmp(unit, "s")) {
sleep_time.tv_sec = value;
sleep_time.tv_nsec = 0;
}
else if (!strcmp(unit, "ns")){
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = value;
}
else if (!strcmp(unit, "us")){
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = value * 1000;
}
else if (!strcmp(unit, "ms")){
sleep_time.tv_sec = 0;
sleep_time.tv_nsec = value * 1000000;
}
else{
puts("Unit not supported - choose between: s, ms, us, ns\n");
}
// Because last argument is NULL in case of error, remaining time is not stored in "t".
clock_nanosleep(clock_id, flags, &sleep_time, NULL);
}
int main(int argc, char *argv[])
{
// Counter.
uint8_t i;
for(i = 0; i < 256; i++){
// Stdout is newline buffered. This is why we either have to include `\n` at the end or flush() it manually.
// So uncomment one example A or B.
// A
//printf("%d\n", i);
// B
printf("%d, ", i);
fflush(stdout);
// Because last argument is NULL in case of error, remaining time is not stored in "t".
sleep_new(1000, "ms");
}
return 0;
}
If I call this function with sleep_new(1, "s") or sleep_new(2, "s") it works fine, because it sets the sleep_time.tv_nsec = 0; and sleep_time.tv_sec = value;.
In any other scenarios i.e. sleep_new(1000, "ms") something is wrong and sleep is not applied. I debugged application and values are applied to the timespec members just fine but clock_nanosec() just ignores them.
I am using type long for the value because I read in the POSIX here where header time.h defines timespec structure's members tv_nsec who needs long and member tv_sec who uses time_t which is in turn defined in header sys/types.h like this:
time_t shall be an integer type.
So because long can also hold int values I expected this to work, but it doesn't. Does anyone have any suggestion?
The tv_nsec is the number of nanoseconds in a second - 1000 * 1000000 nanoseconds is too much. That's 1 second! tv_nsec should range from 0 to 999999999. The proper calculation could look like:
sleep_time.tv_sec = value / 1000;
sleep_time.tv_nsec = (value % 1000) * 1000000;
So finally after a lot of time googling I've been able to create a function that gets current date and convert it to string then write to buffer in C. But I noticed sometimes the day is off by 1? So for example today is may 4, but it may return may 5
Here is my code
void getCurrentDateTime(char * buffer)
{
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char bufForOut[64] = { 0 };
strftime(bufForOut, sizeof(bufForOut), "%c", tm);
strcpy(buffer,bufForOut);
}
I call it like
char hi[64] = { 0 }
getCurrentDateTime(hi);
print(hi);
I'm trying to use 2 different equivalents for UNIX's gettimeofday() function on Windows, using Visual Studio 2013.
I took the first one from here. As the second one, I'm using the _ftime64_s function, as explained here.
They work, but not as I expected. I want to get different values when printing the seconds, or at least the milliseconds, but I get the same value for the printings with gettimeofday() (mytime1 & mytime2) and with _ftime64_s (mytime3 & mytime4).
However, it worth mentioning that the value of the milliseconds is indeed different between these two functions (that is, the milliseconds value of mytime1/mytime2 is different from mytime3/mytime4).
Here's my code:
#include <stdio.h>
#include <Windows.h>
#include <stdint.h>
#include <sys/timeb.h>
#include <time.h>
#define WIN32_LEAN_AND_MEAN
int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
time = ((uint64_t)file_time.dwLowDateTime);
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long)((time - EPOCH) / 10000000L);
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
return 0;
}
int main()
{
/* working with struct timeval and gettimeofday equivalent */
struct timeval mytime1;
struct timeval mytime2;
gettimeofday(&(mytime1), NULL);
gettimeofday(&(mytime2), NULL);
printf("Seconds: %d\n", (int)(mytime1.tv_sec));
printf("Milliseconds: %d\n", (int)(mytime1.tv_usec));
printf("Seconds: %d\n", (int)(mytime2.tv_sec));
printf("Milliseconds: %d\n", (int)(mytime2.tv_usec));
/* working with _ftime64_s */
struct _timeb mytime3;
struct _timeb mytime4;
_ftime64_s(&mytime3);
_ftime64_s(&mytime4);
printf("Seconds: %d\n", mytime3.time);
printf("Milliseconds: %d\n", mytime3.millitm);
printf("Seconds: %d\n", mytime4.time);
printf("Milliseconds: %d\n", mytime4.millitm);
return (0);
}
I tried other format specifiers (%f, %lu) and castings ((float), (double), (long), (size_t)), but it didn't matter. Suggestions will be welcomed.
QueryPerformanceCounter is used for accurate timing on windows. Usage can be as follows:
uint64_t microseconds()
{
LARGE_INTEGER fq, t;
QueryPerformanceFrequency(&fq);
QueryPerformanceCounter(&t);
return (1000000 * t.QuadPart) / fq.QuadPart;
}
This does not work with any EPOCH as far as I know. For that you need GetSystemTimePreciseAsFileTime which is only available on Windows 8 and higher.
uint64_t MyGetSystemTimePreciseAsFileTime()
{
HMODULE lib = LoadLibraryW(L"kernel32.dll");
if (!lib) return 0;
FARPROC fp = GetProcAddress(lib, "GetSystemTimePreciseAsFileTime");
ULARGE_INTEGER largeInt;
largeInt.QuadPart = 0;
if (fp)
{
T_GetSystemTimePreciseAsFileTime* pfn = (T_GetSystemTimePreciseAsFileTime*)fp;
FILETIME fileTime = { 0 };
pfn(&fileTime);
largeInt.HighPart = fileTime.dwHighDateTime;
largeInt.LowPart = fileTime.dwLowDateTime;
}
FreeLibrary(lib);
return largeInt.QuadPart;
}
int main()
{
uint64_t t1 = microseconds();
uint64_t t2 = microseconds();
printf("t1: %llu\n", t1);
printf("t2: %llu\n", t2);
return (0);
}
I am using C on Unix. The program displays the time and I am trying to figure out how to offset the current time in minutes and hours.
This part of the code
while ( ( let = getopt(argc, argv, "alo:vh")) != -1 ) {
switch (let) {
case 'o': offset = atoi(optarg); break; }
and later in this part:
void clock(int sig, int time_expires)
{
time_t now;
struct tm *dateinfo;
(void) time(&now);
now = now + offset;
dateinfo = localtime( &now ); }
Makes an -o offset, which offsets the current time by a certain amount of seconds. For example, -o590 would offset the current time by 590 seconds.
I am trying to figure out how to do this same thing only with an -h flag that offsets the time by a certain amount of hours (like -h6 offsets the time by 6 hours) or by a -m flag which offsets the time by minutes.
I have tried dividing the current -o flag by 60 or 360 but that is not working. Can anyone point me in the right directions here?
To change time_t by so many hours, minutes, seconds in a portable fashion without relying on time_t is some integer type of seconds since 1970, use mktime()
time_t adjust(time_t t, int hour, int minute, int second) {
struct tm *dateinfo;
dateinfo = localtime(&t);
if (dateinfo == NULL) return (time_t) -1;
dateinfo->tm_hour += hour;
dateinfo->tm_min += minute;
dateinfo->tm_sec += second;
return mktime(dateinfo);
}
The time_t structure defines the number of seconds since Jan 1, 1970 UTC.
If you want to add n minutes you should do:
now+= n*60
And for n hours you should:
now+= n*3600
Alternatively you can use struct tm and access directly to the time quanta you wish to modify.
struct tm {
int tm_sec; /* seconds, range 0 to 59 */
int tm_min; /* minutes, range 0 to 59 */
int tm_hour; /* hours, range 0 to 23 */
int tm_mday; /* day of the month, range 1 to 31 */
int tm_mon; /* month, range 0 to 11 */
int tm_year; /* The number of years since 1900 */
int tm_wday; /* day of the week, range 0 to 6 */
int tm_yday; /* day in the year, range 0 to 365 */
int tm_isdst; /* daylight saving time */
};
I have a struct array (Training data[10]) that contains some data that I want to pass to functions.
int convertTime(Time time)
{
minutes = time.seconds * 60;
// Takes data from data[0].time.seconds and converts them to minutes.
// My only problem is that I am being asked send a struct to this function, but I have to send the array because that's where all my data is stored
return minutes;
}
typedef struct
{
int seconds;
} Time;
typedef struct
{
Time time;
double distance;
} Training;
Training data[10];
Training input;
scanf("%d %lf", input.time.seconds, input.distance);
data[0].time.seconds = input.time.seconds;
data[0].distance = input.distance;
So now data[0].time.seconds and data[0].distance contains all data I need. I just have to pass data[0].time.seconds to the function, but in my assignment I am prompted to send the struct Time to the function, and I don't understand that since Time is only storing temporary data? It's the stored data that I want to send to the function.
How do I convert seconds to hours, minutes and seconds?
time.hours = seconds / 3600;
time.minutes = (seconds - time.hours * 3600) / 60;
time.seconds = seconds - 3600 * time.hours - 60 * time.minutes;
This seems to be right in my eyes but it fails. hours is correctly calculated but not minutes and sconds :(
To pass the structure, name it in the call:
some_function(data[0].time); // By value
other_function(&data[0].time); // By address
Both functions get passed the Time value contained in the data[0] element of your array of Training structures.
Suppose you have a value which is the number of seconds since midnight. And suppose you define another structure with hours/minutes/seconds, you can set this clock structure as follows,
typedef struct
{
int hours;
int minutes;
int seconds;
} Clock;
You can print this structure, either to a char buffer, or to stdout,
char*
clockPrint(Clock* timep,char *stringbuf)
{
if(stringbuf)
sprintf(stringbuf,"%02d:%02d:%02d",(timep)->seconds,(timep)->minutes,(timep)->seconds);
else
printf("%02d:%02d:%02d",(timep)->seconds,(timep)->minutes,(timep)->seconds);
return stringbuf;
}
Extracting hours, minutes, and seconds from an epoch time or a number of seconds since midnight can be done,
int //return days...
TimeSet(Clock* timep, int epoch)
{
(timep)->seconds = (epoch) % 60;
(timep)->minutes = (epoch/60) % 60;
(timep)->hours = (epoch/60/60) % 24;
int days;
return days = (epoch/60/60/24);
}
Should you want to obtain hours, minutes, or seconds from this clock value,
void
TimeGet(Clock* timep, int* hoursp, int* minutesp, int* secondsp)
{
if(hoursp) *hoursp = (timep)->hours;
if(minutesp) *minutesp = (timep)->minutes;
if(secondsp) *secondsp = (timep)->seconds;
return;
}
Since you have stored a Time in your Date struct, which contains a number of seconds (presumably since midnight), and you have an array of some number of these Date's defined,
Training data[10];
Training input;
You can use scanf to read your seconds and distance values. And as stated, you can then place your input into your data[0] element,
//scanf wants pointers to int and float data
float distance;
printf("enter: seconds distance "); fflush(stdout);
scanf("%d %lf", &(input.time.seconds), &distance);
//you can then store the distance into your input struct double
input.distance = distance;
data[0].time.seconds = input.time.seconds;
data[0].distance = input.distance;
You could also use gettimeofday(3) or clock_gettime(2) to grab the current time (seconds since epoch),
struct timeval tv;
gettimeofday(&tv,NULL); //posix.1-2001 function, seconds
input.time.seconds = tv.tv_sec;
//or
struct timespec ts;
clock_gettime(CLOCK_REALTIME,&ts); //posix.1-2008 function, seconds
input.time.seconds = ts.tv_sec;
Then you can separate your seconds into hours, minutes, and seconds,
Clock clk;
int hours, minutes, seconds;
TimeSet(&clk, data[0].time.seconds);
TimeGet(&clk, &hours, &minutes, &seconds);
Or you can format a string for printing, or print to stdout,
char clockbuffer[30];
clockPrint(&clk,NULL);
printf("time (HH:MM:SS): %s\n", clockPrint(&clk,clockbuffer));