1 /*
2  * Copyright (c) 2015 DeNA Co., Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20  * IN THE SOFTWARE.
21  */
22 #include <assert.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "h2o/time_.h"
26 
emit_wday(char * dst,int wday)27 static char *emit_wday(char *dst, int wday)
28 {
29     memcpy(dst, ("SunMonTueWedThuFriSat") + wday * 3, 3);
30     return dst + 3;
31 }
32 
emit_mon(char * dst,int mon)33 static char *emit_mon(char *dst, int mon)
34 {
35     memcpy(dst, ("JanFebMarAprMayJunJulAugSepOctNovDec") + mon * 3, 3);
36     return dst + 3;
37 }
38 
emit_digits(char * dst,int n,size_t cnt)39 static char *emit_digits(char *dst, int n, size_t cnt)
40 {
41     char *p = dst + cnt;
42 
43     /* emit digits from back */
44     do {
45         *--p = '0' + n % 10;
46         n /= 10;
47     } while (p != dst);
48 
49     return dst + cnt;
50 }
51 
h2o_time2str_rfc1123(char * buf,struct tm * gmt)52 void h2o_time2str_rfc1123(char *buf, struct tm *gmt)
53 {
54     char *p = buf;
55 
56     /* format: Fri, 19 Sep 2014 05:24:04 GMT */
57     p = emit_wday(p, gmt->tm_wday);
58     *p++ = ',';
59     *p++ = ' ';
60     p = emit_digits(p, gmt->tm_mday, 2);
61     *p++ = ' ';
62     p = emit_mon(p, gmt->tm_mon);
63     *p++ = ' ';
64     p = emit_digits(p, gmt->tm_year + 1900, 4);
65     *p++ = ' ';
66     p = emit_digits(p, gmt->tm_hour, 2);
67     *p++ = ':';
68     p = emit_digits(p, gmt->tm_min, 2);
69     *p++ = ':';
70     p = emit_digits(p, gmt->tm_sec, 2);
71     memcpy(p, " GMT", 4);
72     p += 4;
73     *p = '\0';
74 
75     assert(p - buf == H2O_TIMESTR_RFC1123_LEN);
76 }
77 
fetch_digits(const char * s,size_t n)78 static int fetch_digits(const char *s, size_t n)
79 {
80     int value = 0;
81     for (; n != 0; ++s, --n) {
82         if (!('0' <= *s && *s <= '9'))
83             return -1;
84         value = value * 10 + *s - '0';
85     }
86     return value;
87 }
88 
h2o_time_parse_rfc1123(const char * s,size_t len,struct tm * tm)89 int h2o_time_parse_rfc1123(const char *s, size_t len, struct tm *tm)
90 {
91     if (len != H2O_TIMESTR_RFC1123_LEN)
92         return -1;
93 
94         /*           1         2
95          * 01234567890123456789012345678
96          * Fri, 19 Sep 2014 05:24:04 GMT
97          */
98 
99 #define FETCH(dst, pos, n)                                                                                                         \
100     if ((dst = fetch_digits(s + pos, n)) == -1)                                                                                    \
101         return -1;
102     FETCH(tm->tm_year, 12, 4);
103     tm->tm_year -= 1900;
104     /* month is parsed afterwards */
105     FETCH(tm->tm_mday, 5, 2);
106     FETCH(tm->tm_hour, 17, 2);
107     FETCH(tm->tm_min, 20, 2);
108     FETCH(tm->tm_sec, 23, 2);
109 #undef FETCH
110 
111 #define PACK3(a, b, c) (((a)&0xff) << 16 | ((b)&0xff) << 8 | ((c)&0xff))
112 #define MAP(c1, c2, c3, value)                                                                                                     \
113     case PACK3(c1, c2, c3):                                                                                                        \
114         tm->tm_mon = value;                                                                                                        \
115         break
116     switch (PACK3(s[8], s[9], s[10])) {
117         MAP('J', 'a', 'n', 0);
118         MAP('F', 'e', 'b', 1);
119         MAP('M', 'a', 'r', 2);
120         MAP('A', 'p', 'r', 3);
121         MAP('M', 'a', 'y', 4);
122         MAP('J', 'u', 'n', 5);
123         MAP('J', 'u', 'l', 6);
124         MAP('A', 'u', 'g', 7);
125         MAP('S', 'e', 'p', 8);
126         MAP('O', 'c', 't', 9);
127         MAP('N', 'o', 'v', 10);
128         MAP('D', 'e', 'c', 11);
129     default:
130         return -1;
131     }
132 #undef MAP
133 #undef PACK3
134 
135     return 0;
136 }
137 
calc_gmt_offset(time_t t,struct tm * local)138 static int calc_gmt_offset(time_t t, struct tm *local)
139 {
140     struct tm gmt;
141     int delta;
142 
143     gmtime_r(&t, &gmt);
144     delta = (local->tm_hour - gmt.tm_hour) * 60 + (local->tm_min - gmt.tm_min);
145 
146     if (local->tm_yday != gmt.tm_yday) {
147         int day_offset;
148         if (local->tm_year == gmt.tm_year)
149             day_offset = local->tm_yday - gmt.tm_yday;
150         else
151             day_offset = local->tm_year - gmt.tm_year;
152         delta += day_offset * 24 * 60;
153     }
154     return delta;
155 }
156 
h2o_time2str_log(char * buf,time_t time)157 void h2o_time2str_log(char *buf, time_t time)
158 {
159     struct tm localt;
160     localtime_r(&time, &localt);
161     int gmt_off = calc_gmt_offset(time, &localt);
162     int gmt_sign;
163 
164     if (gmt_off >= 0) {
165         gmt_sign = '+';
166     } else {
167         gmt_off = -gmt_off;
168         gmt_sign = '-';
169     }
170 
171     int len = sprintf(buf, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d", localt.tm_mday,
172                       ("Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec\0") + localt.tm_mon * 4, localt.tm_year + 1900,
173                       localt.tm_hour, localt.tm_min, localt.tm_sec, gmt_sign, gmt_off / 60, gmt_off % 60);
174     assert(len == H2O_TIMESTR_LOG_LEN);
175 }
176