views:

1053

answers:

6

I have a Unix timestamp that I need to get the individual year, month, day, hour, minute and second values from. I never was very good in math class so I was wondering if you guys could help me out a little :)

I have to do everything myself (no time.h functions). The language is C.

+2  A: 

You really do not want to do this by hand. You could write up some simple code that assumes years, months, days, hours, minutes and seconds are all the same lengths (12 months; 28, 30, or 31 days; 24 hours; 60 minutes; and 60 seconds) and come up with the wrong answer.

To get the right answer, you have to handle leap years and leap seconds, and convert to the local time zone (with the right DST mode). (Unless you choose to only display in UTC time.)

I suggest that you have a look at the code of glibc and see how strftime works.


Edit: UNIX time does not use the leap second.

Kevin Panko
I am actually working on a platform that only supports C and comes with a few very basic libraries.
Baggins
(not homework, i'm using a timestamp to make it more ecanomical to store)
Baggins
Do you know where I can find a function that can do the conversion for me?
Baggins
Presumably your platform is not UNIX, then? In which case, why are you using a UNIX timestamp?
anon
what kind of timestamp should I be using?
Baggins
For compact storage, time_t is very good, no need to invent another one. time.h is part of standard C library so you don't even have a real C. Just copy strftime() and all its dependencies from glibc.
ZZ Coder
+1  A: 

That format is often called ISO 8601, and searching for that might help you out.

Kevin L.
+3  A: 

Disclaimer: The following code does not account for leap years or leap seconds [Unix time does not account for leap seconds. They're overrated, anyway. -Ed]. Also, I did not test it, so there may be bugs. It may kick your cat and insult your mother. Have a nice day.

Let's try a little psuedocode (Python, really):

# Define some constants here...

# You'll have to figure these out.  Don't forget about February (leap years)...
secondsPerMonth = [ SECONDS_IN_JANUARY, SECONDS_IN_FEBRUARY, ... ]

def formatTime(secondsSinceEpoch):
    # / is integer division in this case.
    # Account for leap years when you get around to it :)
    year = 1970 + secondsSinceEpoch / SECONDS_IN_YEAR
    acc = secondsSinceEpoch - year * SECONDS_IN_YEAR

    for month in range(12):
        if secondsPerMonth[month] < acc:
            acc -= month
            month += 1

    month += 1

    # Again, / is integer division.
    days = acc / SECONDS_PER_DAY
    acc -= days * SECONDS_PER_DAY

    hours = acc / SECONDS_PER_HOUR
    acc -= hours * SECONDS_PER_HOUR

    minutes = acc / SECONDS_PER_MINUTE
    acc -= minutes * SECONDS_PER_MINUTE

    seconds = acc

    return "%d-%d-%d %d:%d%d" % (year, month, day, hours, minutes, seconds)

If I goofed up, please let me know. Doing this in C shouldn't be too much harder.

Andrew Keeton
Thanks, this was just what I needed :)
Baggins
When you get past 15 reputation, you can pay me back by voting my answer up, too :)
Andrew Keeton
I don't notice mention of leap years in the above. Th C standard date handling functions are very far from trivial to implement correctly - trying to do it yourself is generally not a good idea.
anon
I'll edit the answer to make it more obvious that this isn't a whole solution, merely a starting point. I understand that reinventing the wheel is usually a bad idea, but I think we've gotten past that part.
Andrew Keeton
I understand that is not a complete solution and I will proceed as such.
Baggins
Well good luck - it is sodding difficult. A good book in this area is PJ Plauger's "The Standard C Library", which is all about implementing such library functions.
anon
Is there any reason you're doing acc - minutes * SECONDS_PER_MINUTE instead of just doing a modulus?
Joren
A: 

You don't have to do math. This can be easily handled in C like this,

char *ISO_Time (
    time_t                  time
)

{
    static char             mStr[128];
    struct tm              *gmt;

    gmt = gmtime (&time);

    strftime (mStr, sizeof(mStr), "%Y-%m-%dT%H:%M:%SZ", gmt);

    return mStr;
}

Since yours is not exactly ISO time, you just need to change one line,

strftime (mStr, sizeof(mStr), "%Y-%m-%d %H:%M:%S", gmt);
ZZ Coder
The question said "no time.h"
Kevin Panko
oh. Didn't realize I was using time.h. Then just copy strftime(). It's pretty complicated. Definitely not worth rewriting.
ZZ Coder
+1  A: 

You should have done a search - I posted a complete answer to this question over here just the other day (for UTC, at least - to adjust for other timezones just add or subtract the timezone offset in seconds from the unixtime before you call the function).

caf
+1  A: 

This is exactly what you need.

Robert L