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

Fixed CORE-6271 - Bind time with time zone to legacy produces wrong values.

This commit is contained in:
Adriano dos Santos Fernandes 2020-05-04 13:03:28 -03:00
parent 9914167397
commit f6cda2a693
8 changed files with 198 additions and 167 deletions

View File

@ -21,7 +21,7 @@ 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`) from GMT.
(for example, `-03:00` or `+3`) 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'`.
@ -32,7 +32,7 @@ in America/New_York, 2:30 AM on March 12, 2017 does not exist and is interpreted
3:30 AM UTC-04). For the second case, when DST ends in America/New_York, 1:30 AM on November 5, 2017 repeats twice and
is interpreted as 1:30 AM UTC-04 instead of 1:30 AM UTC-05.
`EXTENDED TIME/TIMESTAMP WITH TIME ZONE` are intended for use only when communicating with a cliens,
`EXTENDED TIME/TIMESTAMP WITH TIME ZONE` are intended for use only when communicating with clients,
they solve a problem of representing correct time on clients missing ICU library. One can't use extended
datatypes in tables, procedures, etc. The only way to use that datatypes is datatype coercion including
SET BIND statement (see [README.set_bind](./README.set_bind.md) for further details).
@ -48,6 +48,33 @@ TIMESTAMP [ { WITH | WITHOUT } TIME ZONE ]
EXTENDED { TIME | TIMESTAMP } WITH TIME ZONE
```
## Region-based `TIME WITH TIME ZONE` semantics
By definition region-based time zones depends on a moment (date and time - or timestamp) to
know its UTC offset in relation to GMT.
But Firebird also supports region-based time zones in `TIME WITH TIME ZONE` values.
When constructing a `TIME WITH TIME ZONE` value from a literal or conversion its UTC value must
be computed and cannot be changed, so the current date may not be used. In this case the fixed date
`2020-01-01` is used. So when comparing `TIME WITH TIME ZONE` with different time zones the
comparation is done is a manner similar to they being `TIMESTAMP WITH TIME ZONE` values in the
given date.
However when converting between `TIMESTAMP` types to `TIME WITH TIME ZONE` that fixed date is
not used, otherwise some weird conversions may be seen when the current date has a different
offset (due to DST changes) than one in `2020-01-01`. In this case when converting
a `TIME WITH TIME ZONE` to `TIMESTAMP WITH TIME ZONE` the time portion is maintained
(if possible). For example, if current date is `2020-05-03` the effective offset in time zone
`America/Los_Angeles` is `-420` while its effective offset in `2020-01-01` is `-480`, but
`cast(time '10:00:00 America/Los_Angeles' as timestamp with time zone)` will result in
`2020-05-03 10:00:00.0000 America/Los_Angeles` instead of having the time portion adjusted.
But in a date when DST starts there is a missing hour, for example in `America/Los_Angeles`
in `2021-03-14` which there is no `02:00:00` to `02:59:59` hours. In this case the conversion
is done like constructing a literal and the hour is adjusted to its next valid value.
For example, in `2021-03-14` a `cast(time '02:10:00 America/Los_Angeles' as timestamp with time zone)`
will result in `2021-03-14 03:10:00.0000 America/Los_Angeles`.
### Storage
TIME/TIMESTAMP WITH TIME ZONE has respectively the same storage of TIME/TIMESTAMP WITHOUT TIME ZONE
@ -63,6 +90,7 @@ For example, a `00:00` displacement is encoded as `(1 * (0 * 60 + 0)) + 1439 = 1
EXTENDED TIME/TIMESTAMP WITH TIME ZONE have additionally more 2 bytes always containing absolute
time zone offset in minutes.
### API structs
```
@ -342,16 +370,16 @@ DATABASE_VERSION
`RDB$TIME_ZONE_UTIL.TRANSITIONS` returns the set of rules between the start and end timestamps.
Input parameters:
- `TIME_ZONE_NAME` type `CHAR(63)`
- `FROM_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
- `TO_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
- `RDB$TIME_ZONE_NAME` type `CHAR(63)`
- `RDB$FROM_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
- `RDB$TO_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
Output parameters:
- `START_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition' start timestamp
- `END_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition's end timestamp
- `ZONE_OFFSET` type `SMALLINT` - number of minutes related to the zone's offset
- `DST_OFFSET` type `SMALLINT` - number of minutes related to the zone's DST offset
- `EFFECTIVE_OFFSET` type `SMALLINT` - effective offset (`ZONE_OFFSET + DST_OFFSET`)
- `RDB$START_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition' start timestamp
- `RDB$END_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition's end timestamp
- `RDB$ZONE_OFFSET` type `SMALLINT` - number of minutes related to the zone's offset
- `RDB$DST_OFFSET` type `SMALLINT` - number of minutes related to the zone's DST offset
- `RDB$EFFECTIVE_OFFSET` type `SMALLINT` - effective offset (`ZONE_OFFSET + DST_OFFSET`)
```
select *
@ -364,13 +392,13 @@ select *
Returns:
```
START_TIMESTAMP END_TIMESTAMP ZONE_OFFSET DST_OFFSET EFFECTIVE_OFFSET
============================ ============================ =========== ========== ================
2016-10-16 03:00:00.0000 GMT 2017-02-19 01:59:59.9999 GMT -180 60 -120
2017-02-19 02:00:00.0000 GMT 2017-10-15 02:59:59.9999 GMT -180 0 -180
2017-10-15 03:00:00.0000 GMT 2018-02-18 01:59:59.9999 GMT -180 60 -120
2018-02-18 02:00:00.0000 GMT 2018-10-21 02:59:59.9999 GMT -180 0 -180
2018-10-21 03:00:00.0000 GMT 2019-02-17 01:59:59.9999 GMT -180 60 -120
RDB$START_TIMESTAMP RDB$END_TIMESTAMP RDB$ZONE_OFFSET RDB$DST_OFFSET RDB$EFFECTIVE_OFFSET
============================ ============================ =============== ============== ====================
2016-10-16 03:00:00.0000 GMT 2017-02-19 01:59:59.9999 GMT -180 60 -120
2017-02-19 02:00:00.0000 GMT 2017-10-15 02:59:59.9999 GMT -180 0 -180
2017-10-15 03:00:00.0000 GMT 2018-02-18 01:59:59.9999 GMT -180 60 -120
2018-02-18 02:00:00.0000 GMT 2018-10-21 02:59:59.9999 GMT -180 0 -180
2018-10-21 03:00:00.0000 GMT 2019-02-17 01:59:59.9999 GMT -180 60 -120
```
# Updating the time zone database

