Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort an array of struct tm

I am currently creating a personal diary in C and I'd like to have the possibility to print the post sorted by date. I can extract the date using the struct tm but I don't know to sort the dates so that the most recent is on top. This is my whole function here :

void dateOrder() {
    FILE *postfile = fopen("post.txt", "r");

    int numofpost = getNumOfPost(postfile);
    int dates[numofpost];

    struct tm ptime;

    char *elt = malloc(5 * sizeof(char));
    char *dref = "Date";
    char *href = "Heure";
    char c = 'c';

    char *pseudo = malloc(20 * sizeof(char));
    int pseudolen = 0;

    rewind(postfile);

    while (!feof(postfile)) {

        fscanf(postfile, "%s", elt);

        if (strcmp(elt, dref) == 0) {
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d/%d/%d", (int)&(ptime.tm_mday), (int)&(ptime.tm_mon), (int)&(ptime.tm_year));
        }

        if (strcmp(elt, href) == 0) {
            fseek(postfile, 3, SEEK_CUR);
            fscanf(postfile, "%d:%d", (int)&(ptime.tm_hour), (int)&(ptime.tm_min));
        }

        ptime.tm_year -= 1900;
        ptime.tm_mon -= 1;
        ptime.tm_sec = 0;
        ptime.tm_isdst = -1;
        int rep = mktime(&ptime);

        if (rep != -1) {
            dates[i++] = rep;
        }
    }

    insertsort(dates, sizeof(dates)/sizeof(dates[0]));

    for (int i = 0; i < numofpost; i++) {
        c = 'c';
        rewind(postfile);

        while (!feof(postfile) && c != 24) {

            fscanf(postfile, "%s", elt);

            if (strcmp(elt, "Pseudo") == 0) {
                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%s", pseudo);
                pseudolen = strlen(pseudo);
            }

            if (strcmp(elt, dref) == 0) {

                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%d/%d/%d", (int)&(ptime.tm_mday), (int)&(ptime.tm_mon), (int)&(ptime.tm_year));
            }

            if (strcmp(elt, href) == 0) {
                fseek(postfile, 3, SEEK_CUR);
                fscanf(postfile, "%d:%d", (int)&(ptime.tm_hour), (int)&(ptime.tm_min));
            }

            ptime.tm_year -= 1900;
            ptime.tm_mon -= 1;
            ptime.tm_sec = 0;
            ptime.tm_isdst = -1;
            int mkt = mktime(&ptime);

            if (mkt == dates[i]) {
                fseek(postfile, -39, SEEK_CUR);
                fseek(postfile, -pseudolen, SEEK_CUR);

                while (c != 24) {
                    c = fgetc(postfile);

                    if (c == 24)
                        continue;

                    printf("%c", c);
                }
            }
        }
    }

    fclose(postfile);   
}

And this is struct tm :

struct tm {
   int tm_sec;         /* seconds,  range 0 to 59          */
   int tm_min;         /* minutes, range 0 to 59           */
   int tm_hour;        /* hours, range 0 to 23             */
   int tm_mday;        /* day of the month, range 1 to 31  */
   int tm_mon;         /* month, range 0 to 11             */
   int tm_year;        /* The number of years since 1900   */
   int tm_wday;        /* day of the week, range 0 to 6    */
   int tm_yday;        /* day in the year, range 0 to 365  */
   int tm_isdst;       /* daylight saving time             */
 };
like image 289
lvndry Avatar asked May 13 '26 13:05

lvndry


1 Answers

You can write a comparison function using mktime() and difftime(), and then use qsort() to sort an array of tm structs. The comparison function cmp_dates_descend() below can be used by qsort() to sort an array of dates in descending order:

#include <stdlib.h>
#include <time.h>

#define NUM_DATES  10  /* for example */

int cmp_dates_descend(const void *d1, const void *d2);

int main(void)
{
    /* ... */

    struct tm arr_dates[NUM_DATES];

    /* ... */

    size_t num_dates = sizeof arr_dates / sizeof *arr_dates;

    qsort(arr_dates, num_dates, sizeof *arr_dates, cmp_dates_descend);

    /* ... */

    return 0;
}

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm *date_1 = (struct tm *) d1;
    struct tm *date_2 = (struct tm *) d2;

    return double d = -difftime(mktime(date_1), mktime(date_2));
}

Note that this method may encounter problems for large date differences. Since difftime() returns a double (representing a time difference in seconds), the return value may not be representable in an int, which is the value returned by comparison functions used by qsort(). Where INT_MAX == 2147483647, typical of 4 byte ints, date differences of more than about 68 years will lead to overflow in the conversion from double to int, and thus undefined behavior. If such large date differences are to be handled, perhaps a custom sort function should be written.

Edit

@chqrlie has pointed out in the comments that this method could also lead to erroneous comparisons for extremely close dates (fractions of a second), since if difftime(mktime(date_1), mktime(date_2)) is less than 1 in magnitude, the value will be converted to 0 upon return, thus comparing as equal. To avoid this complication, the result of difftime() can be stored in a double and compared with 0 to determine a return value. This is a common trick with comparison functions; this also removes the previous issue with large date differences. Here is an improved comparison function:

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm *date_1 = (struct tm *) d1;
    struct tm *date_2 = (struct tm *) d2;

    double d = difftime(mktime(date_1), mktime(date_2));

    return (d < 0) - (d > 0);
}

Edit 2

To leave the array unchanged, which the comparison function is supposed to do as it gets const pointers to the array elements, we can make copies of the tm structures in the comparison function and invoke mktime() on the copies at a small cost in performance:

int cmp_dates_descend(const void *d1, const void *d2)
{
    struct tm date_1 = *(const struct tm *)d1;
    struct tm date_2 = *(const struct tm *)d2;

    double d = difftime(mktime(&date_1), mktime(&date_2));

    return (d < 0) - (d > 0);
}
like image 122
ad absurdum Avatar answered May 15 '26 10:05

ad absurdum



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!