strptime() produces wrong result on armv7l environment - c

I have the following code:
#include <stdio.h>
#include <string.h>
#include <time.h>
time_t make_time(char *input, char *hs) {
struct tm t;
char d[17];
strcpy(d, input);
strcat(d, hs);
d[16] = '\0';
strptime(d, "%m%d%Y%H:%M:%S", &t);
printf("data: %s", asctime(&t));
return mktime(&t);
}
void main(int argc, char *argv[]) {
char *s = argv[1];
char *e = argv[2];
time_t now = time(NULL);
time_t start = make_time(s, "00:00:00");
time_t end = make_time(e, "23:59:59");
int s1 = difftime(now, start);
int s2 = difftime(end, now);
int ret = 0;
if (s1 > 0 && s2 > 0) {
ret = 1;
}
printf("Result:\nInput: %s %s\nDiff: start: %d; end: %d\nret: %d\n", s, e, s1, s2, ret);
printf("\n");
}
2 versions were built. One is for use on debian linux & the other for an arm device.
On debian linux, the result looks right:
devbox#debian:~$ ./a.out 11182015 11302015
data: Wed Nov 18 00:00:00 2015
data: Mon Nov 30 23:59:59 2015
Result:
Input: 11182015 11302015
diff: start: 665291; end: 457908
ret: 1
But on armv7l env, strptime() produces wrong result:
# /tmp/a.out 11182015 11302015
data: ??? Jan 8 ??:??:?? 1900
data: ??? Jan 8 ??:??:?? 1900
Result:
Input: 11182015 11302015
diff: start: 1448437831; end: -1448437831
ret: 0
Is there something wrong with strptime on armv7l or is it not a standard GNU C method?
UPDATE
Here is the result of uname command:
# uname -a
Linux buildroot 3.1.0-xg3517-1.1 #1 Tue Mar 12 12:54:57 JST 2013 armv7l GNU/Linux

I finally know why & make it works. The problem is: strptime() seems to have no idea how to parse the following formats:
"%m%d%Y%H:%M:%S"
// or
"%m%d%Y %H:%M:%S"
// or
"%m/%d/%Y %H:%M:%S"
But, if I use this: "%m-%d-%Y %T"
strptime(d, "%m-%d-%Y %T", &t);
Then it works, what a shame !
I don't know why those formats are not accepted, since doc didn't say anything about those which are not being fully implemented. Anyway, if anyone knows why, please shed me some light.

Related

Printing current time in different time zones in C

I'm trying to print in the console the current time in my local timezone (-0600), then printing the time at the +0100 timezone. Currently I'm using gmtime, and adding 1 to the tm_hour section.
However when using strftime, it still prints the: "... +0000".
How can I print it properly? How can I change my effective time zone, for instance?
On macOS Sierra 10.12.2 with GCC 6.3.0, the following code works:
#include "posixver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef lint
extern const char jlss_id_settz_c[];
const char jlss_id_settz_c[] = "#(#)$Id: settz.c,v 1.2 2017/01/23 07:06:21 jleffler Exp $";
#endif
static void time_convert(time_t t0, char const *tz_value)
{
char old_tz[64] = "-none-";
char *tz = getenv("TZ");
if (tz != 0)
strcpy(old_tz, tz);
setenv("TZ", tz_value, 1);
tzset();
char new_tz[64];
strcpy(new_tz, getenv("TZ"));
char buffer[64];
struct tm *lt = localtime(&t0);
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
if (strcmp(old_tz, "-none-") == 0)
unsetenv("TZ");
else
setenv("TZ", old_tz, 1);
tzset();
printf("%ld = %s (TZ=%s)\n", (long)t0, buffer, new_tz);
}
int main(void)
{
time_t t0 = time(0);
char *tz = getenv("TZ");
if (tz != 0)
time_convert(t0, tz);
time_convert(t0, "UTC0");
time_convert(t0, "IST-5:30");
time_convert(t0, "EST5");
time_convert(t0, "EST5EDT");
time_convert(t0, "PST8");
time_convert(t0, "PST8PDT");
}
By default, TZ is not set in the environment — those ungainly tests for getenv("TZ") returning NULL are necessary to handle that. When run, the output is:
$ ./settz
1485155290 = 2017-01-23 07:08:10 (TZ=UTC0)
1485155290 = 2017-01-23 12:38:10 (TZ=IST-5:30)
1485155290 = 2017-01-23 02:08:10 (TZ=EST5)
1485155290 = 2017-01-23 02:08:10 (TZ=EST5EDT)
1485155290 = 2017-01-22 23:08:10 (TZ=PST8)
1485155290 = 2017-01-22 23:08:10 (TZ=PST8PDT)
$
With the environment set so TZ=US/Alaska, the output is:
$ TZ=US/Alaska ./settz
1485155395 = 2017-01-22 22:09:55 (TZ=US/Alaska)
1485155395 = 2017-01-23 07:09:55 (TZ=UTC0)
1485155395 = 2017-01-23 12:39:55 (TZ=IST-5:30)
1485155395 = 2017-01-23 02:09:55 (TZ=EST5)
1485155395 = 2017-01-23 02:09:55 (TZ=EST5EDT)
1485155395 = 2017-01-22 23:09:55 (TZ=PST8)
1485155395 = 2017-01-22 23:09:55 (TZ=PST8PDT)
$
This is ugly as a technique; it is also not fast. However, on some platforms, it does in fact work.

calculating time in C - AIX machine

I am editing time value using a variable of type struct tm (adding some seconds to tm->tm_sec), but I am getting wrong results after doing mktime(&t).
Doing so in Linux gets me proper results, but in AIX not. What could be the problem?
#include <stdio.h>
#include <time.h>
#include <langinfo.h>
#include <locale.h>
int main ()
{
struct tm tm;
struct tm *end;
time_t t;
char str[20] = {'\0'};
//if (strptime("7 Feb 2013 01:47:30", "%d %b %Y %H:%M:%S", &tm) == NULL)
if (strptime("2012-10-17-01-07-30", "%Y-%m-%d-%H-%M-%S", &tm) == NULL)
{printf("Error\n");
}
tm.tm_sec = (tm.tm_sec + 1200);
//tm.tm_sec = 12;
//t = mktime(&tm);
//t = t + 12;
//end =localtime(&t);
strftime(str,20,"%Y %m %d %H %M %S",&tm);
printf("str is %s\n",str);
return 0;
}
I believe the correct answer is to use time_t, which is a large number representing the time in seconds since midnight of 1 Jan 1970. Adding arbitrary number of seconds here becomes very trivial.
I expect that if you are just adding seconds to tm->tm_sec, it overflows, and that causes the result to be incorrect. If you are unlucky, you will need to ripple your change in seconds all the way through to year (adding 5 seconds to 31 Dec 2013 23:59:56 will take you to 01 Jan 2014 00:00:01). Which of course can be done, but instead of:
t =+ 5;
you get about a dozen steps along the line of
tm.tm_sec += 5;
if (tm.tm_sec >= 60)
{
tm.tm_sec -= 60;
tm.tm_min += 1;
if (tm.tm_min >= 60)
{
... And so on ...
}
}
It gets even more interesting if you overflow the days in a month, since you then have to take into account of the number of days in each month, 28, 29, 30 or 31 depending on which month [and if it's a leap-year or not].
This is effectively what Mats said:
#include <stdio.h>
#include <time.h>
#include <langinfo.h>
#include <locale.h>
int main ()
{
struct tm tm;
time_t t;
char str[20] = {'\0'};
if (strptime("2012-10-17-01-07-30", "%Y-%m-%d-%H-%M-%S", &tm) == NULL) {
printf("error\n");
}
t = mktime(&tm);
t += 1200;
tm = *localtime(&t);
strftime(str,20,"%Y %m %d %H %M %S",&tm);
printf("str is %s\n",str);
return 0;
}
Produces:
cc -o t t.c && ./t
str is 2012 10 17 02 27 30

Two digit date in strftime and struct tm

I'm doing the following to get time in "Day Month Day Year" format.
struct tm *example= localtime(&t);
strftime(buf,sizeof(buf),"%a %b %d %Y",example);
strncpy(time_buffer,buffer,sizeof(time_buffer)) ;
But if date is single digit,such as 9 it is displayed as 9. I'd like to print it as 09. Any idea how that can be done?
The manpage for strftime says:
%d The day of the month as a decimal number (range 01 to 31).
Which seems like what you want.
// compile with: gcc -o ex1 -Wall ex1.c
#include "stdio.h"
#include "sys/time.h"
#include "time.h"
int main (const int argc, const char ** argv ) {
time_t curr_time;
char buff[1024];
// time(&curr_time);
curr_time = 1359684105; // Thu Jan 31 2013
struct tm *now = localtime(&curr_time);
strftime(buff, sizeof(buff), "%a %b %d %Y", now);
printf("time: %ld\n", curr_time);
printf("time: %s\n", buff);
curr_time += 24 * 60 * 60; // Fri Feb 01 2013
now = localtime(&curr_time);
strftime(buff, sizeof(buff), "%a %b %d %Y", now);
printf("time: %ld\n", curr_time);
printf("time: %s\n", buff);
return 0;
}
That produces:
time: 1359684105
time: Thu Jan 31 2013
time: 1359770505
time: Fri Feb 01 2013
Which looks like what you're after. If you want to drop the leading zero, you can use %e:
%e Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU)
Author reported that %d doesn't adhere to the manpage on solaris, here's an alternate solution that uses sprintf directly:
// compile with: gcc -o ex1 -Wall ex1.c
#include "stdio.h"
#include "sys/time.h"
#include "time.h"
int main (const int argc, const char ** argv ) {
time_t curr_time;
char buff[1024], daynamebuff[8], monbuff[8], daynumbuff[3], yearbuff[8];
// time(&curr_time);
curr_time = 1359684105; // Thu Jan 31 2013
curr_time += 24 * 60 * 60; // Fri Feb 01 2013
struct tm *now = localtime(&curr_time);
strftime(daynamebuff, sizeof(daynamebuff), "%a", now);
strftime(monbuff, sizeof(monbuff), "%b", now);
strftime(daynumbuff, sizeof(daynumbuff), "%e", now);
strftime(yearbuff, sizeof(yearbuff), "%Y", now);
sprintf(buff, "%s %s %02d %s", daynamebuff, monbuff, now->tm_mday, yearbuff);
printf("%s\n", buff);
return 0;
}

