Why doesn't strptime() set tm_wday properly in this example? - c

I'm using strptime(3) to parse a string representing a date:
#include <time.h>
#include <stdio.h>
int main () {
struct tm t;
strptime("2015-04-19 12:00:00", "%F %T", &t); /* Sunday */
printf("%d\n", t.tm_wday); /* Should print 0 */
return 0;
}
That date is a Sunday, according to the output of cal -y 2015. But when I compile this on OSX (presumably with clang) it prints 6:
$ gcc timetest.c ; ./a.out
6
whereas on Debian it prints the (correct) 0:
$ gcc timetest.c ; ./a.out
0
Any explanation for the difference?
UPDATE
Here is the same program, except that t is initialised with a valid time and I'm reporting the return value of strptime():
#include <time.h>
#include <stdio.h>
int main () {
time_t epoch = 0;
struct tm t;
char *ret;
t = *localtime(&epoch);
ret = strptime("2015-04-19 12:00:00", "%F %T", &t); /* Sunday */
printf("%d\n", t.tm_wday); /* Should print 0 */
printf("strptime() returned %p (%d)\n", ret, *ret);
return 0;
}
Here is the output:
$ gcc timetest.c ; ./a.out
6
strptime() returned 0x10c72af83 (0)
Here is the clang version I use:
$ clang -v
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.1.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

I think the reason is simply that, by design, the strptime function only sets the fields that appear in the format. Essentially, strptime(3) just parses fields from a given string using a supplied format into a referenced structure and performs no other computation or logic. Since your code uses the format %F %T then only the fields corresponding to %Y-%m-%d and %H:%M:%S (namely tm_{year,mon,mday,hour,min,sec}) are modified.
You can experiment by explicitly setting t.tm_wday to some known value that shouldn't by set by strptime (e.g. 123) and verify that it is not changed by that call. Note that you should probably initialize your struct tm before playing with it since any of those fields may contain random values, e.g. struct tm t; memset((void *) &t, 0, sizeof(t));.
Moreover, this Linux strptime(3) man page contains the following note which leads me to believe that the special behavior it describes is non-standard (though obviously desirable):
The glibc implementation does not touch those fields which are not explicitly specified, except that it recomputes the tm_wday and tm_yday field if any of the year, month, or day elements changed.
This answer shows how you can use the trio of strptime/mktime/localtime (or gmtime) to populate the tm.tm_wday field for dates after the UNIX epoch.

