Thread: Date Math

  1. #1
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70

    Date Math

    Is there any way to do date math using standard C libraries? I looked around in the time.h but didn't see what I needed.

    What I need to do is be able to add a certain number of minutes to a date and have it give the current date/time. For example, add 15918765 minutes to 01/01/1980 00:00 and have it tell me 04/07/2010 4:45PM. I really don't want to write this myself or go platform-specific. Does this exist somewhere or am I SOL?

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    It's possible. The C time and date library has lots of functions to help you get the right time with all sorts of information. Look at all the components of this snippet:
    Code:
    #include <time.h>
    #include <stdio.h>
    
    int main(void)
    {
        time_t someTimeSinceEpoch;
        struct tm someTime;
        someTime.tm_mday = 1;
        someTime.tm_mon = 0;
        someTime.tm_year = 1980 - 1900; /* years since 1900 */
        someTime.tm_min = 15918765;
        someTime.tm_hour = 0;
        someTime.tm_sec = 0;
        someTimeSinceEpoch = mktime( &someTime );
        if (someTimeSinceEpoch != (time_t)-1) 
            puts( ctime( &someTimeSinceEpoch ) );
        return 0;
    }
    C code - 18 lines - codepad

    ctime(3) - Linux man page
    Last edited by whiteflags; 12-22-2013 at 04:48 PM. Reason: I forgot that the range of tm_mon is [0, 11] sorry

  3. #3
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    I didn't know you could just put out of range values and it'd be ok. That's exactly what I needed, thank you.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Sorin View Post
    I didn't know you could just put out of range values and it'd be ok. That's exactly what I needed, thank you.
    You do need to be a little careful. The standard specifies that members of struct tm are of type int. Overflow ints (as opposed to moving outside the defined ranges for struct tm members), then the behaviour is undefined. But since conversion of minutes to hours involves dividing by 60, it is easy to fix (with a loop if necessary).
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  5. #5
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    Quote Originally Posted by grumpy View Post
    You do need to be a little careful. The standard specifies that members of struct tm are of type int. Overflow ints (as opposed to moving outside the defined ranges for struct tm members), then the behaviour is undefined. But since conversion of minutes to hours involves dividing by 60, it is easy to fix (with a loop if necessary).
    Overflow shouldn't be a problem since the figure will never been bigger than the 10s of millions. One curiosity though is that it's translating times that are two minutes apart as being 1 hour and 2 minutes apart.

    With both ctime and strftime, 14670330 is coming out as being 11/22/07 430pm and 14670332 is coming out as 532pm on the same day. The latter is correct, but the former should be 530pm. Nothing changed, the numbers were ran through the function back to back. I guess I'll check to see if the dst flag was automatically set at some point when I get home.

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Sorin View Post
    Overflow shouldn't be a problem since the figure will never been bigger than the 10s of millions.
    The standard only guarantees that an int can represent values between -32767 and 32767. A figure in the "10s of millions" will overflow that. A fair few compilers support an int type with a larger range, but others do not.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  7. #7
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    Quote Originally Posted by grumpy View Post
    The standard only guarantees that an int can represent values between -32767 and 32767. A figure in the "10s of millions" will overflow that. A fair few compilers support an int type with a larger range, but others do not.
    I was thinking normal int, not short int. I best not share my thoughts on that. I guess I'll try scaling it down so everything is within the proper int type ranges and see what happens. That would be one hell of a coincidence if there was an overflow issue right in the middle of those numbers.

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Just to let you know, I can't reproduce that problem here. Although, I am pretty much just editing the snippet I gave you, which means that I always assume a start date of 1/1/1980. That, and now I'm doing arithmetic on a calculator before filling in the struct, squeezing every minute of time into 16 bit numbers, just because of grumpy's comment. (I'm using a 32-bit compiler at home as well so I shouldn't need to worry, either, but some people find nits everywhere.)

  9. #9
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Your "off by an hour" problem is probably because you haven't set tm_isdst. Try setting it to -1.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  10. #10
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    Code:
    File start   :    128 (0x0080)
    File size    :  18144 bytes
    File name    : DR_FORCE_00
    File comment : Gongos2931
    File minutes : 15918765
    Save date    : Wed Apr 07 16:45:00 2010 //ctime()
    Save date    : Wednesday April 07, 2010; 04:45PM //strftime()
    First block #: 3
    Last block  #: 315
    Gross Blocks : 313
    Net Blocks   : 284
    
    File start   :  20224 (0x4F00)
    File size    :    148 bytes
    File name    : OASIS2__SYS
    File comment : Config.
    File minutes : 14670330
    Save date    : Thu Nov 22 16:30:00 2007 //ctime()
    Save date    : Thursday November 22, 2007; 04:30PM //strftime()
    First block #: 317
    Last block  #: 319
    Gross Blocks : 3
    Net Blocks   : 3
    
    File start   :  20480 (0x5000)
    File size    :    696 bytes
    File name    : OASIS2_SVD0
    File comment : PLAY DATA
    File minutes : 14670332
    Save date    : Thu Nov 22 17:32:00 2007 //ctime()
    Save date    : Thursday November 22, 2007; 05:32PM //strftime()
    First block #: 321
    Last block  #: 332
    Gross Blocks : 12
    Net Blocks   : 12
    File minutes is the figure being used. The two Save date lines are the resulting output.

    This is the structure in the time.h file on my system.

    Code:
    /*
     * A structure for storing all kinds of useful information about the
     * current (or another) time.
     */
    struct tm
    {
    	int	tm_sec;		/* Seconds: 0-59 (K&R says 0-61?) */
    	int	tm_min;		/* Minutes: 0-59 */
    	int	tm_hour;	/* Hours since midnight: 0-23 */
    	int	tm_mday;	/* Day of the month: 1-31 */
    	int	tm_mon;		/* Months *since* january: 0-11 */
    	int	tm_year;	/* Years since 1900 */
    	int	tm_wday;	/* Days since Sunday (0-6) */
    	int	tm_yday;	/* Days since Jan. 1: 0-365 */
    	int	tm_isdst;	/* +1 Daylight Savings Time, 0 No DST,
    				 * -1 don't know */
    };
    This is the function being used.

    Code:
    time_t gameSaveDate(const int fileMinutes)
    {
        struct tm segaEpoch;
    
        segaEpoch.tm_sec  = 0;
        segaEpoch.tm_min  = fileMinutes;
        segaEpoch.tm_hour = 0;
        segaEpoch.tm_mday = 1;
        segaEpoch.tm_mon  = 0;
        segaEpoch.tm_year = 80;
    
        return mktime(&segaEpoch);
    }
    Using the debugger, I'm seeing that the values being returned by mktime are 62 minutes worth of seconds apart.

    Am I doing something wrong somewhere?

    Quote Originally Posted by oogabooga View Post
    Your "off by an hour" problem is probably because you haven't set tm_isdst. Try setting it to -1.
    I just set it to -1 right now to see what would happen, and the time is correct now. But I'm confused because why would that matter with back-to-back function calls for times that are 2 minutes apart on the same day? It's correct for the first one, wrong for the second one, and correct for the third one. That boggles me.
    Last edited by Sorin; 12-22-2013 at 10:43 PM.

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Sorin View Post
    I just set it to -1 right now to see what would happen, and the time is correct now. But I'm confused because why would that matter with back-to-back function calls for times that are 2 minutes apart on the same day? It's correct for the first one, wrong for the second one, and correct for the third one. That boggles me.
    If you didn't set it to anything in particular, then why would you assume that three different times you'd happen to get the same number out of it?

  12. #12
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Sorin View Post
    I was thinking normal int, not short int. I best not share my thoughts on that.
    I agree - probably best not to share your thoughts, because you will only exhibit your ignorance.

    The int type is - notionally - the native integral type supported by the host - in other words what people mean when they say "normal int". In fact, the int type is not required by the standard to support a larger range of values than a short (i.e. the range -32767 to 32767). It is permitted for int to support a larger range, but it is not required.

    Yes, a number of modern (and not so modern) compilers support a larger range. The standard permits that, so those compilers are compliant. The fact that those compilers do so does not supersede the standard, nor does it render the standard incorrect. The standard is the basis for assessing correctness of compilers (or, more generally, implementations) not the reverse. That is the purpose of the standard.

    As to your other problem, accessing the value of an uninitialised variable (or an uninitialised struct member) causes undefined behaviour. Odds are, the mktime() will access all members of the supplied struct tm.
    Last edited by grumpy; 12-23-2013 at 01:04 AM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  13. #13
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    I appreciate the assistance guys, thank you.

  14. #14
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by grumpy View Post
    As to your other problem, accessing the value of an uninitialised variable (or an uninitialised struct member) causes undefined behaviour. Odds are, the mktime() will access all members of the supplied struct tm.
    All fields but tm_wday and tm_yday need to be set before mktime is called, but the fields do not have to be within their usual limits (e.g., tm_min can be greater than 59).
    After it returns, not only will tm_wday and tm_yday be set, but the other fields will be adjusted to be within range.
    Code:
    struct tm *
    add_minutes(int year, int mon, int mday,
                int hour, int min, long mins_to_add) {
        static struct tm tm;
        tm.tm_year = year - 1900;
        tm.tm_mon  = mon  - 1;
        tm.tm_mday = mday + mins_to_add / 1440;
        mins_to_add %= 1440;
        tm.tm_hour = hour + mins_to_add / 60;
        tm.tm_min  = min  + mins_to_add % 60;
        tm.tm_sec  = 0;
        tm.tm_isdst = -1; // impl. should detect and set dst
        mktime(&tm);
        return &tm;
    }
    Note that the above returns a static local, with all that implies.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C Program that output the very next date after Input a date
    By rumel.ehsan in forum C Programming
    Replies: 7
    Last Post: 03-23-2013, 11:53 AM
  2. Replies: 2
    Last Post: 05-07-2012, 07:37 AM
  3. Replies: 10
    Last Post: 03-28-2012, 10:30 AM
  4. Replies: 11
    Last Post: 03-27-2012, 11:37 PM
  5. Two date objects showing same date?
    By dxfoo in forum C++ Programming
    Replies: 7
    Last Post: 06-17-2010, 06:06 PM