Order of elements in strptime

I am trying to use strptime(buf, &pattern,&result) to convert char[] containing date into tm structure.
I am using function like this:
if(strptime(buf, &pattern,&result) == NULL)
{
printf("\nstrptime failed\n");
...
and everything works if my variables are defined like this:
char buf[] = "26/10/2011";
char pattern[] = "%d/%m/%y";
struct tm result;
but if I change them into:
char buf[] = "2011/26/10";
char pattern[] = "%y/%d/%m";
struct tm result;
I get "strptime failed". Notice, that I have only put year in the beginning (both in buf and pattern).
Help appreciated. My final target is to convert string in this format: 2011-10-26T08:39:21
It's because the lower case %y is for the two-digit year within the century. Try changing it to uppercase %Y and it will work okay. You can see this from the following program:
#include <stdio.h>
#include <time.h>
int main (void) {
char buf[] = "26/10/2011";
char pattern[] = "%d/%m/%y";
struct tm result;
if (strptime (buf, pattern, &result) == NULL) {
printf("strptime failed\n");
return -1;
}
printf ("%d\n", 1900 + result.tm_year);
return 0;
}
This outputs 2020, meaning that the year is being read as just the 20 portion of 2011, with the remainder being ignored. If you use upper-case %Y, it outputs the correct 2011 instead.
Code that generates the conversion error using the reversed format:
#include <stdio.h>
#include <time.h>
int main (void) {
char buf[] = "2011/10/26";
char pattern[] = "%y/%m/%d";
struct tm result;
if (strptime (buf, pattern, &result) == NULL) {
printf("strptime failed\n");
return -1;
}
printf ("%d\n", 1900 + result.tm_year);
return 0;
}
will work fine (ie, output 2011) when you change the pattern value to "%Y/%m/%d".
Using my own 'strptime' and 'timestamp' commands, I get:
$ strptime -T '%y/%d/%m' 2011/26/11
strptime: failed to convert <2011/26/11> using format <%y/%d/%m>
$ strptime -T '%Y/%d/%m' 2011/26/11
1322294400 = 2011/26/11
$ strptime -T '%d/%m/%y' 26/11/2011
1606377600 = 26/11/2011
$ timestamp 1322294400 1606377600
1322294400 = Sat Nov 26 00:00:00 2011
1606377600 = Thu Nov 26 00:00:00 2020
$
(Time zone here is US/Pacific, currently UTC-7.)
Note that the '%d/%m/%y' format generates a date in 2020, not in 2011.

is c mktime different on Windows and GNU/Linux?

the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
static const char * wday_abb_names[] =
{
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun",
};
static void mb_setenv(const char *name, const char *value)
{
#if !(defined _WIN32) || defined HAVE_SETENV
setenv(name, value, 1);
#else
int len = strlen(name)+1+strlen(value)+1;
char *str = malloc(len);
sprintf(str, "%s=%s", name, value);
putenv(str);
#endif
}
static void mb_unsetenv(const char *name)
{
#if !(defined _WIN32) || defined HAVE_SETENV
unsetenv(name);
#else
int len = strlen(name)+2;
char *str = malloc(len);
sprintf(str, "%s=", name);
putenv(str);
free(str);
#endif
}
time_t mb_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
mb_setenv("TZ", "");
tzset();
ret = mktime(tm);
if (tz)
{
mb_setenv("TZ", tz);
}
else
{
mb_unsetenv("TZ");
}
tzset();
return ret;
}
time_t get_test_time()
{
struct tm msg_time;
msg_time.tm_isdst = 0;
msg_time.tm_wday = 4;
msg_time.tm_mon = 5;
msg_time.tm_mday = 16;
msg_time.tm_hour = 4;
msg_time.tm_min = 53;
msg_time.tm_sec = 0;
msg_time.tm_year = 111; //2011 - 1900
time_t retval = mb_timegm(&msg_time);
printf("final msg_time = %ld\n", retval);
return retval;
}
void print_time(const char *msg, struct tm *t)
{
printf("%s %s, %02d.%02d.%2d %2d:%02d\n", msg,
wday_abb_names[t->tm_wday], t->tm_mday, t->tm_mon, t->tm_year,
t->tm_hour, t->tm_min);
}
int main()
{
printf( "=== ENVIRON ===\n");
printf("TZ = %s\n", getenv("TZ"));
time_t now;
struct tm l, g;
time(&now);
l = *localtime(&now);
g = *gmtime(&now);
print_time("Local time :", &l);
print_time("utc :", &g);
printf("=== END ENVIRON ===\n\n");
time_t tt = get_test_time();
printf("fix test (16.6.2011 04:53) --> %s\n", ctime(&tt));
printf("done.\n");
return 0;
}
running on GNU/Linux it produces:
=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:20
utc : Sat, 24.05.111 12:20
=== END ENVIRON ===
final msg_time = 1308199980
fix test (16.6.2011 04:53) --> Thu Jun 16 06:53:00 2011
done.
running on Win7 it produces:
=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:25
utc : Sat, 24.05.111 12:25
=== END ENVIRON ===
final msg_time = 1308196380
fix test (16.6.2011 04:53) --> Thu Jun 16 05:53:00 2011
done.
Both Systems have a Timezone of UTC+1 including DST (that makes UTC+2 in effect) and both systems are not having any time-problems at all - except for the difference displayed.
As you can see, the "final msg_time" is missing exactly 3600 seconds, so it is not a problem in ctime.
Can anybody explain to me why mktime seems to behave different on GNU/Linux and Windows - or how to correct that?
Edit:
Both systems (after calling tzset()) are reporting tzname[0] = CET, tzname[1] = CEST, daylight=1, timezone = -3600
My mb_timegm was based on the code stated in man 3 timegm and it stated
"set the TZ environment variable to UTC" to do this setenv("TZ", ""); is called.
However - this does not work on windows.
Using setenv("TZ", "UTC"); (or, in the above case mb_setenv) instead fixes the problem.
I'm assuming based on the info your provided where daylight savings time is in effect, that you would need to set msg_time.tm_isdst in get_test_time() to a value of 1 rather than 0. This may be the issue accounting for the missing hour. Either that, or you could set it to -1 and allow the system to attempt to figure out if you are in daylight savings time or not for the given input value.

Resources