Coverage Report

Created: 2025-06-22 06:19

/src/h2o/lib/common/time.c
Line
Count
Source (jump to first uncovered line)
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
27
static char *emit_wday(char *dst, int wday)
28
135
{
29
135
    memcpy(dst, ("SunMonTueWedThuFriSat") + wday * 3, 3);
30
135
    return dst + 3;
31
135
}
32
33
static char *emit_mon(char *dst, int mon)
34
135
{
35
135
    memcpy(dst, ("JanFebMarAprMayJunJulAugSepOctNovDec") + mon * 3, 3);
36
135
    return dst + 3;
37
135
}
38
39
static char *emit_digits(char *dst, int n, size_t cnt)
40
675
{
41
675
    char *p = dst + cnt;
42
43
    /* emit digits from back */
44
1.62k
    do {
45
1.62k
        *--p = '0' + n % 10;
46
1.62k
        n /= 10;
47
1.62k
    } while (p != dst);
48
49
675
    return dst + cnt;
50
675
}
51
52
void h2o_time2str_rfc1123(char *buf, struct tm *gmt)
53
135
{
54
135
    char *p = buf;
55
56
    /* format: Fri, 19 Sep 2014 05:24:04 GMT */
57
135
    p = emit_wday(p, gmt->tm_wday);
58
135
    *p++ = ',';
59
135
    *p++ = ' ';
60
135
    p = emit_digits(p, gmt->tm_mday, 2);
61
135
    *p++ = ' ';
62
135
    p = emit_mon(p, gmt->tm_mon);
63
135
    *p++ = ' ';
64
135
    p = emit_digits(p, gmt->tm_year + 1900, 4);
65
135
    *p++ = ' ';
66
135
    p = emit_digits(p, gmt->tm_hour, 2);
67
135
    *p++ = ':';
68
135
    p = emit_digits(p, gmt->tm_min, 2);
69
135
    *p++ = ':';
70
135
    p = emit_digits(p, gmt->tm_sec, 2);
71
135
    memcpy(p, " GMT", 4);
72
135
    p += 4;
73
135
    *p = '\0';
74
75
135
    assert(p - buf == H2O_TIMESTR_RFC1123_LEN);
76
135
}
77
78
static int fetch_digits(const char *s, size_t n)
79
0
{
80
0
    int value = 0;
81
0
    for (; n != 0; ++s, --n) {
82
0
        if (!('0' <= *s && *s <= '9'))
83
0
            return -1;
84
0
        value = value * 10 + *s - '0';
85
0
    }
86
0
    return value;
87
0
}
88
89
int h2o_time_parse_rfc1123(const char *s, size_t len, struct tm *tm)
90
0
{
91
0
    if (len != H2O_TIMESTR_RFC1123_LEN)
92
0
        return -1;
93
94
    /*           1         2
95
     * 01234567890123456789012345678
96
     * Fri, 19 Sep 2014 05:24:04 GMT
97
     */
98
99
0
#define FETCH(dst, pos, n)                                                                                                         \
100
0
    if ((dst = fetch_digits(s + pos, n)) == -1)                                                                                    \
101
0
        return -1;
102
0
    FETCH(tm->tm_year, 12, 4);
103
0
    tm->tm_year -= 1900;
104
    /* month is parsed afterwards */
105
0
    FETCH(tm->tm_mday, 5, 2);
106
0
    FETCH(tm->tm_hour, 17, 2);
107
0
    FETCH(tm->tm_min, 20, 2);
108
0
    FETCH(tm->tm_sec, 23, 2);
109
0
#undef FETCH
110
111
0
#define PACK3(a, b, c) (((a) & 0xff) << 16 | ((b) & 0xff) << 8 | ((c) & 0xff))
112
0
#define MAP(c1, c2, c3, value)                                                                                                     \
113
0
    case PACK3(c1, c2, c3):                                                                                                        \
114
0
        tm->tm_mon = value;                                                                                                        \
115
0
        break
116
0
    switch (PACK3(s[8], s[9], s[10])) {
117
0
        MAP('J', 'a', 'n', 0);
118
0
        MAP('F', 'e', 'b', 1);
119
0
        MAP('M', 'a', 'r', 2);
120
0
        MAP('A', 'p', 'r', 3);
121
0
        MAP('M', 'a', 'y', 4);
122
0
        MAP('J', 'u', 'n', 5);
123
0
        MAP('J', 'u', 'l', 6);
124
0
        MAP('A', 'u', 'g', 7);
125
0
        MAP('S', 'e', 'p', 8);
126
0
        MAP('O', 'c', 't', 9);
127
0
        MAP('N', 'o', 'v', 10);
128
0
        MAP('D', 'e', 'c', 11);
129
0
    default:
130
0
        return -1;
131
0
    }
132
0
#undef MAP
133
0
#undef PACK3
134
135
0
    return 0;
136
0
}
137
138
static int calc_gmt_offset(time_t t, struct tm *local)
139
135
{
140
135
    struct tm gmt;
141
135
    int delta;
142
143
135
    gmtime_r(&t, &gmt);
144
135
    delta = (local->tm_hour - gmt.tm_hour) * 60 + (local->tm_min - gmt.tm_min);
145
146
135
    if (local->tm_yday != gmt.tm_yday) {
147
0
        int day_offset;
148
0
        if (local->tm_year == gmt.tm_year)
149
0
            day_offset = local->tm_yday - gmt.tm_yday;
150
0
        else
151
0
            day_offset = local->tm_year - gmt.tm_year;
152
0
        delta += day_offset * 24 * 60;
153
0
    }
154
135
    return delta;
155
135
}
156
157
void h2o_time2str_log(char *buf, time_t time)
158
135
{
159
135
    struct tm localt;
160
135
    localtime_r(&time, &localt);
161
135
    int gmt_off = calc_gmt_offset(time, &localt);
162
135
    int gmt_sign;
163
164
135
    if (gmt_off >= 0) {
165
135
        gmt_sign = '+';
166
135
    } else {
167
0
        gmt_off = -gmt_off;
168
0
        gmt_sign = '-';
169
0
    }
170
171
135
    int len = sprintf(buf, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d", localt.tm_mday,
172
135
                      ("Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec\0") + localt.tm_mon * 4, localt.tm_year + 1900,
173
135
                      localt.tm_hour, localt.tm_min, localt.tm_sec, gmt_sign, gmt_off / 60, gmt_off % 60);
174
135
    assert(len == H2O_TIMESTR_LOG_LEN);
175
135
}