Following on from the observation in the comments, here's a program derived from your code which illustrates, even if it does not explain, what is going on:
#include <time.h>
#include <stdio.h>
static void dump_struct_tm(const char *tag, const struct tm *t)
{
printf("%s:\n", tag);
printf(" Time: %.2d:%.2d:%.2d ", t-> tm_hour, t->tm_min, t->tm_sec);
printf(" Date: %.4d-%.2d-%.2d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
printf(" Wday: %d Yday: %.3d (DST %d Zone [%s] offset %ld)\n",
t->tm_wday, t->tm_yday, t->tm_isdst, t->tm_zone, t-> tm_gmtoff);
}
int main(void)
{
time_t epoch = 0;
struct tm t;
char *ret;
t = *localtime(&epoch);
dump_struct_tm("Epoch", &t);
putchar('\n');
ret = strptime("2015-04-19 12:00:00", "%F %T", &t);
dump_struct_tm("strptime()", &t);
time_t rt = mktime(&t);
dump_struct_tm("mktime()", &t);
printf("Weekday: %d\n", t.tm_wday);
printf("strptime() returned %p (%d)\n", ret, *ret);
printf("Unix time: %lld\n\n", (long long)rt);
t.tm_isdst = -1;
ret = strptime("2015-04-19 12:00:00", "%F %T", &t);
dump_struct_tm("strptime()", &t);
rt = mktime(&t);
dump_struct_tm("mktime()", &t);
printf("Weekday: %d\n", t.tm_wday);
printf("strptime() returned %p (%d)\n", ret, *ret);
printf("Unix time: %lld\n\n", (long long)rt);
return 0;
}
This analyzes the struct tm (as defined by the manual page on macOS Sierra) at different points. Note how the setting of tm_isdst alters the behaviour.
Epoch:
Time: 16:00:00 Date: 1969-12-31
Wday: 3 Yday: 364 (DST 0 Zone [PST] offset -28800)
strptime():
Time: 12:00:00 Date: 2015-04-19
Wday: 6 Yday: 108 (DST 0 Zone [(null)] offset -28800)
mktime():
Time: 13:00:00 Date: 2015-04-19
Wday: 0 Yday: 108 (DST 1 Zone [PDT] offset -25200)
Weekday: 0
strptime() returned 0x100c82f0c (0)
Unix time: 1429473600
strptime():
Time: 12:00:00 Date: 2015-04-19
Wday: 6 Yday: 108 (DST -1 Zone [(null)] offset -25200)
mktime():
Time: 12:00:00 Date: 2015-04-19
Wday: 0 Yday: 108 (DST 1 Zone [PDT] offset -25200)
Weekday: 0
strptime() returned 0x100c82f0c (0)
Unix time: 1429470000
I'm still not clear why strptime() mispopulates the tm_wday field, especially since it seems to get the tm_yday field correct. The 19th of April is day 108 of the year when the 1st of January is day 0.

Related

Retrieve Linux Time using struct timespec

Can someone please suggest how can I retrieve linux time using
struct timespec ts
type? It just gives me time since Epoch. Can I get the actual Linux time using this datatype?
Brief Background: I am writing a logger utility on embedded device with timestamp resolution in milli/micro seconds. The source adds timestamp which is consumed by destination component.
struct stLogItem logitem; //stLogItem has a member, struct timespec ts
clock_gettime(clk_id, &logitem.ts);
The destination component is printing this log timestamp on file/console. But the date which is printed out is time since Epoch, and not the actual Linux date.
The printed data is:
1970-01-01 23:30:07.586864475
Whereas, the Linux date is different as shown below:
root#imh:# date
Tue Nov 14 11:34:12 UTC 2017
Its not a format issue. It is about getting the current Linux time (in nano seconds).
After calling clock_gettime, ts.tv_sec, which has type time_t, is populated with the timestamp in seconds since the epoch. You can pass that directly to localtime:
struct timespec ts;
clock_gettime(clk_id, &ts);
struct tm *my_tm = localtime(&ts.tv_sec);
Now my_tm points to a struct tm which has the time broken down into year / month / day / hour / minute / second, and ts.tv_nsec has the nanosecond portion.
What keeps you from doing:
#include <time.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
char * p = ctime(&ts.tv_sec); /* Note that ctime() isn't thread-safe. */
p[strcspn(p, "\r\n")] = 0;
printf("Date: %s %ldns\n", p, ts.tv_nsec);
...
}
From the relevant documentation:
All implementations support the system-wide real-time clock, which is
identified by CLOCK_REALTIME. Its time represents seconds and
nanoseconds since the Epoch.
(POSIX documentation is here.)
Seeing #dbush's answer is exactly what I needed to see to learn how to convert Linux timestamps to usable local times!
I took it further though and printed the timestamp output in more ways, including in human-readable strings with day, month, year, etc., like this: Fri Apr 15 14:05:12 2022 -0700.
Here is the full source code, from my eRCaGuy_hello_world repo:
timing_clock_gettime_basic.c
// This define is required to bring in some of the extra POSIX features defined
// in `<time.h>`. Depending on your compiler settings, it may be required to
// get access to `clock_gettime()`. Using `-std=gnu17`, however, brings it in
// automatically since the compiler then uses the "gnu c" language instead of
// the standard "c" language.
//
// #define _POSIX_C_SOURCE 200112L
// Linux includes
// NA
// C includes
#include <errno.h> // `errno`
#include <inttypes.h> // `PRIu64`
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <string.h> // `strerror(errno)`
#include <time.h> // Includes `clock_gettime()` on Linux
#define NS_PER_SEC (1000000000L)
/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*NS_PER_SEC)
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Obtain an NTP-adjusted Real-time Clock timestamp on Linux.\n\n");
// Obtain a timestamp
struct timespec ts;
int retcode = clock_gettime(CLOCK_REALTIME, &ts);
if (retcode == -1)
{
printf("Failed to get a timestamp. errno = %i: %s\n",
errno, strerror(errno));
}
// Print seconds.nanoseconds
printf("timestamp = %li.%09li sec.\n\n", ts.tv_sec, ts.tv_nsec);
// Convert it to just `uint64_t` nanoseconds
// See: eRCaGuy_hello_world/c/timinglib.c
uint64_t ns = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
printf("timestamp = %" PRIu64 " nanoseconds.\n\n", ns);
// Convert it to a local time stored in `struct tm`. Use the re-entrant
// (thread-safe) version of the function, called `localtime_r()`. See:
// 1. https://man7.org/linux/man-pages/man3/localtime.3p.html
// 1. https://stackoverflow.com/a/47532938/4561887
// 1. `struct tm`: https://man7.org/linux/man-pages/man3/ctime.3.html
struct tm localtime_struct;
// Note: retptr means "return pointer"
struct tm * retptr = localtime_r(&ts.tv_sec, &localtime_struct);
if (retptr == NULL)
{
printf("Failed to convert to localtime. errno = %i: %s\n",
errno, strerror(errno));
}
printf("localtime_struct contains:\n"
" ns = %li\n" // Nanoseconds (0-999999999); NOT FROM THIS STRUCT
" sec = %i\n" // Seconds (0-60)
" min = %i\n" // Minutes (0-59)
" hour = %i\n" // Hours (0-23)
" mday = %i\n" // Day of the month (1-31)
" mon = %i\n" // Month (0-11)
" year = %i\n" // Year - 1900
" wday = %i\n" // Day of the week (0-6, Sunday = 0)
" yday = %i\n" // Day in the year (0-365, 1 Jan = 0)
" isdst = %i\n" // Daylight saving time
"\n",
ts.tv_nsec,
localtime_struct.tm_sec,
localtime_struct.tm_min,
localtime_struct.tm_hour,
localtime_struct.tm_mday,
localtime_struct.tm_mon,
localtime_struct.tm_year,
localtime_struct.tm_wday,
localtime_struct.tm_yday,
localtime_struct.tm_isdst);
// Convert the `struct tm` localtime struct to a human-readable string in
// normal human time units of Day, Month, Year, etc.
// - This is the format string required to output timestamps in the exact
// same format as `git` uses when you `git commit`.
const char * time_format_str = "%a %b %-d %H:%M:%S %Y %z";
char time_str[100];
size_t bytes_written = strftime(time_str, sizeof(time_str),
time_format_str, &localtime_struct);
if (bytes_written == 0)
{
printf("Failed to convert `struct tm` to a human-readable "
"time string.\n");
}
printf("Formatted local time string = %s\n\n", time_str);
return 0;
}
Sample build and run command, and output:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 timing_clock_gettime_full_demo.c -o bin/a && bin/a
Obtain an NTP-adjusted Real-time Clock timestamp on Linux.
timestamp = 1650056712.080211270 sec.
timestamp = 1650056712080211270 nanoseconds.
localtime_struct contains:
ns = 80211270
sec = 12
min = 5
hour = 14
mday = 15
mon = 3
year = 122
wday = 5
yday = 104
isdst = 0
Formatted local time string = Fri Apr 15 14:05:12 2022 -0700
The above code is part of my more-thorough answer here: How to read an NTP-adjusted RTC (Real-time clock) timestamp on Linux

C programming - calculate differences of 2 date times

I am trying to use getDateTime to calculate the difference between two different time dates: It suppose to be something like this:
Enter date and time (yyyy-mm-dd hh:mm:ss): 2017-03-13 12:00:00 Start: Mon Mar 13 12:00:00 2017
Enter date and time (yyyy-mm-dd hh:mm:ss): 2017-03-13 12:00:30 End: Mon Mar 13 12:00:30 2017
Difference: 30 seconds.
This is my attempt:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int dayOfWeek(int d, int m, int y){
// Compute day of week: 0 Sun 1 Mon...6 Sat
int z;
if(m<3){
z=y;
y=y-1;
}
else z=y-2;
return (23*m/9 + d + z + 4 +
y/4- y/100 + y/400)%7;
}
int getDateTime(struct tm *t){
int year,month,day,hrs,min,sec;
printf("Enter date and time (yyyy-mm-dd hh:mm:ss): ");
scanf("%d-%d-%d", &year, &month, &day);
scanf("%d:%d:%d",&hrs,&min,&sec);
int x;
t->tm_year = year - 1900;
t->tm_mon = month - 1;
t->tm_mday = day;
t->tm_hour = hrs;
t->tm_min = min;
t->tm_sec = sec;
t->tm_wday = dayOfWeek(day,month,year);
return ;
}
int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
struct tm Time1 , Time2;
double x;
getDateTime(&Time1);
printf("Start: %s", asctime(&Time1));
getDateTime(&Time2);
printf("End: %s", asctime(&Time2));
return EXIT_SUCCESS;
}
I was able to print it in the correct format, but how do I calculate the difference in seconds ? I tried to use difftime but I think I use it wrong. Many thanks !
There's already a function to parse dates, strptime. It's like scanf for dates. It follows the same formatting codes as strftime (p for parse, f for format).
You can do it long hand.
struct tm date;
strptime( "2017-03-13 12:00:00", "%Y-%m-%s %H:%M:%S", &date );
Or use the short hands provided for ISO 8601 dates and times.
struct tm date;
strptime( "2017-03-13 12:00:00", "%F %T", &date );
That replaces getDateTime.
Once that's done and you have the dates as struct tm, you can convert them to seconds since the epoch (1970-01-01 00:00:00 UTC) and subtract. Converting to seconds since the epoch, time_t, is done using mktime. Then subtract with difftime.
time_t seconds1 = mktime(&date1);
time_t seconds2 = mktime(&date2);
double timediff = difftime( seconds1, seconds2 );
Though, quite honestly, you can also just seconds1 - seconds2. difftime was added to the standard to allow time_t to not be a simple number, but I know of no instance of that.
mktime assumes the date is in the local time zone. That doesn't matter for the purposes of diffing two dates in the same time zone.
If it does, there's the non-standard function timegm, which is mktime but it assumes they're UTC dates. Or you can write your own using the POSIX functions setenv and tzset to change the time zone temporarily.
time_t my_timegm(struct tm *date) {
// Store the current TZ value.
char *tz = getenv("TZ");
// Unset TZ which will cause UTC to be used.
setenv("TZ", "", 1);
tzset();
// Call mktime, now in UTC.
time_t time = mktime(date);
// Restore the TZ environment variable (or leave it unset).
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return time;
}

strftime issues day of the week