View File

@ -190,6 +190,8 @@ static InitInstance<TimeZoneStartup> timeZoneStartup;
//-------------------------------------
const ISC_DATE TimeZoneUtil::TIME_TZ_BASE_DATE = 58849; // 2020-01-01
const char TimeZoneUtil::GMT_FALLBACK[5] = "GMT*";
InitInstance<PathName> TimeZoneUtil::tzDataPath;
@ -524,69 +526,41 @@ void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* of
*offset = displacement;
}
// Converts a time-tz to a time in a given zone.
ISC_TIME TimeZoneUtil::timeTzToTime(const ISC_TIME_TZ& timeTz, USHORT toTimeZone, Callbacks* cb)
{
ISC_TIMESTAMP_TZ tempTimeStampTz;
tempTimeStampTz.utc_timestamp.timestamp_date = cb->getLocalDate();
tempTimeStampTz.utc_timestamp.timestamp_time = 0;
tempTimeStampTz.time_zone = cb->getSessionTimeZone();
TimeZoneUtil::localTimeStampToUtc(tempTimeStampTz);
tempTimeStampTz.utc_timestamp.timestamp_time = timeTz.utc_time;
tempTimeStampTz.time_zone = timeTz.time_zone;
return timeStampTzToTimeStamp(tempTimeStampTz, toTimeZone).timestamp_time;
}
// Converts a timestamp-tz to a timestamp in a given zone.
ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT toTimeZone)
{
ISC_TIMESTAMP_TZ tempTimeStampTz = timeStampTz;
tempTimeStampTz.time_zone = toTimeZone;
struct tm times;
int fractions;
decodeTimeStamp(tempTimeStampTz, false, TimeZoneUtil::NO_OFFSET, &times, &fractions);
return TimeStamp::encode_timestamp(&times, fractions);
}
// Converts a time from local to UTC.
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, Callbacks* cb)
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone)
{
ISC_TIME_TZ timeTz;
timeTz.utc_time = time;
timeTz.time_zone = cb->getSessionTimeZone();
localTimeToUtc(timeTz, cb);
timeTz.time_zone = timeZone;
localTimeToUtc(timeTz);
time = timeTz.utc_time;
}
// Converts a time from local to UTC.
void TimeZoneUtil::localTimeToUtc(ISC_TIME_TZ& timeTz, Callbacks* cb)
void TimeZoneUtil::localTimeToUtc(ISC_TIME_TZ& timeTz)
{
ISC_TIMESTAMP_TZ tempTimeStampTz;
tempTimeStampTz.utc_timestamp.timestamp_date = cb->getCurrentGmtTimeStamp().timestamp_date;
tempTimeStampTz.utc_timestamp.timestamp_time = timeTz.utc_time;
tempTimeStampTz.time_zone = timeTz.time_zone;
localTimeStampToUtc(tempTimeStampTz);
ISC_TIMESTAMP_TZ tsTz;
tsTz.utc_timestamp.timestamp_date = TIME_TZ_BASE_DATE;
tsTz.utc_timestamp.timestamp_time = timeTz.utc_time;
tsTz.time_zone = timeTz.time_zone;
localTimeStampToUtc(tsTz);
timeTz.utc_time = tempTimeStampTz.utc_timestamp.timestamp_time;
timeTz.utc_time = tsTz.utc_timestamp.timestamp_time;
}
// Converts a timestamp from its local datetime fields to UTC.
void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb)
{
ISC_TIMESTAMP_TZ tempTimeStampTz;
tempTimeStampTz.utc_timestamp.timestamp_date = timeStamp.timestamp_date;
tempTimeStampTz.utc_timestamp.timestamp_time = timeStamp.timestamp_time;
tempTimeStampTz.time_zone = cb->getSessionTimeZone();
ISC_TIMESTAMP_TZ tsTz;
tsTz.utc_timestamp.timestamp_date = timeStamp.timestamp_date;
tsTz.utc_timestamp.timestamp_time = timeStamp.timestamp_time;
tsTz.time_zone = cb->getSessionTimeZone();
localTimeStampToUtc(tempTimeStampTz);
localTimeStampToUtc(tsTz);
timeStamp.timestamp_date = tempTimeStampTz.utc_timestamp.timestamp_date;
timeStamp.timestamp_time = tempTimeStampTz.utc_timestamp.timestamp_time;
timeStamp.timestamp_date = tsTz.utc_timestamp.timestamp_date;
timeStamp.timestamp_time = tsTz.utc_timestamp.timestamp_time;
}
// Converts a timestamp from its local datetime fields to UTC.
@ -643,35 +617,15 @@ void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz)
timeStampTz.utc_timestamp = TimeStamp::ticksToTimeStamp(ticks);
}
bool TimeZoneUtil::decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, SLONG gmtOffset, Callbacks* cb,
bool TimeZoneUtil::decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, SLONG gmtOffset,
struct tm* times, int* fractions)
{
bool tzLookup = true;
ISC_TIMESTAMP_TZ timeStampTz;
timeStampTz.utc_timestamp.timestamp_date = TIME_TZ_BASE_DATE;
timeStampTz.utc_timestamp.timestamp_time = timeTz.utc_time;
timeStampTz.time_zone = timeTz.time_zone;
try
{
#ifdef DEV_BUILD
if (gmtFallback && getenv("MISSING_ICU_EMULATION"))
(Arg::Gds(isc_random) << "Emulating missing ICU").raise();
#endif
timeStampTz = cvtTimeTzToTimeStampTz(timeTz, cb);
}
catch (const Exception&)
{
if (gmtFallback)
{
tzLookup = false;
timeStampTz.time_zone = displacementToOffsetZone(gmtOffset == TimeZoneUtil::NO_OFFSET ? 0 : gmtOffset);
timeStampTz.utc_timestamp = cb->getCurrentGmtTimeStamp();
timeStampTz.utc_timestamp.timestamp_time = timeTz.utc_time;
}
else
throw;
}
decodeTimeStamp(timeStampTz, gmtFallback, gmtOffset, times, fractions);
return tzLookup;
return decodeTimeStamp(timeStampTz, gmtFallback, gmtOffset, times, fractions);
}
bool TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmtFallback, SLONG gmtOffset,
@ -727,7 +681,7 @@ bool TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmt
throw;
icuFail = true;
displacement = gmtOffset == TimeZoneUtil::NO_OFFSET ? 0 : gmtOffset;
displacement = gmtOffset == NO_OFFSET ? 0 : gmtOffset;
}
}
@ -829,8 +783,42 @@ void TimeZoneUtil::validateGmtTimeStamp(NoThrowTimeStamp& ts)
ts.value() = getCurrentGmtTimeStamp().utc_timestamp;
}
// Converts a time-tz to a time.
ISC_TIME TimeZoneUtil::timeTzToTime(const ISC_TIME_TZ& timeTz, Callbacks* cb)
{
ISC_TIMESTAMP_TZ tsTz;
tsTz.utc_timestamp.timestamp_date = TIME_TZ_BASE_DATE;
tsTz.utc_timestamp.timestamp_time = timeTz.utc_time;
tsTz.time_zone = timeTz.time_zone;
struct tm times;
int fractions;
decodeTimeStamp(tsTz, false, NO_OFFSET, &times, &fractions);
tsTz.utc_timestamp.timestamp_date = cb->getLocalDate();
tsTz.utc_timestamp.timestamp_time =
TimeStamp::encode_time(times.tm_hour, times.tm_min, times.tm_sec, fractions);
localTimeStampToUtc(tsTz);
return timeStampTzToTimeStamp(tsTz, cb->getSessionTimeZone()).timestamp_time;
}
// Converts a timestamp-tz to a timestamp in a given zone.
ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT toTimeZone)
{
ISC_TIMESTAMP_TZ tsTz = timeStampTz;
tsTz.time_zone = toTimeZone;
struct tm times;
int fractions;
decodeTimeStamp(tsTz, false, NO_OFFSET, &times, &fractions);
return TimeStamp::encode_timestamp(&times, fractions);
}
// Converts a time to timestamp-tz.
ISC_TIMESTAMP_TZ TimeZoneUtil::cvtTimeToTimeStampTz(const ISC_TIME& time, Callbacks* cb)
ISC_TIMESTAMP_TZ TimeZoneUtil::timeToTimeStampTz(const ISC_TIME& time, Callbacks* cb)
{
// SQL: source => TIMESTAMP WITHOUT TIME ZONE => TIMESTAMP WITH TIME ZONE
@ -844,66 +832,82 @@ ISC_TIMESTAMP_TZ TimeZoneUtil::cvtTimeToTimeStampTz(const ISC_TIME& time, Callba
}
// Converts a time to time-tz.
ISC_TIME_TZ TimeZoneUtil::cvtTimeToTimeTz(const ISC_TIME& time, Callbacks* cb)
ISC_TIME_TZ TimeZoneUtil::timeToTimeTz(const ISC_TIME& time, Callbacks* cb)
{
ISC_TIMESTAMP_TZ tsTz = cvtTimeToTimeStampTz(time, cb);
ISC_TIMESTAMP_TZ tsTz;
tsTz.time_zone = cb->getSessionTimeZone();
tsTz.utc_timestamp.timestamp_date = TIME_TZ_BASE_DATE;
tsTz.utc_timestamp.timestamp_time = time;
localTimeStampToUtc(tsTz);
ISC_TIME_TZ timeTz;
timeTz.utc_time = tsTz.utc_timestamp.timestamp_time;
timeTz.time_zone = tsTz.time_zone;
timeTz.utc_time = tsTz.utc_timestamp.timestamp_time;
return timeTz;
}
// Converts a time-tz to timestamp-tz.
ISC_TIMESTAMP_TZ TimeZoneUtil::cvtTimeTzToTimeStampTz(const ISC_TIME_TZ& timeTz, Callbacks* cb)
ISC_TIMESTAMP_TZ TimeZoneUtil::timeTzToTimeStampTz(const ISC_TIME_TZ& timeTz, Callbacks* cb)
{
// SQL: Copy date fields from CURRENT_DATE and time and time zone fields from the source.
struct tm localTimes;
TimeStamp::decode_date(cb->getLocalDate(), &localTimes);
ISC_TIMESTAMP_TZ tsTz;
tsTz.utc_timestamp.timestamp_date = cb->getLocalDate();
tsTz.utc_timestamp.timestamp_time = timeTzToTime(timeTz, cb->getSessionTimeZone(), cb);
tsTz.time_zone = cb->getSessionTimeZone();
localTimeStampToUtc(tsTz);
tsTz.time_zone = timeTz.time_zone;
tsTz.utc_timestamp.timestamp_date = TIME_TZ_BASE_DATE;
tsTz.utc_timestamp.timestamp_time = timeTz.utc_time;
struct tm times;
int fractions;
decodeTimeStamp(tsTz, false, NO_OFFSET, &times, &fractions);
times.tm_mday = localTimes.tm_mday;
times.tm_mon = localTimes.tm_mon;
times.tm_year = localTimes.tm_year;
tsTz.utc_timestamp = TimeStamp::encode_timestamp(&times, fractions);
localTimeStampToUtc(tsTz);
return tsTz;
}
// Converts a time-tz to timestamp.
ISC_TIMESTAMP TimeZoneUtil::cvtTimeTzToTimeStamp(const ISC_TIME_TZ& timeTz, Callbacks* cb)
ISC_TIMESTAMP TimeZoneUtil::timeTzToTimeStamp(const ISC_TIME_TZ& timeTz, Callbacks* cb)
{
// SQL: source => TIMESTAMP WITH TIME ZONE => TIMESTAMP WITHOUT TIME ZONE
ISC_TIMESTAMP_TZ tsTz = cvtTimeTzToTimeStampTz(timeTz, cb);
ISC_TIMESTAMP_TZ tsTz = timeTzToTimeStampTz(timeTz, cb);
return timeStampTzToTimeStamp(tsTz, cb->getSessionTimeZone());
}
// Converts a time-tz to time.
ISC_TIME TimeZoneUtil::cvtTimeTzToTime(const ISC_TIME_TZ& timeTz, Callbacks* cb)
{
return timeTzToTime(timeTz, cb->getSessionTimeZone(), cb);
}
// Converts a timestamp-tz to timestamp.
ISC_TIMESTAMP TimeZoneUtil::cvtTimeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, Callbacks* cb)
ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, Callbacks* cb)
{
return timeStampTzToTimeStamp(timeStampTz, cb->getSessionTimeZone());
}
// Converts a timestamp-tz to time-tz.
ISC_TIME_TZ TimeZoneUtil::cvtTimeStampTzToTimeTz(const ISC_TIMESTAMP_TZ& timeStampTz)
ISC_TIME_TZ TimeZoneUtil::timeStampTzToTimeTz(const ISC_TIMESTAMP_TZ& timeStampTz)
{
struct tm times;
int fractions;
decodeTimeStamp(timeStampTz, false, NO_OFFSET, &times, &fractions);
ISC_TIME_TZ timeTz;
timeTz.utc_time = timeStampTz.utc_timestamp.timestamp_time;
timeTz.utc_time = TimeStamp::encode_time(times.tm_hour, times.tm_min, times.tm_sec, fractions);
timeTz.time_zone = timeStampTz.time_zone;
localTimeToUtc(timeTz);
return timeTz;
}
// Converts a timestamp to timestamp-tz.
ISC_TIMESTAMP_TZ TimeZoneUtil::cvtTimeStampToTimeStampTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb)
ISC_TIMESTAMP_TZ TimeZoneUtil::timeStampToTimeStampTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb)
{
// SQL: Copy time and time zone fields from the source.
@ -917,21 +921,15 @@ ISC_TIMESTAMP_TZ TimeZoneUtil::cvtTimeStampToTimeStampTz(const ISC_TIMESTAMP& ti
}
// Converts a timestamp to time-tz.
ISC_TIME_TZ TimeZoneUtil::cvtTimeStampToTimeTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb)
ISC_TIME_TZ TimeZoneUtil::timeStampToTimeTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb)
{
// SQL: source => TIMESTAMP WITH TIME ZONE => TIME WITH TIME ZONE
ISC_TIMESTAMP_TZ tsTz = cvtTimeStampToTimeStampTz(timeStamp, cb);
ISC_TIME_TZ timeTz;
timeTz.utc_time = tsTz.utc_timestamp.timestamp_time;
timeTz.time_zone = tsTz.time_zone;
return timeTz;
return timeStampTzToTimeTz(timeStampToTimeStampTz(timeStamp, cb));
}
// Converts a date to timestamp-tz.
ISC_TIMESTAMP_TZ TimeZoneUtil::cvtDateToTimeStampTz(const ISC_DATE& date, Callbacks* cb)
ISC_TIMESTAMP_TZ TimeZoneUtil::dateToTimeStampTz(const ISC_DATE& date, Callbacks* cb)
{
// SQL: source => TIMESTAMP WITHOUT TIME ZONE => TIMESTAMP WITH TIME ZONE

View File

@ -52,13 +52,7 @@ class NoThrowTimeStamp;
class TimeZoneUtil
{
public:
enum Bind
{
BIND_LEGACY,
BIND_NATIVE
};
public:
static const ISC_DATE TIME_TZ_BASE_DATE;
static const char GMT_FALLBACK[5]; // "GMT*"
static const USHORT GMT_ZONE = 65535;
@ -102,17 +96,15 @@ public:
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* offset);
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign, unsigned* tzh, unsigned* tzm);
static ISC_TIME timeTzToTime(const ISC_TIME_TZ& timeTz, USHORT toTimeZone, Callbacks* cb);
static ISC_TIMESTAMP timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT toTimeZone);
static void localTimeToUtc(ISC_TIME& time, Callbacks* cb);
static void localTimeToUtc(ISC_TIME_TZ& timeTz, Callbacks* cb);
static void localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone);
static void localTimeToUtc(ISC_TIME_TZ& timeTz);
static void localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb);
static void localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz);
static const SLONG NO_OFFSET = MAX_SLONG;
static bool decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, SLONG gmtOffset, Callbacks* cb,
static bool decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, SLONG gmtOffset,
struct tm* times, int* fractions = NULL);
static bool decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmtFallback, SLONG gmtOffset,
struct tm* times, int* fractions = NULL);
@ -122,20 +114,22 @@ public:
static void validateGmtTimeStamp(NoThrowTimeStamp& ts);
static ISC_TIMESTAMP_TZ cvtTimeToTimeStampTz(const ISC_TIME& time, Callbacks* cb);
static ISC_TIME_TZ cvtTimeToTimeTz(const ISC_TIME& time, Callbacks* cb);
static ISC_TIME timeTzToTime(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIMESTAMP timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT toTimeZone);
static ISC_TIMESTAMP_TZ cvtTimeTzToTimeStampTz(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIMESTAMP cvtTimeTzToTimeStamp(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIME cvtTimeTzToTime(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIMESTAMP_TZ timeToTimeStampTz(const ISC_TIME& time, Callbacks* cb);
static ISC_TIME_TZ timeToTimeTz(const ISC_TIME& time, Callbacks* cb);
static ISC_TIME_TZ cvtTimeStampTzToTimeTz(const ISC_TIMESTAMP_TZ& timeStampTz);
static ISC_TIMESTAMP cvtTimeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, Callbacks* cb);
static ISC_TIMESTAMP_TZ timeTzToTimeStampTz(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIMESTAMP timeTzToTimeStamp(const ISC_TIME_TZ& timeTz, Callbacks* cb);
static ISC_TIMESTAMP_TZ cvtTimeStampToTimeStampTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb);
static ISC_TIME_TZ cvtTimeStampToTimeTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb);
static ISC_TIME_TZ timeStampTzToTimeTz(const ISC_TIMESTAMP_TZ& timeStampTz);
static ISC_TIMESTAMP timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, Callbacks* cb);
static ISC_TIMESTAMP_TZ cvtDateToTimeStampTz(const ISC_DATE& date, Callbacks* cb);
static ISC_TIMESTAMP_TZ timeStampToTimeStampTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb);
static ISC_TIME_TZ timeStampToTimeTz(const ISC_TIMESTAMP& timeStamp, Callbacks* cb);
static ISC_TIMESTAMP_TZ dateToTimeStampTz(const ISC_DATE& date, Callbacks* cb);
};
class TimeZoneRuleIterator

