8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:43:03 +01:00

Fixed CORE-6427 and CORE-6429:

- CORE-6427 - Whitespace as date separator causes conversion error.
- CORE-6429 - Timezone offset in timestamp/time literal and CAST should follow SQL standard syntax only.
This commit is contained in:
Adriano dos Santos Fernandes 2020-10-26 12:30:21 -03:00
parent 7b0a29d0bc
commit ff37d445ce
5 changed files with 48 additions and 44 deletions

View File

@ -21,10 +21,10 @@ a routine that changes the current time zone and later run `SET TIME ZONE LOCAL`
to its initially received value.
A time zone may be a string with a time zone region (for example, `America/Sao_Paulo`) or a hours:minutes displacement
(for example, `-03:00` or `+3`) from GMT.
(for example, `-03:00` or `+3:0`) from GMT.
A time/timestamp with time zone is considered equal to another time/timestamp with time zone if their conversion to UTC
are equal, for example, `time '10:00 -02' = time '09:00 -03'`, since both are the same as `time '12:00 GMT'`.
are equal, for example, `time '10:00 -02:00' = time '09:00 -03:00'`, since both are the same as `time '12:00 GMT'`.
This is also valid in the context of `UNIQUE` constraints and for sorting purposes.
Some timestamps does not exist (DST starting) or repeats twice (DST ending). For the first case, when DST starts
@ -85,8 +85,8 @@ The time/timestamp parts are stored in UTC (translated from the informed time zo
Time zone identifiers (from regions) are put directly in the time_zone field.
They start from 65535 (which is the GMT code) and are decreasing as new time zones were/are added.
Time zone displacements (+/- HH:MM) are encoded with `(sign * (HH * 60 + MM)) + 1439`.
For example, a `00:00` displacement is encoded as `(1 * (0 * 60 + 0)) + 1439 = 1439` and `-02:00` as `(-1 * (2 * 60 + 0)) + 1439 = 1319`.
Time zone displacements (+/- hours:minutes) are encoded with `(sign * (hours * 60 + minutes)) + 1439`.
For example, a `+00:00` displacement is encoded as `(1 * (0 * 60 + 0)) + 1439 = 1439` and `-02:00` as `(-1 * (2 * 60 + 0)) + 1439 = 1319`.
EXTENDED TIME/TIMESTAMP WITH TIME ZONE have additionally more 2 bytes always containing absolute
time zone offset in minutes.
@ -215,16 +215,16 @@ fields are set using specified `ext_offset`.
<time zone> ::=
<time zone region> |
[+/-] <hour displacement> [: <minute displacement>]
{+ | -} <hours displacement> : <minutes displacement>
```
Examples:
- `'America/Sao_Paulo'`
- `'-02:00'`
- `'+04'`
- `'04:00'`
- `'04:30'`
- `'+04:00'`
- `'+4:0'`
- `'-04:30'`
## `TIME WITH TIME ZONE` and `TIMESTAMP WITH TIME ZONE` literals
@ -239,9 +239,9 @@ Examples:
Examples:
- `time '10:00 America/Los_Angeles'`
- `time '10:00:00.5 +08'`
- `time '10:00:00.5 +08:00'`
- `timestamp '2018-01-01 10:00 America/Los_Angeles'`
- `timestamp '2018-01-01 10:00:00.5 +08'`
- `timestamp '2018-01-01 10:00:00.5 +08:00'`
## Statements and expressions
@ -281,7 +281,7 @@ If `LOCAL` is used, the value is converted to the session time zone.
#### Examples
```
select time '12:00 GMT' at time zone '-03'
select time '12:00 GMT' at time zone '-03:00'
from rdb$database;
select current_timestamp at time zone 'America/Sao_Paulo'
@ -362,7 +362,7 @@ Returns:
```
DATABASE_VERSION
================
2017c
2020d
```
### Procedure `TRANSITIONS`

View File

@ -428,39 +428,40 @@ USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
skipSpaces(p, end);
int sign = 1;
bool signPresent = false;
const auto start = str;
if (p < end && (*p == '-' || *p == '+'))
{
signPresent = true;
sign = *p == '-' ? -1 : 1;
++p;
int sign = *p++ == '-' ? -1 : 1;
skipSpaces(p, end);
}
if (p < end && (signPresent || (*p >= '0' && *p <= '9')))
{
int tzh = parseNumber(p, end);
int tzm = 0;
skipSpaces(p, end);
if (p < end && *p == ':')
if (tzh >= 0)
{
++p;
skipSpaces(p, end);
tzm = (unsigned) parseNumber(p, end);
skipSpaces(p, end);
if (p < end && *p == ':')
{
++p;
skipSpaces(p, end);
int tzm = parseNumber(p, end);
if (tzm >= 0)
{
skipSpaces(p, end);
if (p == end)
return makeFromOffset(sign, tzh, tzm);
}
}
}
if (p != end)
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << string(str, strLen));
return makeFromOffset(sign, tzh, tzm);
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << string(start, end));
return 0; // avoid warning
}
else
return parseRegion(p, str + strLen - p);
return parseRegion(p, str + strLen - p);
}
// Parses a time zone id from a region string.
@ -497,7 +498,7 @@ USHORT TimeZoneUtil::parseRegion(const char* str, unsigned strLen)
return id;
}
status_exception::raise(Arg::Gds(isc_invalid_timezone_region) << string(start, len));
status_exception::raise(Arg::Gds(isc_invalid_timezone_region) << string(start, end));
return 0;
}
@ -1178,7 +1179,7 @@ static int parseNumber(const char*& p, const char* end)
n = n * 10 + *p++ - '0';
if (p == start)
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << string(start, end - start));
return -1;
return n;
}

