Hi all,

I am a newbie to C programming world. In my program I want to log information into a log file whose file name should be filename_date_time

I am using following code to write information but need help in changing the file name from filename to filename_date_time.

#include <stdio.h>
#include<string.h>

int main() 
{
  /* declare a FILE pointer */
  FILE *file; 

  /* open a text file for writing */
  file = fopen("data/filename.log", "w"); 

   fprintf(file, "I am the new file with date and time.");

    fclose(file);
    return 0;  
}

Please help me to resolve this issue.

Thanks,
Scott.

Recommended Answers

All 12 Replies

use the functions in time.h to get a struct tm, then use sprintf() to format the filename

char filename[255];
struct tm* tm;
time_t now;
now = time(0); // get current time
tm = localtime(&now); // get structure
sprintf(filename, "file_%02d%02d%04d", tm->tm_mon+1,
    tm->tm_day, tm->tm_year+1900);

I'm partial to the use of "strftime()"

#include <time.h>
#include <stdio.h>
#include <string.h>

void timestamp_string (char * myString)
{
    char     timestamp[16];
    time_t    caltime;
    struct tm * broketime;

    // find current time, convert to broken-down time
    time(&caltime);
    broketime = localtime(&caltime);

    // append timestamp in the format "_yymmdd_hhmmss"
    strftime(timestamp,16,"_%y%m%d_%H%M%S",broketime);
    strcat(myString,timestamp);

    return;
}

int main()
{
    char name[128];
    int i;
    
    printf("\nenter string: ");
    
    fgets(name,100,stdin);  // get string, strip newline
    do { 
        if (name[i]==0x0D || name[i]==0x0A)
        {
            name[i] = 0;
            break;
        }
    } while(name[++i] != 0x00);
    
    timestamp_string(name);
    printf("timestamped : %s\n", name);
    
    return 0;
}

OMG... i forgot to initialize "i"

line 25: int i = 0; and a more astute programmer than me reminded me that I ought to flush the output buffer. so also add for completeness:

line 28: fflush(stdout); my programming muse also pointed out that my hardcoding the sizes in "fgets" and "strftime" is also a little weak, a preferred method would be to use sizeof(.. .

and that my use of 0x0D and 0x0A instead of '\n' and '\r' is also short-sighted.

le sigh.


.

>le sigh.
:D

It seems you've suffered enough for that bit of code, so I'll restrain myself.

>le sigh.
:D

It seems you've suffered enough for that bit of code, so I'll restrain myself.

Must you? Could we encourage you to change your mind? For the good of the community, please? :D

jephthah, don't worry I'll try to bandage you up afterward. :P

getting alternately beat up then nursed by two daniweb gals?

where do i sign up?


:P

Meh, it's your call.

Let's start with some design decisions:

>void timestamp_string (char * myString)
The first thing I notice is the inconvenient notation caused by returning void. You can take a trick from the standard library and return the result string as well as let it just fall out of being a pointer parameter:

char *timestamp_string ( char *myString );

/* ... */

printf ( "Timestamped: %s\n", timestamp_string ( name ) );

That's cleaner in my opinion. The function name is also redundant. Obviously it's working with a string, so you could break it down to just timestamp, or perhaps, append_timestamp. However, when I think of a timestamp, I think of a prefix rather than a suffix. It's both easier to see in a string and easier to sort on when the timestamps is the first thing[1].

Let's do that magic and take another look:

char *prepend_timestamp ( char *s )
{
    const char fmt[] = "[%y-%m-%d %H:%M:%S]";
    const size_t n = sizeof ( fmt );
    const time_t init = time ( NULL );

    memmove ( s + n, s, strlen ( s ) + 1 );
    strftime ( s, n, fmt, localtime ( &init ) );
    s[n - 1] = ' ';

    return s;
}

The first thing that raises a flag for me is the lack of error handling. It's not possible with the current interface to verify that adding the timestamp won't overflow your string. The interface needs to change, perhaps by taking a capacity as a parameter so we can fiddle with numbers to see if everything is kosher:

#include <errno.h>

char *prepend_timestamp ( char s[], size_t capacity )
{
    const char fmt[] = "[%y-%m-%d %H:%M:%S]";
    size_t n = sizeof ( fmt );
    size_t len = strlen ( s );

    if ( len + n < capacity ) {
        time_t init = time ( NULL );

        memmove ( s + n, s, len + 1 );
        strftime ( s, n, fmt, localtime ( &init ) );
        s[n - 1] = ' ';
    }
    else
        errno = ERANGE;

    return s;
}

ERANGE isn't a perfect fit, but it's informative enough to be more useful than no error handling at all. It also saves me the trouble of explaining how to write a complete error handling framework for a simple example. ;)