View File

@ -920,7 +920,10 @@ void CVT_string_to_datetime(const dsc* desc,
{
// Fetch current date
tm times2;
Firebird::TimeStamp::getCurrentTimeStamp().decode(&times2);
TimeStamp baseTimeStamp;
baseTimeStamp.value().timestamp_date = TimeZoneUtil::TIME_TZ_BASE_DATE;
baseTimeStamp.value().timestamp_time = 0;
baseTimeStamp.decode(&times2);
times.tm_year = times2.tm_year;
times.tm_mon = times2.tm_mon;
@ -1002,7 +1005,7 @@ void CVT_string_to_datetime(const dsc* desc,
ISC_TIME_TZ timeTz;
timeTz.utc_time = date->utc_timestamp.timestamp_time;
timeTz.time_zone = zone;
date->utc_timestamp.timestamp_time = TimeZoneUtil::timeTzToTime(timeTz, sessionTimeZone, cb);
date->utc_timestamp.timestamp_time = TimeZoneUtil::timeTzToTime(timeTz, cb);
}
else if (expect_type == expect_timestamp)
*(ISC_TIMESTAMP*) date = TimeZoneUtil::timeStampTzToTimeStamp(*date, sessionTimeZone);
@ -1563,13 +1566,13 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_sql_time_tz:
case dtype_ex_time_tz:
*(ISC_TIMESTAMP*) to->dsc_address =
TimeZoneUtil::cvtTimeTzToTimeStamp(*(ISC_TIME_TZ*) from->dsc_address, cb);
TimeZoneUtil::timeTzToTimeStamp(*(ISC_TIME_TZ*) from->dsc_address, cb);
return;
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
*(ISC_TIMESTAMP*) to->dsc_address =
TimeZoneUtil::cvtTimeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb);
TimeZoneUtil::timeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb);
return;
default:
@ -1588,7 +1591,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_ex_time_tz:
d.makeTimeTz((ISC_TIME_TZ*)(to->dsc_address));
CVT_move_common(from, &d, decSt, cb);
TimeZoneUtil::extractOffset(TimeZoneUtil::cvtTimeTzToTimeStampTz(*(ISC_TIME_TZ*)(to->dsc_address), cb),
TimeZoneUtil::extractOffset(TimeZoneUtil::timeTzToTimeStampTz(*(ISC_TIME_TZ*)(to->dsc_address), cb),
&((ISC_TIME_TZ_EX*)(to->dsc_address))->ext_offset);
return;
@ -1607,13 +1610,13 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_sql_time:
*(ISC_TIMESTAMP_TZ*) to->dsc_address =
TimeZoneUtil::cvtTimeToTimeStampTz(*(ISC_TIME*) from->dsc_address, cb);
TimeZoneUtil::timeToTimeStampTz(*(ISC_TIME*) from->dsc_address, cb);
return;
case dtype_ex_time_tz:
case dtype_sql_time_tz:
*((ISC_TIMESTAMP_TZ*) to->dsc_address) =
TimeZoneUtil::cvtTimeTzToTimeStampTz(*(ISC_TIME_TZ*) from->dsc_address, cb);
TimeZoneUtil::timeTzToTimeStampTz(*(ISC_TIME_TZ*) from->dsc_address, cb);
return;
case dtype_ex_timestamp_tz:
@ -1622,12 +1625,12 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_sql_date:
*(ISC_TIMESTAMP_TZ*) to->dsc_address =
TimeZoneUtil::cvtDateToTimeStampTz(*(GDS_DATE*) from->dsc_address, cb);
TimeZoneUtil::dateToTimeStampTz(*(GDS_DATE*) from->dsc_address, cb);
return;
case dtype_timestamp:
*(ISC_TIMESTAMP_TZ*) to->dsc_address =
TimeZoneUtil::cvtTimeStampToTimeStampTz(*(ISC_TIMESTAMP*) from->dsc_address, cb);
TimeZoneUtil::timeStampToTimeStampTz(*(ISC_TIMESTAMP*) from->dsc_address, cb);
return;
default:
@ -1656,7 +1659,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
*(GDS_DATE*) to->dsc_address =
TimeZoneUtil::cvtTimeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb).timestamp_date;
TimeZoneUtil::timeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb).timestamp_date;
return;
default:
@ -1680,7 +1683,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_sql_time_tz:
case dtype_ex_time_tz:
*(ISC_TIME*) to->dsc_address = TimeZoneUtil::cvtTimeTzToTime(*(ISC_TIME_TZ*) from->dsc_address, cb);
*(ISC_TIME*) to->dsc_address = TimeZoneUtil::timeTzToTime(*(ISC_TIME_TZ*) from->dsc_address, cb);
return;
case dtype_timestamp:
@ -1690,7 +1693,7 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
*(GDS_TIME*) to->dsc_address =
TimeZoneUtil::cvtTimeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb).timestamp_time;
TimeZoneUtil::timeStampTzToTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb).timestamp_time;
return;
default:
@ -1714,18 +1717,18 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
return;
case dtype_sql_time:
*(ISC_TIME_TZ*) to->dsc_address = TimeZoneUtil::cvtTimeToTimeTz(*(ISC_TIME*) from->dsc_address, cb);
*(ISC_TIME_TZ*) to->dsc_address = TimeZoneUtil::timeToTimeTz(*(ISC_TIME*) from->dsc_address, cb);
return;
case dtype_timestamp:
*(ISC_TIME_TZ*) to->dsc_address =
TimeZoneUtil::cvtTimeStampToTimeTz(*(ISC_TIMESTAMP*) from->dsc_address, cb);
TimeZoneUtil::timeStampToTimeTz(*(ISC_TIMESTAMP*) from->dsc_address, cb);
return;
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
*(ISC_TIME_TZ*) to->dsc_address =
TimeZoneUtil::cvtTimeStampTzToTimeTz(*(ISC_TIMESTAMP_TZ*) from->dsc_address);
TimeZoneUtil::timeStampTzToTimeTz(*(ISC_TIMESTAMP_TZ*) from->dsc_address);
return;
case dtype_ex_time_tz:
@ -2197,7 +2200,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
case dtype_sql_time_tz:
case dtype_ex_time_tz:
tzLookup = TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) from->dsc_address,
true, TimeZoneUtil::NO_OFFSET, cb, &times, &fractions);
true, TimeZoneUtil::NO_OFFSET, &times, &fractions);
timezone = ((ISC_TIME_TZ*) from->dsc_address)->time_zone;
break;

