Commit 1a68e5b0 authored by Pauli's avatar Pauli
Browse files

Improve struct tm population



Using Zeller's congruence to fill the day of week field,
Also populate the day of year field.

Add unit test to cover a number of cases.

Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3999)
parent 3d0f1cb9
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -31,6 +31,38 @@ static int leap_year(const int year)
    return 0;
}

/*
 * Compute the day of the week and the day of the year from the year, month
 * and day.  The day of the year is straightforward, the day of the week uses
 * a form of Zeller's congruence.  For this months start with March and are
 * numbered 4 through 15.
 */
static void determine_days(struct tm *tm)
{
    static const int ydays[12] = {
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
    };
    int y = tm->tm_year + 1900;
    int m = tm->tm_mon;
    int d = tm->tm_mday;
    int c;

    tm->tm_yday = ydays[m] + d - 1;
    if (m >= 2) {
        /* March and onwards can be one day further into the year */
        tm->tm_yday += leap_year(y);
        m += 2;
    } else {
        /* Treat January and February as part of the previous year */
        m += 14;
        y--;
    }
    c = y / 100;
    y %= 100;
    /* Zeller's congruance */
    tm->tm_wday = (d + (13 * m) / 5 + y + y / 4 + c / 4 + 5 * c + 6) % 7;
}

int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
{
    static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
@@ -127,6 +159,7 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
            if (n > md)
                goto err;
            tmp.tm_mday = n;
            determine_days(&tmp);
            break;
        case 4:
            tmp.tm_hour = n;
+4 −4
Original line number Diff line number Diff line
@@ -55,10 +55,10 @@ an error.

ASN1_TIME_to_tm() converts the time B<s> to the standard B<tm> structure.
If B<s> is NULL, then the current time is converted. The output time is GMT.
The B<tm_sec>, B<tm_min>, B<tm_hour>, B<tm_mday>, B<tm_mon> and B<tm_year>
fields of B<tm> structure are set to proper values, whereas all other fields
are set to 0. If B<tm> is NULL this function performs a format check on B<s>
only.
The B<tm_sec>, B<tm_min>, B<tm_hour>, B<tm_mday>, B<tm_wday>, B<tm_yday>,
B<tm_mon> and B<tm_year> fields of B<tm> structure are set to proper values,
whereas all other fields are set to 0. If B<tm> is NULL this function performs
a format check on B<s> only.

ASN1_TIME_diff() sets B<*pday> and B<*psec> to the time difference between
B<from> and B<to>. If B<to> represents a time later than B<from> then
+80 −0
Original line number Diff line number Diff line
@@ -345,9 +345,89 @@ out:
    return rv;
}

static const struct {
    int y, m, d;
    int yd, wd;
} day_of_week_tests[] = {
    /*YYYY  MM  DD  DoY  DoW */
    { 1900,  1,  1,   0, 1 },
    { 1900,  2, 28,  58, 3 },
    { 1900,  3,  1,  59, 4 },
    { 1900, 12, 31, 364, 1 },
    { 1901,  1,  1,   0, 2 },
    { 1970,  1,  1,   0, 4 },
    { 1999,  1, 10,   9, 0 },
    { 1999, 12, 31, 364, 5 },
    { 2000,  1,  1,   0, 6 },
    { 2000,  2, 28,  58, 1 },
    { 2000,  2, 29,  59, 2 },
    { 2000,  3,  1,  60, 3 },
    { 2000, 12, 31, 365, 0 },
    { 2001,  1,  1,   0, 1 },
    { 2008,  1,  1,   0, 2 },
    { 2008,  2, 28,  58, 4 },
    { 2008,  2, 29,  59, 5 },
    { 2008,  3,  1,  60, 6 },
    { 2008, 12, 31, 365, 3 },
    { 2009,  1,  1,   0, 4 },
    { 2011,  1,  1,   0, 6 },
    { 2011,  2, 28,  58, 1 },
    { 2011,  3,  1,  59, 2 },
    { 2011, 12, 31, 364, 6 },
    { 2012,  1,  1,   0, 0 },
    { 2019,  1,  2,   1, 3 },
    { 2019,  2,  2,  32, 6 },
    { 2019,  3,  2,  60, 6 },
    { 2019,  4,  2,  91, 2 },
    { 2019,  5,  2, 121, 4 },
    { 2019,  6,  2, 152, 0 },
    { 2019,  7,  2, 182, 2 },
    { 2019,  8,  2, 213, 5 },
    { 2019,  9,  2, 244, 1 },
    { 2019, 10,  2, 274, 3 },
    { 2019, 11,  2, 305, 6 },
    { 2019, 12,  2, 335, 1 },
    { 2020,  1,  2,   1, 4 },
    { 2020,  2,  2,  32, 0 },
    { 2020,  3,  2,  61, 1 },
    { 2020,  4,  2,  92, 4 },
    { 2020,  5,  2, 122, 6 },
    { 2020,  6,  2, 153, 2 },
    { 2020,  7,  2, 183, 4 },
    { 2020,  8,  2, 214, 0 },
    { 2020,  9,  2, 245, 3 },
    { 2020, 10,  2, 275, 5 },
    { 2020, 11,  2, 306, 1 },
    { 2020, 12,  2, 336, 3 }
};

static int test_days(int n)
{
    char d[16];
    ASN1_TIME *a = NULL;
    struct tm t;
    int r;

    BIO_snprintf(d, sizeof(d), "%04d%02d%02d050505Z",
                 day_of_week_tests[n].y, day_of_week_tests[n].m,
                 day_of_week_tests[n].d);

    if (!TEST_ptr(a = ASN1_TIME_new()))
        return 0;

    r = TEST_true(ASN1_TIME_set_string(a, d))
        && TEST_true(ASN1_TIME_to_tm(a, &t))
        && TEST_int_eq(t.tm_yday, day_of_week_tests[n].yd)
        && TEST_int_eq(t.tm_wday, day_of_week_tests[n].wd);

    ASN1_TIME_free(a);
    return r;
}

void register_tests()
{
    ADD_TEST(test_x509_cmp_time_current);
    ADD_ALL_TESTS(test_x509_cmp_time, OSSL_NELEM(x509_cmp_tests));
    ADD_ALL_TESTS(test_x509_time, OSSL_NELEM(x509_format_tests));
    ADD_ALL_TESTS(test_days, OSSL_NELEM(day_of_week_tests));
}