mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
refactor(core): Reuse the generic DateTime decoding for XML
This commit is contained in:
parent
3ef14ba7f7
commit
28306413e0
@ -1101,132 +1101,8 @@ DECODE_XML(String) {
|
||||
DECODE_XML(DateTime) {
|
||||
CHECK_DATA_BOUNDS;
|
||||
GET_DATA_VALUE;
|
||||
|
||||
/* The last character has to be 'Z'. We can omit some length checks later on
|
||||
* because we know the atoi functions stop before the 'Z'. */
|
||||
if(length == 0 || data[length - 1] != 'Z')
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
|
||||
struct mytm dts;
|
||||
memset(&dts, 0, sizeof(dts));
|
||||
|
||||
size_t pos = 0;
|
||||
size_t len;
|
||||
|
||||
/* Parse the year. The ISO standard asks for four digits. But we accept up
|
||||
* to five with an optional plus or minus in front due to the range of the
|
||||
* DateTime 64bit integer. But in that case we require the year and the
|
||||
* month to be separated by a '-'. Otherwise we cannot know where the month
|
||||
* starts. */
|
||||
if(data[0] == '-' || data[0] == '+')
|
||||
pos++;
|
||||
UA_Int64 year = 0;
|
||||
len = parseInt64(&data[pos], 5, &year);
|
||||
pos += len;
|
||||
if(len != 4 && data[pos] != '-')
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
if(data[0] == '-')
|
||||
year = -year;
|
||||
dts.tm_year = (UA_Int16)year - 1900;
|
||||
if(data[pos] == '-')
|
||||
pos++;
|
||||
|
||||
/* Parse the month */
|
||||
UA_UInt64 month = 0;
|
||||
len = parseUInt64(&data[pos], 2, &month);
|
||||
pos += len;
|
||||
UA_CHECK(len == 2, return UA_STATUSCODE_BADDECODINGERROR);
|
||||
dts.tm_mon = (UA_UInt16)month - 1;
|
||||
if(data[pos] == '-')
|
||||
pos++;
|
||||
|
||||
/* Parse the day and check the T between date and time */
|
||||
UA_UInt64 day = 0;
|
||||
len = parseUInt64(&data[pos], 2, &day);
|
||||
pos += len;
|
||||
UA_CHECK(len == 2 || data[pos] != 'T',
|
||||
return UA_STATUSCODE_BADDECODINGERROR);
|
||||
dts.tm_mday = (UA_UInt16)day;
|
||||
pos++;
|
||||
|
||||
/* Parse the hour */
|
||||
UA_UInt64 hour = 0;
|
||||
len = parseUInt64(&data[pos], 2, &hour);
|
||||
pos += len;
|
||||
UA_CHECK(len == 2, return UA_STATUSCODE_BADDECODINGERROR);
|
||||
dts.tm_hour = (UA_UInt16)hour;
|
||||
if(data[pos] == ':')
|
||||
pos++;
|
||||
|
||||
/* Parse the minute */
|
||||
UA_UInt64 min = 0;
|
||||
len = parseUInt64(&data[pos], 2, &min);
|
||||
pos += len;
|
||||
UA_CHECK(len == 2, return UA_STATUSCODE_BADDECODINGERROR);
|
||||
dts.tm_min = (UA_UInt16)min;
|
||||
if(data[pos] == ':')
|
||||
pos++;
|
||||
|
||||
/* Parse the second */
|
||||
UA_UInt64 sec = 0;
|
||||
len = parseUInt64(&data[pos], 2, &sec);
|
||||
pos += len;
|
||||
UA_CHECK(len == 2, return UA_STATUSCODE_BADDECODINGERROR);
|
||||
dts.tm_sec = (UA_UInt16)sec;
|
||||
|
||||
/* Compute the seconds since the Unix epoch */
|
||||
long long sinceunix = __tm_to_secs(&dts);
|
||||
|
||||
/* Are we within the range that can be represented? */
|
||||
long long sinceunix_min =
|
||||
(long long)(UA_INT64_MIN / UA_DATETIME_SEC) -
|
||||
(long long)(UA_DATETIME_UNIX_EPOCH / UA_DATETIME_SEC) -
|
||||
(long long)1; /* manual correction due to rounding */
|
||||
long long sinceunix_max = (long long)
|
||||
((UA_INT64_MAX - UA_DATETIME_UNIX_EPOCH) / UA_DATETIME_SEC);
|
||||
if(sinceunix < sinceunix_min || sinceunix > sinceunix_max)
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
|
||||
/* Convert to DateTime. Add or subtract one extra second here to prevent
|
||||
* underflow/overflow. This is reverted once the fractional part has been
|
||||
* added. */
|
||||
sinceunix -= (sinceunix > 0) ? 1 : -1;
|
||||
UA_DateTime dt = (UA_DateTime)
|
||||
(sinceunix + (UA_DATETIME_UNIX_EPOCH / UA_DATETIME_SEC)) * UA_DATETIME_SEC;
|
||||
|
||||
/* Parse the fraction of the second if defined */
|
||||
if(data[pos] == ',' || data[pos] == '.') {
|
||||
pos++;
|
||||
double frac = 0.0;
|
||||
double denom = 0.1;
|
||||
while(pos < length && data[pos] >= '0' && data[pos] <= '9') {
|
||||
frac += denom * (data[pos] - '0');
|
||||
denom *= 0.1;
|
||||
pos++;
|
||||
}
|
||||
frac += 0.00000005; /* Correct rounding when converting to integer */
|
||||
dt += (UA_DateTime)(frac * UA_DATETIME_SEC);
|
||||
}
|
||||
|
||||
/* Remove the underflow/overflow protection (see above) */
|
||||
if(sinceunix > 0) {
|
||||
if(dt > UA_INT64_MAX - UA_DATETIME_SEC)
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
dt += UA_DATETIME_SEC;
|
||||
} else {
|
||||
if(dt < UA_INT64_MIN + UA_DATETIME_SEC)
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
dt -= UA_DATETIME_SEC;
|
||||
}
|
||||
|
||||
/* We must be at the end of the string (ending with 'Z' as checked above) */
|
||||
if(pos != length - 1)
|
||||
return UA_STATUSCODE_BADDECODINGERROR;
|
||||
|
||||
*dst = dt;
|
||||
|
||||
ctx->index++;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
UA_String str = {length, (UA_Byte*)(uintptr_t)data};
|
||||
return decodeDateTime(str, dst);
|
||||
}
|
||||
|
||||
static status
|
||||
|
Loading…
Reference in New Issue
Block a user