char buffer[800];
struct tm str_time;
str_time.tm_mday = Cur_Day;
str_time.tm_mon = Cur_Month - 1;
str_time.tm_year = entries[i].Year_Start - 1900;
int len = strftime(buffer, 100, "%A, %d %B %Y", &str_time);
printf("\n%s\n", buffer);
What about the above results in the day of the week always being Sunday, regardless of values of Cur_Day and Cur_Month?
Sample output:
Sunday, 23 November 2012
------------------------
stuff
Sunday, 25 November 2012
------------------------
stuff
Sunday, 26 November 2012
------------------------
stuff
Your str_time structure (if, as it seems to be, it's a local variable) has indeterminate values in its fields unless you explicitly set them. All strftime does is use the values it has, it doesn't first adjust values to conform other fields.
Since you're not setting tm_wday, it will stay with whatever it was originally (0 by the looks of it, since it's always Sunday).
If you do want to adjust fields based on other fields, you should look into mktime().
From the standard (ISO C99):
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.
Your best bet is to use time() and localtime() to populate a tm structure, then change the fields you want to change before calling mktime(). That way, you're guaranteed that all fields will have sensible values.
The following program shows one way to do this:
#include <stdio.h>
#include <time.h>
int main (void) {
char buffer[100];
time_t now;
struct tm *ts;
// Get today in local time and output it.
now = time (NULL);
struct tm *ts = localtime (&now);
strftime (buffer, 100, "%A, %d %B %Y", ts);
printf ("Now = %s\n", buffer);
// Advance day-of-month and make new date.
// Probably need to intelligently handle month rollover.
ts->tm_mday++;
mktime (ts);
strftime (buffer, 100, "%A, %d %B %Y", ts);
printf ("Tomorrow = %s\n", buffer);
return 0;
}
The output of that program is:
Now = Tuesday, 09 October 2012
Tomorrow = Wednesday, 10 October 2012
For what it's worth, here's a complete program that uses that method to give you the day of the week for a given date (defaults to today).
You can change the year, month and day of month with the optional -y, -m and -d arguments in any order you want, and as many times as you want, though only the last for each type counts.
#include <stdio.h>
#include <time.h>
static int makeError (char *argVal, char *errStr) {
printf ("Error with argument '%s': %s\n", argVal, errStr);
printf ("Usage: dow [-y<year>] [-m<month>] [-d<day>]\n");
return 1;
}
int main (int argc, char *argv[]) {
int idx, intVal;
char chVal;
char buff[100];
time_t now = time (NULL);
struct tm *nowStr = localtime (&now);
for (idx = 1; idx < argc; idx++) {
chVal = (*argv[idx] != '-') ? '\0' : *(argv[idx] + 1);
if ((chVal != 'y') && (chVal != 'm') && (chVal != 'd'))
return makeError (argv[idx], "does not start with '-y/m/d'");
intVal = atoi (argv[idx] + 2);
if (intVal < 0)
return makeError (argv[idx], "suffix is negative");
sprintf (buff, "%d", intVal);
if (strcmp (buff, argv[idx] + 2) != 0)
return makeError (argv[idx], "suffix is not numeric");
switch (chVal) {
case 'y': nowStr->tm_year = intVal - 1900; break;
case 'm': nowStr->tm_mon = intVal - 1; break;
case 'd': nowStr->tm_mday = intVal; break;
}
}
mktime (nowStr);
strftime (buff, sizeof (buff), "%A, %d %B %Y", nowStr);
printf ("%s\n", buff);
return 0;
}
A sample transcript:
pax> ./dow
Tuesday, 09 October 2012
pax> ./dow -y2011
Sunday, 09 October 2011
pax> ./dow -y2000 -m1 -d1
Saturday, 01 January 2000
The most probable explanation is that your strftime requires tm_wday to have a meaningful value if you're going to ask it to print the day of the week.
This is the simplest available way to avoid having to compute it yourself:
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
tm.tm_mday = Cur_Day;
tm.tm_mon = Cur_Month - 1;
tm.tm_year = entries[i].Year_Start - 1900;
tm.tm_hour = 12;
(void) timegm(&tm); /* fills in the rest of `tm` as a side effect */
/* now call strftime */
If you don't have timegm you MAY be able to get away with using mktime instead (the problems with doing this calculation in 'local' time are largely irrelevant if you only want to print the date). DO NOT use the "portable version of timegm" described in the Linux manpage for timegm, it has portability gotchas waiting to bite on almost every line!
You need to set str_time.tm_wday so that strftime() can convert it.
See localtime(3) and strftime(3) for examples.

How to convert from UTC to local time in C?