View File

@ -752,9 +752,14 @@ void CVT_string_to_datetime(const dsc* desc,
components[i] = n;
bool hadSpace = false;
// Grab whitespace following the number
while (p < end && (*p == ' ' || *p == '\t'))
{
p++;
hadSpace = true;
}
if (p == end)
break;
@ -762,18 +767,16 @@ void CVT_string_to_datetime(const dsc* desc,
// Grab a separator character
if (i <= 1)
{
if (date_sep == '\0' || *p == date_sep)
if (date_sep == '\0' || (date_sep != ' ' && *p == date_sep) || (date_sep == ' ' && hadSpace))
{
date_sep = *p;
if (*p == '/' || *p == '-')
if (date_sep != ' ' && (*p == '/' || *p == '-' || *p == '.'))
{
p++;
date_sep = *p++;
continue;
}
else if (*p == '.')
else if (hadSpace)
{
p++;
date_sep = ' ';
continue;
}
}

View File

@ -917,7 +917,7 @@ Data source : @4"}, /* eds_statement */
{335545210, "Plugin @1:"}, /* plugin_name */
{335545211, "PARAMETER @1"}, /* parameter_name */
{335545212, "Starting page number for file @1 must be @2 or greater"}, /* file_starting_page_err */
{335545213, "Invalid time zone offset: @1 - must be between -14:00 and +14:00"}, /* invalid_timezone_offset */
{335545213, "Invalid time zone offset: @1 - must use format +/-hours:minutes and be between -14:00 and +14:00"}, /* invalid_timezone_offset */
{335545214, "Invalid time zone region: @1"}, /* invalid_timezone_region */
{335545215, "Invalid time zone ID: @1"}, /* invalid_timezone_id */
{335545216, "Wrong base64 text length @1, should be multiple of 4"}, /* tom_decode64len */

View File

@ -1000,7 +1000,7 @@ Data source : @4', NULL, NULL)
('plugin_name', NULL, 'CryptoManager.cpp', NULL, 0, 890, NULL, 'Plugin @1:', NULL, NULL);
('parameter_name', 'ProcedureManager::checkDependencies', 'dfw.e', NULL, 0, 891, NULL, 'PARAMETER @1', NULL, NULL);
('file_starting_page_err', 'add_file', 'dfw.epp', NULL, 0, 892, NULL, 'Starting page number for file @1 must be @2 or greater', NULL, NULL);
('invalid_timezone_offset', NULL, 'TimeZoneUtil.cpp', NULL, 0, 893, NULL, 'Invalid time zone offset: @1 - must be between -14:00 and +14:00', NULL, NULL);
('invalid_timezone_offset', NULL, 'TimeZoneUtil.cpp', NULL, 0, 893, NULL, 'Invalid time zone offset: @1 - must use format +/-hours:minutes and be between -14:00 and +14:00', NULL, NULL);
('invalid_timezone_region', NULL, 'TimeZoneUtil.cpp', NULL, 0, 894, NULL, 'Invalid time zone region: @1', NULL, NULL);
('invalid_timezone_id', NULL, 'TimeZoneUtil.cpp', NULL, 0, 895, NULL, 'Invalid time zone ID: @1', NULL, NULL);
('tom_decode64len', NULL, 'SysFunction.cpp', NULL, 0, 896, NULL, 'Wrong base64 text length @1, should be multiple of 4', NULL, NULL);