File Coverage

bson/bson-iso8601.c
Criterion Covered Total %
statement 0 126 0.0
branch 0 104 0.0
condition n/a
subroutine n/a
pod n/a
total 0 230 0.0


line stmt bran cond sub pod time code
1             /*
2             * Copyright 2013 MongoDB, Inc.
3             *
4             * Licensed under the Apache License, Version 2.0 (the "License");
5             * you may not use this file except in compliance with the License.
6             * You may obtain a copy of the License at
7             *
8             * http://www.apache.org/licenses/LICENSE-2.0
9             *
10             * Unless required by applicable law or agreed to in writing, software
11             * distributed under the License is distributed on an "AS IS" BASIS,
12             * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13             * See the License for the specific language governing permissions and
14             * limitations under the License.
15             */
16              
17              
18             #include "bson-compat.h"
19             #include "bson-macros.h"
20             #include "bson-error.h"
21             #include "bson-iso8601-private.h"
22              
23             #ifndef _WIN32
24             # include "bson-timegm-private.h"
25             #endif
26              
27              
28             static bool
29 0           get_tok (const char *terminals,
30             const char **ptr,
31             int32_t *remaining,
32             const char **out,
33             int32_t *out_len)
34             {
35             const char *terminal;
36 0           bool found_terminal = false;
37              
38 0 0         if (!*remaining) {
39 0           *out = "";
40 0           *out_len = 0;
41             }
42              
43 0           *out = *ptr;
44 0           *out_len = -1;
45              
46 0 0         for (; *remaining && !found_terminal;
    0          
47 0           (*ptr)++, (*remaining)--, (*out_len)++) {
48 0 0         for (terminal = terminals; *terminal; terminal++) {
49 0 0         if (**ptr == *terminal) {
50 0           found_terminal = true;
51 0           break;
52             }
53             }
54             }
55              
56 0 0         if (!found_terminal) {
57 0           (*out_len)++;
58             }
59              
60 0           return found_terminal;
61             }
62              
63             static bool
64 0           digits_only (const char *str,
65             int32_t len)
66             {
67             int i;
68              
69 0 0         for (i = 0; i < len; i++) {
70 0 0         if (!isdigit(str[i])) {
71 0           return false;
72             }
73             }
74              
75 0           return true;
76             }
77              
78             static bool
79 0           parse_num (const char *str,
80             int32_t len,
81             int32_t digits,
82             int32_t min,
83             int32_t max,
84             int32_t *out)
85             {
86             int i;
87 0           int magnitude = 1;
88 0           int32_t value = 0;
89              
90 0 0         if ((digits >= 0 && len != digits) || !digits_only (str, len)) {
    0          
    0          
91 0           return false;
92             }
93              
94 0 0         for (i = 1; i <= len; i++, magnitude *= 10) {
95 0           value += (str[len - i] - '0') * magnitude;
96             }
97              
98 0 0         if (value < min || value > max) {
    0          
99 0           return false;
100             }
101              
102 0           *out = value;
103              
104 0           return true;
105             }
106              
107             bool
108 0           _bson_iso8601_date_parse (const char *str,
109             int32_t len,
110             int64_t *out)
111             {
112             const char *ptr;
113 0           int32_t remaining = len;
114              
115             const char *year_ptr;
116             const char *month_ptr;
117             const char *day_ptr;
118             const char *hour_ptr;
119             const char *min_ptr;
120             const char *sec_ptr;
121             const char *millis_ptr;
122             const char *tz_ptr;
123              
124 0           int32_t year_len = 0;
125 0           int32_t month_len = 0;
126 0           int32_t day_len = 0;
127 0           int32_t hour_len = 0;
128 0           int32_t min_len = 0;
129 0           int32_t sec_len = 0;
130 0           int32_t millis_len = 0;
131 0           int32_t tz_len = 0;
132              
133             int32_t year;
134             int32_t month;
135             int32_t day;
136             int32_t hour;
137             int32_t min;
138 0           int32_t sec = 0;
139 0           int64_t millis = 0;
140 0           int32_t tz_adjustment = 0;
141              
142             #ifdef BSON_OS_WIN32
143             SYSTEMTIME win_sys_time;
144             FILETIME win_file_time;
145             int64_t win_time_offset;
146             int64_t win_epoch_difference;
147             #else
148 0           struct tm posix_date = { 0 };
149             #endif
150              
151 0           ptr = str;
152              
153             /* we have to match at least yyyy-mm-ddThh:mm[:+-Z] */
154 0           if (!(get_tok ("-", &ptr, &remaining, &year_ptr,
155 0 0         &year_len) &&
156 0           get_tok ("-", &ptr, &remaining, &month_ptr,
157 0 0         &month_len) &&
158 0           get_tok ("T", &ptr, &remaining, &day_ptr,
159 0 0         &day_len) &&
160 0           get_tok (":", &ptr, &remaining, &hour_ptr,
161 0           &hour_len) &&
162 0           get_tok (":+-Z", &ptr, &remaining, &min_ptr, &min_len))) {
163 0           return false;
164             }
165              
166             /* if the minute has a ':' at the end look for seconds */
167 0 0         if (min_ptr[min_len] == ':') {
168 0 0         if (remaining < 2) {
169 0           return false;
170             }
171              
172 0           get_tok (".+-Z", &ptr, &remaining, &sec_ptr, &sec_len);
173              
174 0 0         if (!sec_len) {
175 0           return false;
176             }
177             }
178              
179             /* if we had a second and it is followed by a '.' look for milliseconds */
180 0 0         if (sec_len && sec_ptr[sec_len] == '.') {
    0          
181 0 0         if (remaining < 2) {
182 0           return false;
183             }
184              
185 0           get_tok ("+-Z", &ptr, &remaining, &millis_ptr, &millis_len);
186              
187 0 0         if (!millis_len) {
188 0           return false;
189             }
190             }
191              
192             /* backtrack by 1 to put ptr on the timezone */
193 0           ptr--;
194 0           remaining++;
195              
196 0           get_tok ("", &ptr, &remaining, &tz_ptr, &tz_len);
197              
198             /* we want to include the last few hours in 1969 for timezones translate
199             * across 1970 GMT. We'll check in timegm later on to make sure we're post
200             * 1970 */
201 0 0         if (!parse_num (year_ptr, year_len, 4, 1969, 9999, &year)) {
202 0           return false;
203             }
204              
205             /* values are as in struct tm */
206 0           year -= 1900;
207              
208 0 0         if (!parse_num (month_ptr, month_len, 2, 1, 12, &month)) {
209 0           return false;
210             }
211              
212             /* values are as in struct tm */
213 0           month -= 1;
214              
215 0 0         if (!parse_num (day_ptr, day_len, 2, 1, 31, &day)) {
216 0           return false;
217             }
218              
219 0 0         if (!parse_num (hour_ptr, hour_len, 2, 0, 23, &hour)) {
220 0           return false;
221             }
222              
223 0 0         if (!parse_num (min_ptr, min_len, 2, 0, 59, &min)) {
224 0           return false;
225             }
226              
227 0 0         if (sec_len && !parse_num (sec_ptr, sec_len, 2, 0, 60, &sec)) {
    0          
228 0           return false;
229             }
230              
231 0 0         if (tz_len > 0) {
232 0 0         if (tz_ptr[0] == 'Z' && tz_len == 1) {
    0          
233             /* valid */
234 0 0         } else if (tz_ptr[0] == '+' || tz_ptr[0] == '-') {
    0          
235             int32_t tz_hour;
236             int32_t tz_min;
237              
238 0 0         if (tz_len != 5 || !digits_only (tz_ptr + 1, 4)) {
    0          
239 0           return false;
240             }
241              
242 0 0         if (!parse_num (tz_ptr + 1, 2, -1, -23, 23, &tz_hour)) {
243 0           return false;
244             }
245              
246 0 0         if (!parse_num (tz_ptr + 3, 2, -1, 0, 59, &tz_min)) {
247 0           return false;
248             }
249              
250             /* we inflect the meaning of a 'positive' timezone. Those are hours
251             * we have to substract, and vice versa */
252 0           tz_adjustment =
253 0 0         (tz_ptr[0] == '-' ? 1 : -1) * ((tz_min * 60) + (tz_hour * 60 * 60));
254              
255 0 0         if (!(tz_adjustment > -86400 && tz_adjustment < 86400)) {
    0          
256 0           return false;
257             }
258             } else {
259 0           return false;
260             }
261             }
262              
263 0 0         if (millis_len > 0) {
264             int i;
265             int magnitude;
266 0           millis = 0;
267              
268 0 0         if (millis_len > 3 || !digits_only (millis_ptr, millis_len)) {
    0          
269 0           return false;
270             }
271              
272 0 0         for (i = 1, magnitude = 1; i <= millis_len; i++, magnitude *= 10) {
273 0           millis += (millis_ptr[millis_len - i] - '0') * magnitude;
274             }
275              
276 0 0         if (millis_len == 1) {
277 0           millis *= 100;
278 0 0         } else if (millis_len == 2) {
279 0           millis *= 10;
280             }
281              
282 0 0         if (millis < 0 || millis > 1000) {
    0          
283 0           return false;
284             }
285             }
286              
287             #ifdef BSON_OS_WIN32
288             win_sys_time.wMilliseconds = millis;
289             win_sys_time.wSecond = sec;
290             win_sys_time.wMinute = min;
291             win_sys_time.wHour = hour;
292             win_sys_time.wDay = day;
293             win_sys_time.wDayOfWeek = -1; /* ignored */
294             win_sys_time.wMonth = month + 1;
295             win_sys_time.wYear = year + 1900;
296              
297             /* the wDayOfWeek member of SYSTEMTIME is ignored by this function */
298             if (SystemTimeToFileTime (&win_sys_time, &win_file_time) == 0) {
299             return 0;
300             }
301              
302             /* The Windows FILETIME structure contains two parts of a 64-bit value representing the
303             * number of 100-nanosecond intervals since January 1, 1601
304             */
305             win_time_offset =
306             (((uint64_t)win_file_time.dwHighDateTime) << 32) |
307             win_file_time.dwLowDateTime;
308              
309             /* There are 11644473600 seconds between the unix epoch and the windows epoch
310             * 100-nanoseconds = milliseconds * 10000
311             */
312             win_epoch_difference = 11644473600000 * 10000;
313              
314             /* removes the diff between 1970 and 1601 */
315             win_time_offset -= win_epoch_difference;
316              
317             /* 1 milliseconds = 1000000 nanoseconds = 10000 100-nanosecond intervals */
318             millis = win_time_offset / 10000;
319             #else
320 0           posix_date.tm_sec = sec;
321 0           posix_date.tm_min = min;
322 0           posix_date.tm_hour = hour;
323 0           posix_date.tm_mday = day;
324 0           posix_date.tm_mon = month;
325 0           posix_date.tm_year = year;
326 0           posix_date.tm_wday = 0;
327 0           posix_date.tm_yday = 0;
328              
329 0           millis = (1000 * ((uint64_t)_bson_timegm (&posix_date))) + millis;
330              
331             #endif
332              
333 0           millis += tz_adjustment * 1000;
334              
335 0 0         if (millis < 0) {
336 0           return false;
337             }
338              
339 0           *out = millis;
340              
341 0           return true;
342             }