It's a simple question, but the solution appears to be far from simple. I would like to know how to convert from UTC to local time. I am looking for a solution in C that's standard and more or less guaranteed to work on any computer at any location.
I have read the following links carefully but I can't find a solution there:
Converting string containing localtime into UTC in C
Converting Between Local Times and GMT/UTC in C/C++
I have tried a number of variations, such as (datetime is a string with time and date in UTC):
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);
Or
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(&lt);
No matter what I try printtime ends up being the same as UTC.
Edit 11-29-2013: based on the very helpful answer by "R" below I finally got around to create a working example. I found it to be working correct in the two timezones I tested it, CET and PST:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
long long diff_tm(struct tm *a, struct tm *b)
{
return a->tm_sec - b->tm_sec
+60LL*(a->tm_min - b->tm_min)
+3600LL*(a->tm_hour - b->tm_hour)
+86400LL*(a->tm_yday - b->tm_yday)
+(a->tm_year-70)*31536000LL
-(a->tm_year-69)/4*86400LL
+(a->tm_year-1)/100*86400LL
-(a->tm_year+299)/400*86400LL
-(b->tm_year-70)*31536000LL
+(b->tm_year-69)/4*86400LL
-(b->tm_year-1)/100*86400LL
+(b->tm_year+299)/400*86400LL;
}
int main()
{
time_t utc, local;
char buf[100];
const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */
struct tm *tp=malloc(sizeof(struct tm));
if(tp==NULL)
exit(-1);
struct tm *localt=malloc(sizeof(struct tm));
if(localt==NULL)
exit(-1);
memset(tp, 0, sizeof(struct tm));
memset(localt, 0, sizeof(struct tm));
printf("UTC date and time to be converted in local time: %s\n", datetime);
/* put values of datetime into time structure *tp */
strptime(datetime, "%Y %m %d %H %M %S %z", tp);
/* get seconds since EPOCH for this time */
utc=mktime(tp);
printf("UTC date and time in seconds since EPOCH: %d\n", utc);
/* lets convert this UTC date and time to local date and time */
struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new;
/* get time_t EPOCH value for e0 (Jan. 1, 1970) */
time_t pseudo=mktime(&e0);
/* get gmtime for this value */
e1=*gmtime(&pseudo);
/* calculate local time in seconds since EPOCH */
e0.tm_sec += utc - diff_tm(&e1, &e0);
/* assign to local, this can all can be coded shorter but I attempted to increase clarity */
local=e0.tm_sec;
printf("local date and time in seconds since EPOCH: %d\n", local);
/* convert seconds since EPOCH for local time into localt time structure */
localt=localtime(&local);
/* get nicely formatted human readable time */
strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt);
printf("local date and time: %s\n", buf);
}
It should compile without problems on most systems. I hard coded a time and date in UTC which then will be converted to the local time and date.
If you can assume POSIX (and thus the POSIX specification of time_t as seconds since the epoch), I would first use the POSIX formula to convert to seconds since the epoch:
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
Next, use localtime((time_t []){0}) to get a struct tm representing the epoch in local time. Add the seconds since the epoch to the tm_sec field of this struct tm, then call mktime to canonicalize it.
Edit: Actually the only POSIX dependency is having a known epoch which (time_t)0 corresponds to. Perhaps you can find a way around that if you really need to... for instance using calls to both gmtime and localtime at time_t 0..
Edit 2: A sketch of how to do this:
#include <time.h>
#include <stdio.h>
long long diff_tm(struct tm *a, struct tm *b)
{
return a->tm_sec - b->tm_sec
+60LL*(a->tm_min - b->tm_min)
+3600LL*(a->tm_hour - b->tm_hour)
+86400LL*(a->tm_yday - b->tm_yday)
+(a->tm_year-70)*31536000LL
-(a->tm_year-69)/4*86400LL
+(a->tm_year-1)/100*86400LL
-(a->tm_year+299)/400*86400LL
-(b->tm_year-70)*31536000LL
+(b->tm_year-69)/4*86400LL
-(b->tm_year-1)/100*86400LL
+(b->tm_year+299)/400*86400LL;
}
int main(int argc, char **argv)
{
char buf[100];
struct tm e0 = { .tm_year = 70, .tm_mday = 1 }, e1, new;
time_t pseudo = mktime(&e0);
e1 = *gmtime(&pseudo);
e0.tm_sec += atoi(argv[1]) - diff_tm(&e1, &e0);
mktime(&e0);
strftime(buf, sizeof buf, "%c", &e0);
puts(buf);
}
Please don't mind the ugly output code. This program takes an argument in the form of "seconds relative to the POSIX epoch" and outputs the resulting time in local time. You can convert any UTC time to seconds since the epoch using the formula I cited above. Note that this code does not in any way depend on POSIX, but it does assume the offset returned by diff_tm combined with the seconds-since-the-epoch value does not overflow int. A fix for this would be to use a long long offset and a loop that keeps adding increments no larger than INT_MAX/2 (or smaller than INT_MIN/2) and calling mktime to renormalize until the offset reaches 0.
Ahm ... I might just be a beginner in C, but I got this working example:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t abs_ts,loc_ts,gmt_ts;
struct tm loc_time_info,gmt_time_info;
/*Absolute time stamp.*/
time(&abs_ts);
/*Now get once the local time for this time stamp,
**and once the GMT (UTC without summer time) time stamp.*/
localtime_r(&abs_ts,&loc_time_info);
gmtime_r(&abs_ts,&gmt_time_info);
/*Convert them back.*/
loc_ts=mktime(&loc_time_info);
gmt_ts=mktime(&gmt_time_info);
/*Unfortunately, GMT still has summer time. Get rid of it:*/
if(gmt_time_info.tm_isdst==1)
{gmt_ts-=3600;}
printf("Local timestamp: %lu\n"
"UTC timestamp: %lu\n"
"Difference in hours: %lu\n\n",
loc_ts,
gmt_ts,
(loc_ts-gmt_ts)/3600);
return 0;
}
Which produces this output:
Local timestamp: 1412554119
GMT timestamp: 1412546919
Difference in hours: 2
Now you have the difference between UTC and local time in seconds. That should be enough to convert it.
One note to your code, aseq: you are using malloc without need here (you can memset values on the stack as well, and malloc can be expensive while stack allocation is often much faster), and you do not free it. That's very, very bad practise.
Another thing:
memset(tp, 0, sizeof(struct tm));
Would be better done if you'd pass sizeof(*tp) (or, if you put tp on the stack, sizeof(tp)) to memset. That ensures that even if the type of your object changes, it will still be fully memset.
To sum-up: the conversion of a broken down date (struct tm) in UTC to a (local) calendar time (time_t) is achieved with timegm() - the opposite of mktime() - BUT timegm() is not a standard function (how logic is that).
The C standard leaves us with only time(), gmtime(), mktime() and difftime().
A workaround found in other docs advises to emulate timegm() by setting first the environment variable TZ to a null string, then calling mktime() resulting in an UTC calendar time, then resetting TZ to its initial value, but once again, this is not standard.
Basically, as I understand it, the difference between a local time and UTC time is just an offset so if we can evaluate that offset, we can adjust the result of mktime(), so here's my proposition:
time_t my_timegm(struct tm *tm) {
time_t epoch = 0;
time_t offset = mktime(gmtime(&epoch));
time_t utc = mktime(tm);
return difftime(utc, offset);
}
A quick test:
int main(void) {
time_t now = time(0);
struct tm local = *localtime(&now);
struct tm utc = *gmtime(&now);
time_t t1 = mktime(&local);
time_t t2 = my_timegm(&utc);
assert(t1 == t2);
printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
return 0;
}
//working stand alone function adjusting UTC to local date and time
//globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
//adjust date and time according to UTC
//tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards
void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month
u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr; //2115 --> 2100 hr and 15 min
if(tzdir){//adjusting forwards
tz+=gps.Hm;
if(tz>2400){gps.Hm=tz-2400;gps.Day++; //spill over to next day
if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
if(gps.Mth>12){gps.Mth=1; gps.Yr++; //spill over to next year
}
}
}else{gps.Hm=tz;}
}else{//adjusting backwards
if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--; // back to previous day
if(gps.Day==0){ //back to previous month
gps.Mth--;gps.Day=maxDayPrevMth;
if(!gps.Mth){gps.Mth=12; //back to previous year
gps.Yr--;
}
}
}else{gps.Hm-=tz;}
}
}
I think it's easier than that; time.h defines three variables:
extern int daylight;
extern long timezone;
extern char *tzname[];
which are loaded based on the TZ env variable when you call
tzset();
if you have a utc time in
struct tm date;
date.tm_isdst = 0;
convert it to a time_t using mktime
time_t utc = mktime( &date );
then convert it to local time
time_t local = utc - timezone + ( daylight?3600:0 );
timezone is the number of seconds away from utc for the current timezone and daylight is 1 to indicate daylight savings time is in play and zero for not.
A small caution: When I coded this for a microcontroller and cross compiled, it's time.h defined those variables with initial underscores.
See the man page for time.h
I found that the solution the OP gave did not work in cases when DST applies. For example, in my case, at the current time, DST was not in effect, but if I set the initial date which should convert to local time with DST, then it would not work, i.e. today's date is 3/1/2018 and DST is not in effect, but if I set the date for conversion to, say, 8/1/2018 0:00:00 when DST is in effect, then the solution given would convert to local time, but would not take DST into account. I found that initializing e0 to the date and hour of the initial date/time string and its member tm_isdst to -1 solved the problem. I then created the following program with complementary functions which you can include in your code. The initial format of the date and time is the same that MySQL uses, because I needed it for such purposes.
#include <stdio.h>
#include <time.h>
#include <string.h>
long long diff_tm(struct tm *a, struct tm *b) {
return a->tm_sec - b->tm_sec
+ 60LL * (a->tm_min - b->tm_min)
+ 3600LL * (a->tm_hour - b->tm_hour)
+ 86400LL * (a->tm_yday - b->tm_yday)
+ (a->tm_year - 70) * 31536000LL
- (a->tm_year - 69) / 4 * 86400LL
+ (a->tm_year - 1) / 100 * 86400LL
- (a->tm_year + 299) / 400 * 86400LL
- (b->tm_year - 70) * 31536000LL
+ (b->tm_year - 69) / 4 * 86400LL
- (b->tm_year - 1) / 100 * 86400LL
+ (b->tm_year + 299) /400 * 86400LL;
}
void localToUTC(char *buf, const char *localTime) {
struct tm tp;
strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm res = *gmtime(&utc);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
}
void utcToLocal(char *buf, const char *utcTime) {
struct tm tp;
strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
time_t pseudo = mktime(&e0);
struct tm e1 = *gmtime(&pseudo);
e0.tm_sec += utc - diff_tm(&e1, &e0);
time_t local = e0.tm_sec;
struct tm localt = *localtime(&local);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
}
int main(void) {
char mytime_1[20] = "2018-02-28 13:00:00";
char utctime_1[20], back_1[20];
localToUTC(utctime_1, mytime_1);
utcToLocal(back_1, utctime_1);
printf("My time: %s\n", mytime_1);
printf("UTC time: %s\n", utctime_1);
printf("Back: %s\n", back_1);
printf("-------------------------------------------\n");
char mytime_2[20] = "2018-07-28 17:00:00";
char utctime_2[20], back_2[20];
localToUTC(utctime_2, mytime_2);
utcToLocal(back_2, utctime_2);
printf("My time: %s\n", mytime_2);
printf("UTC time: %s\n", utctime_2);
printf("Back: %s\n", back_2);
printf("-------------------------------------------\n");
return 0;
}
I followed the answer by #Dachschaden and I made an example which also shows human-readable output and I remove the DST option for the difference in seconds between UTC and local time. Here it is:
#include <time.h>
#include <stdio.h>
#define DATE_MAX_STR_SIZE 26
#define DATE_FMT "%FT%TZ%z"
int main() {
time_t now_time, now_time_local;
struct tm now_tm_utc, now_tm_local;
char str_utc[DATE_MAX_STR_SIZE];
char str_local[DATE_MAX_STR_SIZE];
time(&now_time);
gmtime_r(&now_time, &now_tm_utc);
localtime_r(&now_time, &now_tm_local);
/* human readable */
strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);
printf("\nUTC: %s", str_utc);
printf("\nLOCAL: %s\n", str_local);
/* seconds (epoch) */
/* let's forget about DST for time difference calculation */
now_tm_local.tm_isdst = 0;
now_tm_utc.tm_isdst = 0;
now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));
printf("\nUTC in seconds: %lu", now_time);
printf("\nLOCAL in seconds: %lu\n", now_time_local);
return 0;
}
Output on my machine is:
UTC: 2016-05-05T15:39:11Z-0500
LOCAL: 2016-05-05T11:39:11Z-0400
UTC in seconds: 1462462751
LOCAL in seconds: 1462448351
Note that DST is on in this case (there's a 1 hour time zone offset difference between UTC and LOCAL).
try this, test output:
utcEpochTime: 1487652688, localEpochTime: 1487699488, diff: 46800
$ python
>>>46800 / 60 / 60
13
the diff is 13 hours, which is good, as my timezone is UTC+8.
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[])
{
time_t utcEpochTime = time(0);
time_t localEpochTime = 0;
struct tm tm = {0};
localtime_r(&utcEpochTime, &tm);
tm.tm_isdst = -1;
localEpochTime = timegm(&tm);
printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
return 0;
}
A simple and effective way: Add (or subtract) the number of seconds between your time zone and UTC (considering daylight saving time).
As an example that worked just fine a minute ago, on December 30, 2017, with U.S. Mountain Standard Time (no DST), which is 7 hours behind UTC:
time_t current_time_UTC;
time_t current_time_MST;
struct tm *current_broken_time_MST;
uint32_t seven_hours_in_seconds = 25200; // Get this any way you want
current_time_UTC = time (NULL); // UTC
current_time_MST = current_time_UTC - seven_hours_in_seconds; // MST
current_broken_time_MST = localtime (&current_time_MST); // MST
Enjoy.
void CTestDlg::OnBtnTest()
{
HANDLE hFile;
WIN32_FIND_DATA wfd;
SYSTEMTIME systime;
FILETIME localtime;
char stime[32]; //
memset(&wfd, 0, sizeof(wfd));
if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ", &wfd))==INVALID_HANDLE_VALUE)
{
char c[2];
DWORD dw=GetLastError();
wsprintf(c, "%d ", dw);
AfxMessageBox(c);
return ;//
}
FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime);
FileTimeToSystemTime(&localtime,&systime);
sprintf(stime, "%4d-%02d-%02d %02d:%02d:%02d ",
systime.wYear,systime.wMonth,systime.wDay,systime.wHour,
systime.wMinute,systime.wSecond);
AfxMessageBox(stime);
}

