LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-06-2010, 01:15 PM   #1
halestorm914
LQ Newbie
 
Registered: Nov 2009
Posts: 4

Rep: Reputation: 0
mktime adds hour unless extra timespec pointers declared


I have a set of applications which convert ascii (YYYY-MM-DD) style time to unix time (time_t), and vice-versa. I was getting unexpected results so I put together a little test application which converts ascii string time (from the command line) to a tm struct, back again tm -> ascii (so far, so good), then into time_t calendar representation using mktime(). Here is where it breaks down. The time returned from mktime is an hour ahead. HOWEVER, by dumb luck I found that if I declare (but don't use) not one but two pointers to a timespec struct, then mktime returns the correct time.

What is happening there?

A copy of the test code is below.

Thanks.

Code:
/* timetest.c
 * ----------
 * compile with gcc -lrt -o timetest timetest.c
 *
 * d.hale, 2010-05-05
 *
 * call with YYYYMMDDTHHMMSS (e.g. timetest 20100505T120000)
 *
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

int main(int argc, char **argv)
  {
/* !!! uncomment this line to make it report the correct time !!!
  struct timespec *p, *q;
*/
  struct timespec ts;
  time_t t;
  struct tm tm, *gmt;
  const char fm[]="%Y%m%dT%H%M%S";
  char buf[256];

  if (argc>1) {
    printf("\n    argv=%s\n", argv[1]);
    strptime(argv[1], fm, &tm);           /* convert argv string to tm struct */

    strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm);       /* and back again */
    printf("    argv=%s (converted strptime->strftime)\n",buf);

    t = mktime(&tm);                /* now convert to calendar representation */

/* !!! The incorrect time will be reported here,
   !!!                            unless the *p, *q line is uncommented above */

    printf(" argv tm=%d ==> %d-%02d-%02d %02d:%02d:%02d (dst=%d)\n", t,
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, 
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst);

    strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm);
    printf("         %s (converted mktime->strftime)\n",buf);

    gmt = gmtime(&t);                                   /* this is also wrong */

    printf("argv gmt=%d-%02d-%02d %02d:%02d:%02d\n", gmt->tm_year+1900, 
           gmt->tm_mon+1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec);

    printf(" asctime=%s\n", asctime(&tm));
    }

  clock_gettime(CLOCK_REALTIME, &ts);
  printf("timespec now = %d.%09d\n", ts.tv_sec, ts.tv_nsec);

  return 0;
  }

Last edited by halestorm914; 05-06-2010 at 02:09 PM.
 
Old 05-07-2010, 09:12 AM   #2
wje_lq
Member
 
Registered: Sep 2007
Location: Mariposa
Distribution: FreeBSD,Debian wheezy
Posts: 811

Rep: Reputation: 179Reputation: 179
You're probably not going to like this answer.

First, I was able to reproduce the problem, even when uncommenting the declarations of p and q.

The problem boils down to strptime() not filling in the tm_isdst field of the struct. After I made a few changes to your source, the following script ...
Code:
#!/bin/bash

set -o verbose
##############################################################################
cat timetest.c
##############################################################################
gcc -lrt -o timetest timetest.c
timetest 20100505T120000
... yields this result:
Code:
##############################################################################
cat timetest.c
/* timetest.c
 * ----------
 * compile with gcc -lrt -o timetest timetest.c
 *
 * d.hale, 2010-05-05
 *
 * call with YYYYMMDDTHHMMSS (e.g. timetest 20100505T120000)
 *
 */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

int main(int argc, char **argv)
  {
  struct timespec *p, *q;
  struct timespec ts;
  time_t t;
  struct tm tm, *gmt;
  const char fm[]="%Y%m%dT%H%M%S";
  char buf[256];

  if (argc>1) {
    printf("\n    argv=%s\n", argv[1]);
    strptime(argv[1], fm, &tm);           /* convert argv string to tm struct */

    strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm);       /* and back again */
    printf("    argv=%s (converted strptime->strftime)\n",buf);

    printf("There's your trouble -> %d-%02d-%02d %02d:%02d:%02d (dst=%d)\n",
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, 
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst);

    t = mktime(&tm);                /* now convert to calendar representation */

/* !!! The incorrect time will be reported here,
   !!!                            unless the *p, *q line is uncommented above */

    printf(" argv tm=%d ==> %d-%02d-%02d %02d:%02d:%02d (dst=%d)\n", t,
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, 
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst);

    strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm);
    printf("         %s (converted mktime->strftime)\n",buf);

    gmt = gmtime(&t);                                   /* this is also wrong */

    printf("argv gmt=%d-%02d-%02d %02d:%02d:%02d\n", gmt->tm_year+1900, 
           gmt->tm_mon+1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec);

    printf(" asctime=%s\n", asctime(&tm));
    }

  clock_gettime(CLOCK_REALTIME, &ts);
  printf("timespec now = %d.%09d\n", ts.tv_sec, ts.tv_nsec);

  return 0;
  }
##############################################################################
gcc -lrt -o timetest timetest.c
timetest 20100505T120000

    argv=20100505T120000
    argv=20100505T120000 (converted strptime->strftime)
There's your trouble -> 2010-05-05 12:00:00 (dst=0)
 argv tm=1273089600 ==> 2010-05-05 13:00:00 (dst=1)
         20100505T130000 (converted mktime->strftime)
argv gmt=2010-05-05 20:00:00
 asctime=Wed May  5 13:00:00 2010

timespec now = 1273241321.034025332
What to do? Well, I hope someone can come along and recommend something more constructive than the following, but I've long been dissatisfied with all but the most trivial provided time conversion functions. I just go to the data source:
Code:
/usr/share/zoneinfo
and do my own conversions as I need them. For more info:
Code:
man tzfile
 
Old 05-12-2010, 03:49 PM   #3
halestorm914
LQ Newbie
 
Registered: Nov 2009
Posts: 4

Original Poster
Rep: Reputation: 0
What we ended up doing was setting the system clock to UTC and initializing the tm struct to zero.
 
Old 12-03-2010, 11:34 AM   #4
Keyur-Mithawala
LQ Newbie
 
Registered: Dec 2010
Location: Atlanta
Distribution: Windows ;-)
Posts: 1

Rep: Reputation: Disabled
Smile Solution

Set the struct tm.tm_isdst flag to a negative value before calling the mk/gm/local set of time functions.

Let me know if this works for you!

- Keyur
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Cron job issue - every hour works, but specific hour fails lunarleviathan Linux - Newbie 6 11-20-2009 12:19 AM
LXer: GPLv3 latest draft adds extra protection LXer Syndicated Linux News 0 03-29-2007 12:31 AM
firefox 1.5 adds an extra extension to downloads walterbyrd Linux - Software 1 01-12-2006 12:53 PM
change clock from 24 hour to 12 hour in suse 9.2/KDE 3.3 jmlumpkin Linux - Newbie 1 01-22-2005 11:45 PM
mktime() returns -1 nodger Programming 2 01-27-2004 04:38 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:53 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration