1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /* The function is valid for positive nxt_time_t only. */
11 
12 void
nxt_gmtime(nxt_time_t s,struct tm * tm)13 nxt_gmtime(nxt_time_t s, struct tm *tm)
14 {
15     nxt_int_t   yday;
16     nxt_uint_t  daytime, mday, mon, year, days, leap;
17 
18     days = (nxt_uint_t) (s / 86400);
19     daytime = (nxt_uint_t) (s % 86400);
20 
21     /* January 1, 1970 was Thursday. */
22     tm->tm_wday = (4 + days) % 7;
23 
24     /* The algorithm based on Gauss' formula. */
25 
26     /* Days since March 1, 1 BCE. */
27     days = days - (31 + 28) + 719527;
28 
29     /*
30      * The "days" should be adjusted by 1 only, however some March 1st's
31      * go to previous year, so "days" are adjusted by 2.  This also shifts
32      * the last February days to the next year, but this is catched by
33      * negative "yday".
34      */
35     year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
36 
37     yday = days - (365 * year + year / 4 - year / 100 + year / 400);
38 
39     leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
40 
41     if (yday < 0) {
42         yday = 365 + leap + yday;
43         year--;
44     }
45 
46     /*
47      * An empirical formula that maps "yday" to month.
48      * There are at least 10 variants, some of them are:
49      *     mon = (yday + 31) * 15 / 459
50      *     mon = (yday + 31) * 17 / 520
51      *     mon = (yday + 31) * 20 / 612
52      */
53 
54     mon = (yday + 31) * 10 / 306;
55 
56     /* The Gauss' formula that evaluates days before month. */
57 
58     mday = yday - (367 * mon / 12 - 30) + 1;
59 
60     if (yday >= 306) {
61         year++;
62         mon -= 11;
63         yday -= 306;
64 
65     } else {
66         mon++;
67         yday += 31 + 28 + leap;
68     }
69 
70     tm->tm_mday = mday;
71     tm->tm_mon = mon;
72     tm->tm_year = year - 1900;
73     tm->tm_yday = yday;
74 
75     tm->tm_hour = daytime / 3600;
76     daytime %= 3600;
77     tm->tm_min = daytime / 60;
78     tm->tm_sec = daytime % 60;
79 }
80