Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a date from file in c

Tags:

c

date

file

fgets

I am reading from a file in a program using C. in the file I have some dates each in a separate line exactly like this:

20190101
20190304
20180922

Now I want the program to read these as dates and find the difference between the current date and these dates. is it possible to convert these dates into a format which is readable by C? something like this: 2019.01.01 Currently, I read it using fgets and I am not able to convert it to the format above. This is my code:

#include <stdio.h>
#include <stdlib.h>
void FileRead(FILE *pr)
{
    char date [15];
    fgets(date,15,pr);
    printf("date : %s", date);

}
int main()
{
   char x;
   FILE *pr;
   pr=fopen("mytextfile.txt","r");
   if(pr==NULL)
   {
       printf("File can not be opened.");
       return 0;
   }
  while(scanf("%c", &x))
    {
        switch(x)
        {

        case 'v' :
            FileRead(pr);
        break;

        case 'k' :
            return 0;

        }
    }
fclose(pr);
    return 0;
}

1 Answers

The easiest way to approach the difference from now to then is to read the dates from the file and parse them into month, day and year (m, d, y). If you are not going to do a complete validation of each conversion, then a simple way to separate the 4-digit year and 2-digit month and day is to use fscanf with the appropriate field-width modifiers to limit the conversion to the required number of digits, e.g.

    while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {

Then all that is needed within the loop is to populate the struct tm with the year, month and day values (remembering to subtract 1900 from the year, and set each of the hour, minute and second members zero and the daylight savings time member to -1). A simple function can do this and return time_t after calling mktime, e.g.

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

To finish, all you need is to get the time_t values and call difftime to get the difference in seconds between the current time and time read from the file as a double value. Continuing the loop in main(), e.g.

    while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
        time_t  now = time(NULL),
                then = fill_broken_down_time (y, m, d);
        printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n", 
                n++, m, d, y, difftime (now, then));
    }

Putting it altogether, you could do something like:

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

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int y, m, d, n = 1;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fscanf (fp, "%4d%2d%2d", &y, &m, &d) == 3) {
        time_t  now = time(NULL),
                then = fill_broken_down_time (y, m, d);
        printf ("date[%d] %d/%d/%d is %.2f seconds from now.\n", 
                n++, m, d, y, difftime (now, then));
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

(note: the program expects the filename to read dates from as the first argument to the program (or the program will read from stdin by default if no argument is given))

Example Input File

$ cat dat/3dates.txt
20190101
20190304
20180922

Example Use/Output

$ ./bin/time_from_now dat/3dates.txt
date[1] 1/1/2019 is 6300212.00 seconds from now.
date[2] 3/4/2019 is 943412.00 seconds from now.
date[3] 9/22/2018 is 15030212.00 seconds from now.

Edit Per-Comment Modifying Input File Format

If your data file is actually different than the three-lines of dates you originally posted with your question and it contains heading rows before the date information, then you will need to read, identify and handle those lines before handle the date line. Since you want output in days as opposed to seconds, you simply divide the number of seconds by 86400 to obtain the time difference in days.

To read an handle heading lines, simply adjust your read to read an entire line at a time into an adequately sized buffer. Declared a constant of sufficient size to ensure your buffer will be large enough, e.g.

#define MAXC 1024u  /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv) {
...
    char buf[MAXC];     /* buffer to hold each line read from file */

Then you will simply use sscanf instead of fscanf to do the exact same parse of information from each line. If the line format does not satisfy the yyyymmdd format, you know it's not a date line - handle those lines any way you want (they are just output in the example below with the prefix "non-date line: ".

Combined with dividing the number of seconds since the time in the file with 86400 seconds per-day, your new read loop would be similar to:

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        /* if line isn't a date line, just output line as non-date line */
        if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
            printf ("non-date line: %s", buf);
            continue;
        }
        time_t  now = time(NULL),
                then = fill_broken_down_time (y, m, d);
        double secs = difftime (now, then); /* get seconds between dates */
        printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n", 
                n++, m, d, y, secs, secs / 86400.0);
    }

You say:

"I am not able to open the file"

The program expects you to provide the filename to read as the first argument to the program, otherwise the program will read from stdin by default. The means you have to provide the filename to the program, e.g.

./yourprogram your_date_file

or you have to provide that data on stdin by either piping that information to the program from the output of some other program, or simply redirecting the file as input on stdin, e.g.

some_utility_making_dates | ./yourprogram

or

./yourprogram < your_date_file

Incorporating all the changes, your program would look like:

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

#define MAXC 1024u  /* if you need a constant, #define one (or more) */

time_t fill_broken_down_time (int y, int m, int d)
{                   /* initialize struct members */
    struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d, 
                    .tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };

    return mktime(&bdt);    /* return mktime conversion to time_t */
}

int main (int argc, char **argv) {
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    int y, m, d, n = 1;
    char buf[MAXC];     /* buffer to hold each line read from file */

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line in file */
        /* if line isn't a date line, just output line as non-date line */
        if (sscanf (buf, "%4d%2d%2d", &y, &m, &d) != 3) {
            printf ("non-date line: %s", buf);
            continue;
        }
        time_t  now = time(NULL),
                then = fill_broken_down_time (y, m, d);
        double secs = difftime (now, then); /* get seconds between dates */
        printf ("date[%d] %02d/%02d/%04d is %11.2f sec (%g days) from now.\n", 
                n++, m, d, y, secs, secs / 86400.0);
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

Example Input File w/Headings

$ cat dat/3dates-w-headers.txt
This file contains dates to read and convert to days.
The file also contains this description and dates in the format:

yyyymmdd
20190101
20190304
20180922

Example Use/Output

$ ./bin/time_from_now2 dat/3dates-w-headers.txt
non-date line: This file contains dates to read and convert to days.
non-date line: The file also contains this description and dates in the format:
non-date line:
non-date line: yyyymmdd
date[1] 01/01/2019 is  6348645.00 sec (73.4797 days) from now.
date[2] 03/04/2019 is   991845.00 sec (11.4797 days) from now.
date[3] 09/22/2018 is 15078645.00 sec (174.521 days) from now.

Look things over and let me know if you have further questions.

like image 200
David C. Rankin Avatar answered Dec 23 '25 10:12

David C. Rankin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!