errno = 0;
prepend_timestamp ( name, sizeof name );

if ( errno != ERANGE )
    printf ( "Timestamped: %s\n", name );
else {
    /* Handle the error */
}

And of course because I'm being excessively clever in prepend_timestamp, comments are justified:

char *prepend_timestamp ( char s[], size_t capacity )
{
    const char fmt[] = "[%y-%m-%d %H:%M:%S]";

    /*
        If you use different specifiers, this calculation may need to 
        change. 2 char specifier == 2 char replacement value presently
    */
    size_t n = sizeof ( fmt );
    size_t len = strlen ( s );

    /*
        The value of n includes a null character, which will be
        replaced by a single whitespace after adding the timestamp
    */
    if ( len + n < capacity ) {
        time_t init = time ( NULL );

        /*
            Avoid a temporary buffer by shifting 
            and filling in the original string
        */
        memmove ( s + n, s, len + 1 );
        strftime ( s, n, fmt, localtime ( &init ) );

        /*
            Replace the null character that strftime 
            appends to create a single string again
        */
        s[n - 1] = ' ';
    }
    else
        errno = ERANGE;

    return s;
}

>// find current time, convert to broken-down time
Don't forget that this is the C forum. Single line comments are a C++ creation and weren't adopted into C until C99. However, because C99 still isn't widely implemented and used, we still assume C89 (or to be more precise, C95) in this forum.

>time(&caltime);
This is complete speculation, but based on common usage, I think the alternative syntax is more conventional and would be more readily recognized by C (and C++) programmers for what it is:

caltime = time ( NULL );

>return;
This may be your style, but I think it's redundant. Redundant code should be removed in favor of meaty goodness. You'll find that the difference between the code of a beginner and the code of an experienced C programmer is often that the experienced programmer's code is only as verbose as necessary. Any unnecessary fluff is stripped away.

>int main()
Once again, may be your style, but for absolute consistency with post-K&R function declarations, you should prefer a void parameter list:

int main ( void )

Not doing so is sending the wrong message to readers of your code who aren't familiar with the obscure function declaration/definition rules when it comes to an empty parameter list. You might even confuse yourself and accidentally do something like this:

int foo(); /* Uh oh, probably not what you want */

>fgets(name,100,stdin);
If I must beat it into your head with my Board of Education (a nice piece of 2x4 lumber), you will check return values for your input functions:

if ( fgets ( name, sizeof name, stdin ) != NULL ) {
    /* Do good stuff */
}
else {
    /* Handle an error */
}

>do { ... } while(name[++i] != 0x00);
That's probably the most verbose method I've seen to date for stripping a newline character after fgets. You do know about strchr, right? Or strtok, or strcspn, or whatever else comes to mind that's shorter and more conventional? I always liked this one because of the one-liner yummyness:

name[strcspn ( name, "\n" )] = '\0';

For the most part I'm just nitpicking. Aside from some awkward design choices and lack of error handling, the code is pretty good. Imagine how long my post would be if it weren't. :D

[1] This is also why I prefer yyyy-mm-dd in my dates. The result is a much more convenient key for sorting.

commented: man, am i lucky, or what? people pay cash money for that. thanks! :) +6

I'm partial to the use of "strftime()"

And I did all that in only three lines of executable code ;)

Thank you all for the information provided.

- Scott.

And I did all that in only three lines of executable code

aha... but mine was fully compilable.... er, well.... for some people, probably :P

For the most part I'm just nitpicking. Aside from some awkward design choices and lack of error handling,..

thanks, narue. a good lesson was worth sticking my neck out for a beating. you might just turn me into a professional, yet.

i'm going to go present my injuries to Aia now, and soak up some sympathy.


.

>i'm going to go present my injuries to Aia now, and soak up some sympathy.

This is what we say to our little children in my land of origin when they come with a booboo.
"sana sana colita de rana, si no sana hoy sanará mañana"
It literally means
Heal, heal, little frog's tail, if not today it will heal tomorrow.
But with a better rhyme.

commented: :) +6

ahh. i feel better already :)

commented: This thread made me laugh :) +16
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.