How to get the date and time values in a C program?

I have something like this:
char *current_day, *current_time;
system("date +%F");
system("date +%T");
It prints the current day and time in the stdout, but I want to get this output or assign them to the current_day and current_time variables, so that I can do some processing with those values later on.
current_day ==> current day
current_time ==> current time
The only solution that I can think of now is to direct the output to some file, and then read the file and then assign the values of date and time to current_day and current_time. But I think this is not a good way. Is there any other short and elegant way?
Use time() and localtime() to get the time:
#include <stdio.h>
#include <time.h>
int main()
{
time_t t = time(NULL);
struct tm tm = *localtime(&t);
printf("now: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
strftime (C89)
Martin mentioned it, here's an example:
main.c
#include <assert.h>
#include <stdio.h>
#include <time.h>
int main(void) {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char s[64];
size_t ret = strftime(s, sizeof(s), "%c", tm);
assert(ret);
printf("%s\n", s);
return 0;
}
GitHub upstream.
Compile and run:
gcc -std=c89 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Sample output:
Thu Apr 14 22:39:03 2016
The %c specifier produces the same format as ctime.
One advantage of this function is that it returns the number of bytes written, allowing for better error control in case the generated string is too long:
RETURN VALUE
Provided that the result string, including the terminating null byte, does not exceed max bytes, strftime() returns the number of bytes (excluding the terminating null byte) placed in the array s. If the length of the result string (including the terminating null byte) would exceed max bytes, then strftime() returns 0, and the contents of the array are undefined.
Note that the return value 0 does not necessarily indicate an error. For example, in many locales %p yields an empty string. An empty format string will likewise yield an empty string.
asctime and ctime (C89, deprecated in POSIX 7)
asctime is a convenient way to format a struct tm:
main.c
#include <stdio.h>
#include <time.h>
int main(void) {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
printf("%s", asctime(tm));
return 0;
}
Sample output:
Wed Jun 10 16:10:32 2015
And there is also ctime() which the standard says is a shortcut for:
asctime(localtime())
As mentioned by Jonathan Leffler, the format has the shortcoming of not having timezone information.
POSIX 7 marked those functions as "obsolescent" so they could be removed in future versions:
The standard developers decided to mark the asctime() and asctime_r() functions obsolescent even though asctime() is in the ISO C standard due to the possibility of buffer overflow. The ISO C standard also provides the strftime() function which can be used to avoid these problems.
C++ version of this question: How to get current time and date in C++?
Tested in Ubuntu 16.04.
time_t rawtime;
time ( &rawtime );
struct tm *timeinfo = localtime ( &rawtime );
You can also use strftime to format the time into a string.
To expand on the answer by Ori Osherov
You can use the WinAPI to get the date and time, this method is specific to Windows, but if you are targeting Windows only, or are already using the WinAPI then this is definitly a possibility1:
You can get both the time and date by using the SYSTEMTIME struct. You also need to call one of two functions (either GetLocalTime() or GetSystemTime()) to fill out the struct.
GetLocalTime() will give you the time and date specific to your time zone.
GetSystemTime() will give you the time and date in UTC.
The SYSTEMTIME struct has the following members:
wYear, wMonth, wDayOfWeek, wDay, wHour, wMinute, wSecond and wMilliseconds
You then need to just access the struct in the regular way
Actual example code:
#include <windows.h> // use to define SYSTEMTIME , GetLocalTime() and GetSystemTime()
#include <stdio.h> // For printf() (could otherwise use WinAPI equivalent)
int main(void) { // Or any other WinAPI entry point (e.g. WinMain/wmain)
SYSTEMTIME t; // Declare SYSTEMTIME struct
GetLocalTime(&t); // Fill out the struct so that it can be used
// Use GetSystemTime(&t) to get UTC time
printf("Year: %d, Month: %d, Day: %d, Hour: %d, Minute:%d, Second: %d, Millisecond: %d", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond, t.wMilliseconds); // Return year, month, day, hour, minute, second and millisecond in that order
return 0;
}
(Coded for simplicity and clarity, see the original answer for a better formatted method)
The output will be something like this:
Year: 2018, Month: 11, Day: 24, Hour: 12, Minute:28, Second: 1, Millisecond: 572
Useful References:
All the WinAPI documentation (most already listed above):
GetLocalTime()
GetSystemTime()
SYSTEMTIME
Time Functions
An extremely good beginners tutorial on this subject by ZetCode:
https://zetcode.com/gui/winapi/datetime/
Simple operations with datetime on Codeproject:
https://www.codeproject.com/Articles/5546/WinAPI-Simple-Operations-with-datetime
1: As mentioned in the comments in Ori Osherov's answer ("Given that OP started with date +%F, they're almost certainly not using Windows. – melpomene Sep 9 at 22:17") the OP is not using Windows, however since this question has no platform specific tag (nor does it mention anywhere that the answer should be for that particular system), and is one of the top results when Googling "get time in c" both answers belong here, some users searching for an answer to this question may be on Windows and therefore will be useful to them.
Timespec has day of year built in.
http://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
#include <time.h>
int get_day_of_year(){
time_t t = time(NULL);
struct tm tm = *localtime(&t);
return tm.tm_yday;
}`
The answers given above are good CRT answers, but if you want you can also use the Win32 solution to this. It's almost identical but IMO if you're programming for Windows you might as well just use its API (although I don't know if you are programming in Windows).
char* arrDayNames[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
SYSTEMTIME st;
GetLocalTime(&st); // Alternatively use GetSystemTime for the UTC version of the time
printf("The current date and time are: %d/%d/%d %d:%d:%d:%d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
printf("The day is: %s", arrDayNames[st.wDayOfWeek]);
Anyway, this is a Windows solution. I hope it will be helpful for you sometime!
I was using command line C-compiler to compile these and it completely drove me bonkers as it refused to compile.
For some reason my compiler hated that I was declaring and using the function all in one line.
struct tm tm = *localtime(&t);
test.c
test.c(494) : error C2143: syntax error : missing ';' before 'type'
Compiler Status: 512
First declare your variable and then call the function. This is how I did it.
char todayDateStr[100];
time_t rawtime;
struct tm *timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
strftime(todayDateStr, strlen("DD-MMM-YYYY HH:MM")+1,"%d-%b-%Y %H:%M",timeinfo);
printf("todayDateStr = %s ... \n", todayDateStr );
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct date
{
int month;
int day;
int year;
};
int calcN(struct date d)
{
int N;
int f(struct date d);
int g(int m);
N = 1461 * f(d) / 4 + 153 * g(d.month) / 5 + d.day;
if(d.year < 1700 || (d.year == 1700 && d.month < 3))
{
printf("Date must be after February 29th, 1700\n");
return 0;
}
else if(d.year < 1800 || (d.year == 1800 && d.month < 3))
N += 2;
else if(d.year < 1900 || (d.year == 1900 && d.month < 3))
N += 1;
return N;
}
int f(struct date d)
{
if(d.month <= 2)
d.year -= 1;
return d.year;
}
int g(int m)
{
if(m <=2)
m += 13;
else
m += 1;
return m;
}
int main(void)
{
int calcN(struct date d);
struct date d1, d2;
int N1, N2;
time_t t;
time(&t);
struct tm *now = localtime(&t);
d1.month = now->tm_mon + 1;
d1.day = now->tm_mday;
d1.year = now->tm_year + 1900;
printf("Today's date: %02i/%02i/%i\n", d1.month, d1.day, d1.year);
N1 = calcN(d1);
printf("Enter birthday (mm dd yyyy): ");
scanf("%i%i%i", &d2.month, &d2.day, &d2.year);
N2 = calcN(d2);
if(N2 == 0)
return 0;
printf("Number of days since birthday: %i\n", N1 - N2);
return 0;
}
#include <stdio.h>
int main() {
char *pts; /* pointer to time string */
time_t now; /* current time */
char *ctime();
(void) time(&now);
printf("%s", ctime(&now));
return(0);
}
Sample output:
Sat May 14 19:24:54 2022
This is the easiest way. I haven't even used time.h.
Be advised: The output produced has a newline at the end.
instead of files use pipes and if u wana use C and not C++ u can use popen like this
#include<stdlib.h>
#include<stdio.h>
FILE *fp= popen("date +F","r");
and use *fp as a normal file pointer with fgets and all
if u wana use c++ strings, fork a child, invoke the command and then pipe it to the parent.
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
string currentday;
int dependPipe[2];
pipe(dependPipe);// make the pipe
if(fork()){//parent
dup2(dependPipe[0],0);//convert parent's std input to pipe's output
close(dependPipe[1]);
getline(cin,currentday);
} else {//child
dup2(dependPipe[1],1);//convert child's std output to pipe's input
close(dependPipe[0]);
system("date +%F");
}
// make a similar 1 for date +T but really i recommend u stick with stuff in time.h GL
#include<stdio.h>
using namespace std;
int main()
{
printf("%s",__DATE__);
printf("%s",__TIME__);
return 0;
}

Resources