summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPauli <paul.dale@oracle.com>2017-07-24 09:10:13 +1000
committerPauli <paul.dale@oracle.com>2017-07-24 11:24:27 +1000
commit1a68e5b0d9cf502de0d6c3701bbd5c6cb1aa7b73 (patch)
treeba8a7ca2bdbc3dc738f4b171670efc5fd890dc73
parent3d0f1cb9fdd630c6c920bc97bf496538717e7705 (diff)
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 <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/3999)
-rw-r--r--crypto/asn1/a_time.c33
-rw-r--r--doc/man3/ASN1_TIME_set.pod8
-rw-r--r--test/x509_time_test.c80
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<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
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));
}