#include "bson-compat.h"
#include "bson-macros.h"
#include "bson-error.h"
#include "bson-iso8601-private.h"
#ifndef _WIN32
# include "bson-timegm-private.h"
#endif
static
bool
get_tok (
const
char
*terminals,
const
char
**ptr,
int32_t *remaining,
const
char
**out,
int32_t *out_len)
{
const
char
*terminal;
bool
found_terminal =
false
;
if
(!*remaining) {
*out =
""
;
*out_len = 0;
}
*out = *ptr;
*out_len = -1;
for
(; *remaining && !found_terminal;
(*ptr)++, (*remaining)--, (*out_len)++) {
for
(terminal = terminals; *terminal; terminal++) {
if
(**ptr == *terminal) {
found_terminal =
true
;
break
;
}
}
}
if
(!found_terminal) {
(*out_len)++;
}
return
found_terminal;
}
static
bool
digits_only (
const
char
*str,
int32_t len)
{
int
i;
for
(i = 0; i < len; i++) {
if
(!
isdigit
(str[i])) {
return
false
;
}
}
return
true
;
}
static
bool
parse_num (
const
char
*str,
int32_t len,
int32_t digits,
int32_t min,
int32_t max,
int32_t *out)
{
int
i;
int
magnitude = 1;
int32_t value = 0;
if
((digits >= 0 && len != digits) || !digits_only (str, len)) {
return
false
;
}
for
(i = 1; i <= len; i++, magnitude *= 10) {
value += (str[len - i] -
'0'
) * magnitude;
}
if
(value < min || value > max) {
return
false
;
}
*out = value;
return
true
;
}
bool
_bson_iso8601_date_parse (
const
char
*str,
int32_t len,
int64_t *out)
{
const
char
*ptr;
int32_t remaining = len;
const
char
*year_ptr;
const
char
*month_ptr;
const
char
*day_ptr;
const
char
*hour_ptr;
const
char
*min_ptr;
const
char
*sec_ptr;
const
char
*millis_ptr;
const
char
*tz_ptr;
int32_t year_len = 0;
int32_t month_len = 0;
int32_t day_len = 0;
int32_t hour_len = 0;
int32_t min_len = 0;
int32_t sec_len = 0;
int32_t millis_len = 0;
int32_t tz_len = 0;
int32_t year;
int32_t month;
int32_t day;
int32_t hour;
int32_t min;
int32_t sec = 0;
int64_t millis = 0;
int32_t tz_adjustment = 0;
#ifdef BSON_OS_WIN32
SYSTEMTIME win_sys_time;
FILETIME win_file_time;
int64_t win_time_offset;
int64_t win_epoch_difference;
#else
struct
tm
posix_date = { 0 };
#endif
ptr = str;
if
(!(get_tok (
"-"
, &ptr, &remaining, &year_ptr,
&year_len) &&
get_tok (
"-"
, &ptr, &remaining, &month_ptr,
&month_len) &&
get_tok (
"T"
, &ptr, &remaining, &day_ptr,
&day_len) &&
get_tok (
":"
, &ptr, &remaining, &hour_ptr,
&hour_len) &&
get_tok (
":+-Z"
, &ptr, &remaining, &min_ptr, &min_len))) {
return
false
;
}
if
(min_ptr[min_len] ==
':'
) {
if
(remaining < 2) {
return
false
;
}
get_tok (
".+-Z"
, &ptr, &remaining, &sec_ptr, &sec_len);
if
(!sec_len) {
return
false
;
}
}
if
(sec_len && sec_ptr[sec_len] ==
'.'
) {
if
(remaining < 2) {
return
false
;
}
get_tok (
"+-Z"
, &ptr, &remaining, &millis_ptr, &millis_len);
if
(!millis_len) {
return
false
;
}
}
ptr--;
remaining++;
get_tok (
""
, &ptr, &remaining, &tz_ptr, &tz_len);
if
(!parse_num (year_ptr, year_len, 4, 1969, 9999, &year)) {
return
false
;
}
year -= 1900;
if
(!parse_num (month_ptr, month_len, 2, 1, 12, &month)) {
return
false
;
}
month -= 1;
if
(!parse_num (day_ptr, day_len, 2, 1, 31, &day)) {
return
false
;
}
if
(!parse_num (hour_ptr, hour_len, 2, 0, 23, &hour)) {
return
false
;
}
if
(!parse_num (min_ptr, min_len, 2, 0, 59, &min)) {
return
false
;
}
if
(sec_len && !parse_num (sec_ptr, sec_len, 2, 0, 60, &sec)) {
return
false
;
}
if
(tz_len > 0) {
if
(tz_ptr[0] ==
'Z'
&& tz_len == 1) {
}
else
if
(tz_ptr[0] ==
'+'
|| tz_ptr[0] ==
'-'
) {
int32_t tz_hour;
int32_t tz_min;
if
(tz_len != 5 || !digits_only (tz_ptr + 1, 4)) {
return
false
;
}
if
(!parse_num (tz_ptr + 1, 2, -1, -23, 23, &tz_hour)) {
return
false
;
}
if
(!parse_num (tz_ptr + 3, 2, -1, 0, 59, &tz_min)) {
return
false
;
}
tz_adjustment =
(tz_ptr[0] ==
'-'
? 1 : -1) * ((tz_min * 60) + (tz_hour * 60 * 60));
if
(!(tz_adjustment > -86400 && tz_adjustment < 86400)) {
return
false
;
}
}
else
{
return
false
;
}
}
if
(millis_len > 0) {
int
i;
int
magnitude;
millis = 0;
if
(millis_len > 3 || !digits_only (millis_ptr, millis_len)) {
return
false
;
}
for
(i = 1, magnitude = 1; i <= millis_len; i++, magnitude *= 10) {
millis += (millis_ptr[millis_len - i] -
'0'
) * magnitude;
}
if
(millis_len == 1) {
millis *= 100;
}
else
if
(millis_len == 2) {
millis *= 10;
}
if
(millis < 0 || millis > 1000) {
return
false
;
}
}
#ifdef BSON_OS_WIN32
win_sys_time.wMilliseconds = millis;
win_sys_time.wSecond = sec;
win_sys_time.wMinute = min;
win_sys_time.wHour = hour;
win_sys_time.wDay = day;
win_sys_time.wDayOfWeek = -1;
win_sys_time.wMonth = month + 1;
win_sys_time.wYear = year + 1900;
if
(SystemTimeToFileTime (&win_sys_time, &win_file_time) == 0) {
return
0;
}
win_time_offset =
(((uint64_t)win_file_time.dwHighDateTime) << 32) |
win_file_time.dwLowDateTime;
win_epoch_difference = 11644473600000 * 10000;
win_time_offset -= win_epoch_difference;
millis = win_time_offset / 10000;
#else
posix_date.tm_sec = sec;
posix_date.tm_min = min;
posix_date.tm_hour = hour;
posix_date.tm_mday = day;
posix_date.tm_mon = month;
posix_date.tm_year = year;
posix_date.tm_wday = 0;
posix_date.tm_yday = 0;
millis = (1000 * ((uint64_t)_bson_timegm (&posix_date))) + millis;
#endif
millis += tz_adjustment * 1000;
if
(millis < 0) {
return
false
;
}
*out = millis;
return
true
;
}