View File

@ -4253,15 +4253,18 @@ dsc* CurrentTimeNode::execute(thread_db* tdbb, jrd_req* request) const
// Use the request timestamp.
fb_assert(!request->req_gmt_timestamp.isEmpty());
ISC_TIME time = request->req_gmt_timestamp.value().timestamp_time;
TimeStamp::round_time(time, precision);
ISC_TIMESTAMP_TZ currentTimeStamp;
currentTimeStamp.utc_timestamp = request->req_gmt_timestamp.value();
currentTimeStamp.time_zone = tdbb->getAttachment()->att_current_timezone;
impure->vlu_desc.dsc_dtype = dtype_sql_time_tz;
impure->vlu_desc.dsc_length = type_lengths[dtype_sql_time_tz];
impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_sql_time_tz;
impure->vlu_misc.vlu_sql_time_tz.utc_time = time;
impure->vlu_misc.vlu_sql_time_tz.time_zone = tdbb->getAttachment()->att_current_timezone;
impure->vlu_misc.vlu_sql_time_tz.utc_time = TimeZoneUtil::timeStampTzToTimeTz(currentTimeStamp).utc_time;
TimeStamp::round_time(impure->vlu_misc.vlu_sql_time_tz.utc_time, precision);
return &impure->vlu_desc;
}
@ -5474,7 +5477,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const
case blr_extract_second:
case blr_extract_millisecond:
TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) value->dsc_address,
false, TimeZoneUtil::NO_OFFSET, &EngineCallbacks::instance, &times, &fractions);
false, TimeZoneUtil::NO_OFFSET, &times, &fractions);
break;
case blr_extract_timezone_hour:

