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:
parent
9914167397
commit
f6cda2a693
@ -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
|
||||
|
@ -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, ×, &fractions);
|
||||
|
||||
return TimeStamp::encode_timestamp(×, 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, ×, &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, ×, &fractions);
|
||||
|
||||
return TimeStamp::encode_timestamp(×, 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, ×, &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(×, 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, ×, &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
|
||||
|
||||
|
@ -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
|
||||
|
@ -920,7 +920,10 @@ void CVT_string_to_datetime(const dsc* desc,
|
||||
{
|
||||
// Fetch current date
|
||||
tm times2;
|
||||
Firebird::TimeStamp::getCurrentTimeStamp().decode(×2);
|
||||
TimeStamp baseTimeStamp;
|
||||
baseTimeStamp.value().timestamp_date = TimeZoneUtil::TIME_TZ_BASE_DATE;
|
||||
baseTimeStamp.value().timestamp_time = 0;
|
||||
baseTimeStamp.decode(×2);
|
||||
|
||||
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, ×, &fractions);
|
||||
true, TimeZoneUtil::NO_OFFSET, ×, &fractions);
|
||||
timezone = ((ISC_TIME_TZ*) from->dsc_address)->time_zone;
|
||||
break;
|
||||
|
||||
|
@ -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, ×, &fractions);
|
||||
false, TimeZoneUtil::NO_OFFSET, ×, &fractions);
|
||||
break;
|
||||
|
||||
case blr_extract_timezone_hour:
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
×, &intFractions);
|
||||
bool tzLookup = TimeZoneUtil::decodeTime(*timeTz, true, gmtFallback, ×, &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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user