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