View File

@ -3596,7 +3596,10 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr
timestamp1.value().timestamp_date = 0;
if (value1Dsc->dsc_dtype == dtype_sql_time && value2Dsc->isDateTimeTz())
TimeZoneUtil::localTimeToUtc(timestamp1.value().timestamp_time, &EngineCallbacks::instance);
{
TimeZoneUtil::localTimeToUtc(timestamp1.value().timestamp_time,
EngineCallbacks::instance->getSessionTimeZone());
}
break;
case dtype_sql_date:
@ -3629,7 +3632,10 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr
timestamp2.value().timestamp_date = 0;
if (value2Dsc->dsc_dtype == dtype_sql_time && value1Dsc->isDateTimeTz())
TimeZoneUtil::localTimeToUtc(timestamp2.value().timestamp_time, &EngineCallbacks::instance);
{
TimeZoneUtil::localTimeToUtc(timestamp2.value().timestamp_time,
EngineCallbacks::instance->getSessionTimeZone());
}
break;
case dtype_sql_date:

View File

@ -488,7 +488,7 @@ void INF_database_info(thread_db* tdbb,
case isc_info_creation_date:
{
const ISC_TIMESTAMP ts = TimeZoneUtil::cvtTimeStampTzToTimeStamp(
const ISC_TIMESTAMP ts = TimeZoneUtil::timeStampTzToTimeStamp(
dbb->dbb_creation_date, &EngineCallbacks::instance);
length = INF_convert(ts.timestamp_date, p);

View File

@ -674,8 +674,7 @@ void decodeTimeTzWithFallback(CheckStatusWrapper* status, const ISC_TIME_TZ* tim
{
tm times;
int intFractions;
bool tzLookup = TimeZoneUtil::decodeTime(*timeTz, true, gmtFallback, CVT_commonCallbacks,
&times, &intFractions);
bool tzLookup = TimeZoneUtil::decodeTime(*timeTz, true, gmtFallback, &times, &intFractions);
if (hours)
*hours = times.tm_hour;
@ -722,7 +721,7 @@ void UtilInterface::encodeTimeTz(CheckStatusWrapper* status, ISC_TIME_TZ* timeTz
{
timeTz->utc_time = encodeTime(hours, minutes, seconds, fractions);
timeTz->time_zone = TimeZoneUtil::parse(timeZone, strlen(timeZone));
TimeZoneUtil::localTimeToUtc(*timeTz, CVT_commonCallbacks);
TimeZoneUtil::localTimeToUtc(*timeTz);
}
catch (const Exception& ex)
{