From 1a68e5b0d9cf502de0d6c3701bbd5c6cb1aa7b73 Mon Sep 17 00:00:00 2001 From: Pauli Date: Mon, 24 Jul 2017 09:10:13 +1000 Subject: [PATCH] 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: Rich Salz (Merged from https://github.com/openssl/openssl/pull/3999) --- crypto/asn1/a_time.c | 33 ++++++++++++++++ doc/man3/ASN1_TIME_set.pod | 8 ++-- test/x509_time_test.c | 80 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c index 6e3fade2ee..e5b5f9a052 100644 --- a/crypto/asn1/a_time.c +++ b/crypto/asn1/a_time.c @@ -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; diff --git a/doc/man3/ASN1_TIME_set.pod b/doc/man3/ASN1_TIME_set.pod index 379f28a4af..180b6c89e2 100644 --- a/doc/man3/ASN1_TIME_set.pod +++ b/doc/man3/ASN1_TIME_set.pod @@ -55,10 +55,10 @@ an error. ASN1_TIME_to_tm() converts the time B to the standard B structure. If B is NULL, then the current time is converted. The output time is GMT. -The B, B, B, B, B and B -fields of B structure are set to proper values, whereas all other fields -are set to 0. If B is NULL this function performs a format check on B -only. +The B, B, B, B, B, B, +B and B fields of B structure are set to proper values, +whereas all other fields are set to 0. If B is NULL this function performs +a format check on B only. ASN1_TIME_diff() sets B<*pday> and B<*psec> to the time difference between B and B. If B represents a time later than B then diff --git a/test/x509_time_test.c b/test/x509_time_test.c index 21f6980a49..d86312646d 100644 --- a/test/x509_time_test.c +++ b/test/x509_time_test.c @@ -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)); } -- 2.17.1