You are not logged in.

#1 2009-08-29 01:01:21

spupy
Member
Registered: 2009-08-12
Posts: 218

[SOLVED] Format time_t to show relative day names

Hi. I'm trying to modify a program's method that formats a time_t variable. Right now the method prints the time in format "HH:MM:SS DD Month YYYY". I want to keep this format, but if the date is today or yesterday, show "today" or "yesterday" instead of the full date (the time format doesn't change).
Unfortunately, my C is on a very basic level, and this task is beyond my skills. After googling a little, the only idea I came up with is very crude - split the formatter string (strtok with " " as delimiter), compare the last 3 tokens with other strings strftime'd from the current time, compare the dates, and in case of 'today' or 'yesterday', append 'today' or 'yesterday' to the first token (the time part).
I'm not requesting code (although it would be very helpful big_smile). I'd like to know if this is best way to tackle this, or is there an easier way?
This is the code (from support.c from rox-filer):

/* Format this time nicely.
 * g_free() the result.
 */
char *pretty_time(const time_t *time)
{
    char time_buf[32];
    struct tm *tms;

    if (time == NULL)
        return g_strdup("(null)");

    tms = localtime(time);
    if (tms == NULL)
        return g_strdup("(invalid time)");

    if (strftime(time_buf, sizeof(time_buf), TIME_FORMAT, tms) == 0)
        time_buf[0]= 0;

    return to_utf8(time_buf);
}

EDIT: I looked at the tm struct. It looks like it would be easier to compare the fields (is it what they are called?) of this struct, and then do the concatenation of the time with either the date or 'yesterday'/'today'.

Last edited by spupy (2009-08-30 16:05:19)


There are two types of people in this world - those who can count to 10 by using their fingers, and those who can count to 1023.

Offline

#2 2009-08-29 14:48:36

Cerebral
Forum Fellow
From: Waterloo, ON, CA
Registered: 2005-04-08
Posts: 3,108
Website

Re: [SOLVED] Format time_t to show relative day names

I think it might be easier to create an alternative TIME_FORMAT string for today/yesterday, then do some comparisons to figure out which string to use.

Something like (pseudo-code):

int retcode = -1;
if ( DATE_IS_TODAY)
    retcode = strftime(time_buf, sizeof(time_buf), TIME_FORMAT_TODAY, tms)
else if (DATE_IS_YESTERDAY)
    retcode = strftime(time_buf, sizeof(time_buf), TIME_FORMAT_YESTERDAY, tms)
else
    retcode = strftime(time_buf, sizeof(time_buf), TIME_FORMAT, tms)

if (retcode == 0)
    time_buf[0] = 0;

Where TIME_FORMAT_* is the same as TIME_FORMAT but uses 'today' or 'yesterday' instead of the symbol for the date, and DATE_IS_* is however you figure out whether it's today or yesterday.

Offline

#3 2009-08-30 10:49:26

spupy
Member
Registered: 2009-08-12
Posts: 218

Re: [SOLVED] Format time_t to show relative day names

This is the new code I hacked somehow together so it at least compiles.

char *pretty_time(const time_t *ttime)
{
    char time_buf[32];
    struct tm *tms;

    if (ttime == NULL)
        return g_strdup("(null)");

    tms = localtime(ttime);
    if (tms == NULL)
        return g_strdup("(invalid time)");

    if (strftime(time_buf, sizeof(time_buf), TIME_DATE_FORMAT, tms) == 0)
        time_buf[0]= 0;

    //MOD--------------
    time_t today_t = time(NULL);
    time_t yesterday_t = today_t - 24*60*60;
    struct tm *today_tm = localtime(&today_t);
    struct tm *yesterday_tm = localtime(&yesterday_t);
    if(tms->tm_year==today_tm->tm_year && tms->tm_yday==today_tm->tm_yday){
        // is today
        char buf[32];
        strftime(buf, sizeof(buf), TIME_TIME_FORMAT, tms);
        strcat(buf, " Today");
        return to_utf8(buf);
    }
    if(tms->tm_year==yesterday_tm->tm_year && tms->tm_yday==yesterday_tm->tm_yday){
        // is yesterday
        char buf[32];
        strftime(buf, sizeof(buf), TIME_TIME_FORMAT, tms);
        strcat(buf, " Yesterday");
        return to_utf8(buf);
    }
    //-------------------
    return to_utf8(time_buf);
}

TIME_DATE_FORMAT shows only the date, while TIME_TIME_FORMAT shows only the time (HH:MM:SS).
I first create two time_t for today and yesterday. From them I create tm structs, and compare the year and day-of-the-year fields of the today,yesterday and actual date structs. If I find a match for today or yesterday, I format the actual time and append "Today" or "Yesterday" appropriately. Otherwise the full time+date is returned.
This method is used by the rox file manager to print the last-modified date field of a file. This code I added compiles, but unfortunately it doesn't work. All the files in a folder have the same time with "Today" appended. Could this be some weird problem with pointers?


There are two types of people in this world - those who can count to 10 by using their fingers, and those who can count to 1023.

Offline

#4 2009-08-30 14:49:15

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 990

Re: [SOLVED] Format time_t to show relative day names

The pointer returned by localtime is static.  That means, roughly speaking, that it exists for the lifetime of the program and that the code in localtime uses that memory (the *same* memory) for every call.  So your second call to localtime() overwrites the first call to localtime(), and all the pointers obtained in this manner point to the same object.  Testing them against one another is vacuously true.

You need to copy the information in each struct tm so that it will be available after the memory originally written to is overwritten.  You can do this easily by making today_t and yesterday_t each a "struct tm" instead of "pointer to struct tm" and derefencing the result of localtime when passing it in:

time_t now = time(NULL);
struct tm *tms;
struct tm *today = localtime(&now); /* BAD:  won't preserve values */
struct tm today = *localtime(&now); /* OK */
tms = localtime(ttime);

At least, that's the idea.  I'm working on some code, but it's not really functional right now.  Anyway, here's hoping it helps you.

Offline

#5 2009-08-30 15:38:34

spupy
Member
Registered: 2009-08-12
Posts: 218

Re: [SOLVED] Format time_t to show relative day names

Trent wrote:

The pointer returned by localtime is static.  That means, roughly speaking, that it exists for the lifetime of the program and that the code in localtime uses that memory (the *same* memory) for every call.  So your second call to localtime() overwrites the first call to localtime(), and all the pointers obtained in this manner point to the same object.  Testing them against one another is vacuously true.

You need to copy the information in each struct tm so that it will be available after the memory originally written to is overwritten.  You can do this easily by making today_t and yesterday_t each a "struct tm" instead of "pointer to struct tm" and derefencing the result of localtime when passing it in:

time_t now = time(NULL);
struct tm *tms;
struct tm *today = localtime(&now); /* BAD:  won't preserve values */
struct tm today = *localtime(&now); /* OK */
tms = localtime(ttime);

Thanks for your idea, I will try it.

Last edited by spupy (2009-08-30 15:46:10)


There are two types of people in this world - those who can count to 10 by using their fingers, and those who can count to 1023.

Offline

#6 2009-08-30 16:04:15

spupy
Member
Registered: 2009-08-12
Posts: 218

Re: [SOLVED] Format time_t to show relative day names

Thank you, Trent. Combining your tips with the ones I got at another forums, this is the code that does what I need:

char *pretty_time(const time_t *ttime)
{
    char time_buf[32];
    struct tm *tms;

    if (ttime == NULL)
        return g_strdup("(null)");

    tms = localtime(ttime);
    if (tms == NULL)
        return g_strdup("(invalid time)");

    if (strftime(time_buf, sizeof(time_buf), TIME_FORMAT, tms) == 0)
        time_buf[0]= 0;

    //MOD--------------
    time_t today_t = time(NULL);
    time_t yesterday_t = today_t - 24*60*60;
    struct tm today_tm;
    struct tm yesterday_tm;
    localtime_r(&today_t, &today_tm);
    localtime_r(&yesterday_t, &yesterday_tm);
    if(tms->tm_year==today_tm.tm_year && tms->tm_yday==today_tm.tm_yday){
        // is today
        char buf[32];
        strftime(buf, sizeof(buf), TIME_FORMAT_TODAY, tms);
        return to_utf8(buf);
    }
    if(tms->tm_year==yesterday_tm.tm_year && tms->tm_yday==yesterday_tm.tm_yday){
        // is yesterday
        char buf[32];
        strftime(buf, sizeof(buf), TIME_FORMAT_YESTERDAY, tms);
        return to_utf8(buf);
    }
    //-------------------
    return to_utf8(time_buf);
}

Everyone, thank you for your help. smile


There are two types of people in this world - those who can count to 10 by using their fingers, and those who can count to 1023.

Offline

Board footer

Powered by FluxBB