mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 22:43:03 +01:00
Fallback for problem converting time-tz/timestamp-tz when ICU is not present in client.
This commit is contained in:
parent
2bc5097252
commit
d046a2ca86
@ -103,6 +103,8 @@ void encodeTimeStampTz(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When `decodeTimeTz` / `decodeTimeStampTz` is called with non-null `timeZoneBuffer` and ICU could not be loaded in the client, `timeZoneBuffer` returns the string `GMT*` and the others fields receives the timestamp GMT values.
|
||||||
|
|
||||||
## Time zone string syntax
|
## Time zone string syntax
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -189,6 +189,8 @@ static InitInstance<TimeZoneStartup> timeZoneStartup;
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
|
const char TimeZoneUtil::GMT_FALLBACK[5] = "GMT*";
|
||||||
|
|
||||||
// Return the current user's time zone.
|
// Return the current user's time zone.
|
||||||
USHORT TimeZoneUtil::getSystemTimeZone()
|
USHORT TimeZoneUtil::getSystemTimeZone()
|
||||||
{
|
{
|
||||||
@ -498,7 +500,7 @@ ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeS
|
|||||||
|
|
||||||
struct tm times;
|
struct tm times;
|
||||||
int fractions;
|
int fractions;
|
||||||
decodeTimeStamp(tempTimeStampTz, ×, &fractions);
|
decodeTimeStamp(tempTimeStampTz, false, ×, &fractions);
|
||||||
|
|
||||||
return TimeStamp::encode_timestamp(×, fractions);
|
return TimeStamp::encode_timestamp(×, fractions);
|
||||||
}
|
}
|
||||||
@ -595,16 +597,39 @@ void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz)
|
|||||||
timeStampTz.utc_timestamp.timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
|
timeStampTz.utc_timestamp.timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeZoneUtil::decodeTime(const ISC_TIME_TZ& timeTz, Callbacks* cb, struct tm* times, int* fractions)
|
bool TimeZoneUtil::decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, Callbacks* cb,
|
||||||
|
struct tm* times, int* fractions)
|
||||||
{
|
{
|
||||||
ISC_TIMESTAMP_TZ timeStampTz = cvtTimeTzToTimeStampTz(timeTz, cb);
|
bool tzLookup = true;
|
||||||
decodeTimeStamp(timeStampTz, times, fractions);
|
ISC_TIMESTAMP_TZ timeStampTz;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
timeStampTz = cvtTimeTzToTimeStampTz(timeTz, cb);
|
||||||
|
}
|
||||||
|
catch (const Exception&)
|
||||||
|
{
|
||||||
|
if (gmtFallback)
|
||||||
|
{
|
||||||
|
tzLookup = false;
|
||||||
|
timeStampTz.time_zone = TimeZoneUtil::GMT_ZONE;
|
||||||
|
timeStampTz.utc_timestamp = cb->getCurrentGmtTimeStamp();
|
||||||
|
timeStampTz.utc_timestamp.timestamp_time = timeTz.utc_time;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct tm* times, int* fractions)
|
decodeTimeStamp(timeStampTz, false, times, fractions);
|
||||||
|
return tzLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmtFallback,
|
||||||
|
struct tm* times, int* fractions)
|
||||||
{
|
{
|
||||||
SINT64 ticks = timeStampTz.utc_timestamp.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY +
|
SINT64 ticks = timeStampTz.utc_timestamp.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY +
|
||||||
timeStampTz.utc_timestamp.timestamp_time;
|
timeStampTz.utc_timestamp.timestamp_time;
|
||||||
|
bool icuFail = false;
|
||||||
int displacement;
|
int displacement;
|
||||||
|
|
||||||
if (timeStampTz.time_zone == GMT_ZONE)
|
if (timeStampTz.time_zone == GMT_ZONE)
|
||||||
@ -615,6 +640,8 @@ void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct t
|
|||||||
{
|
{
|
||||||
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
|
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
|
||||||
|
|
||||||
UCalendar* icuCalendar = icuLib.ucalOpen(
|
UCalendar* icuCalendar = icuLib.ucalOpen(
|
||||||
@ -642,6 +669,17 @@ void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct t
|
|||||||
|
|
||||||
icuLib.ucalClose(icuCalendar);
|
icuLib.ucalClose(icuCalendar);
|
||||||
}
|
}
|
||||||
|
catch (const Exception&)
|
||||||
|
{
|
||||||
|
if (gmtFallback)
|
||||||
|
{
|
||||||
|
icuFail = true;
|
||||||
|
displacement = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ticks += displacement * 60 * ISC_TIME_SECONDS_PRECISION;
|
ticks += displacement * 60 * ISC_TIME_SECONDS_PRECISION;
|
||||||
|
|
||||||
@ -650,6 +688,8 @@ void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct t
|
|||||||
ts.timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
|
ts.timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
|
||||||
|
|
||||||
TimeStamp::decode_timestamp(ts, times, fractions);
|
TimeStamp::decode_timestamp(ts, times, fractions);
|
||||||
|
|
||||||
|
return !icuFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISC_TIMESTAMP_TZ TimeZoneUtil::getCurrentSystemTimeStamp()
|
ISC_TIMESTAMP_TZ TimeZoneUtil::getCurrentSystemTimeStamp()
|
||||||
|
@ -59,8 +59,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const USHORT GMT_ZONE = 65535;
|
static const char GMT_FALLBACK[5]; // "GMT*"
|
||||||
|
|
||||||
|
static const USHORT GMT_ZONE = 65535;
|
||||||
static const unsigned MAX_LEN = 32;
|
static const unsigned MAX_LEN = 32;
|
||||||
static const unsigned MAX_SIZE = MAX_LEN + 1;
|
static const unsigned MAX_SIZE = MAX_LEN + 1;
|
||||||
|
|
||||||
@ -99,8 +100,10 @@ public:
|
|||||||
static void localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb);
|
static void localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb);
|
||||||
static void localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz);
|
static void localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz);
|
||||||
|
|
||||||
static void decodeTime(const ISC_TIME_TZ& timeTz, Callbacks* cb, struct tm* times, int* fractions = NULL);
|
static bool decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, Callbacks* cb,
|
||||||
static void decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct tm* times, int* fractions = NULL);
|
struct tm* times, int* fractions = NULL);
|
||||||
|
static bool decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmtFallback,
|
||||||
|
struct tm* times, int* fractions = NULL);
|
||||||
|
|
||||||
static ISC_TIMESTAMP_TZ getCurrentSystemTimeStamp();
|
static ISC_TIMESTAMP_TZ getCurrentSystemTimeStamp();
|
||||||
static ISC_TIMESTAMP_TZ getCurrentGmtTimeStamp();
|
static ISC_TIMESTAMP_TZ getCurrentGmtTimeStamp();
|
||||||
|
@ -2208,6 +2208,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
|||||||
|
|
||||||
// Convert a date or time value into a timestamp for manipulation
|
// Convert a date or time value into a timestamp for manipulation
|
||||||
|
|
||||||
|
bool tzLookup = true;
|
||||||
tm times;
|
tm times;
|
||||||
memset(×, 0, sizeof(struct tm));
|
memset(×, 0, sizeof(struct tm));
|
||||||
|
|
||||||
@ -2222,7 +2223,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case dtype_sql_time_tz:
|
case dtype_sql_time_tz:
|
||||||
TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) from->dsc_address, cb, ×, &fractions);
|
tzLookup = TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) from->dsc_address, true, cb, ×, &fractions);
|
||||||
timezone = ((ISC_TIME_TZ*) from->dsc_address)->time_zone;
|
timezone = ((ISC_TIME_TZ*) from->dsc_address)->time_zone;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2237,7 +2238,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
|||||||
|
|
||||||
case dtype_timestamp_tz:
|
case dtype_timestamp_tz:
|
||||||
cb->isVersion4(version4); // Used in the conversion to text some lines below.
|
cb->isVersion4(version4); // Used in the conversion to text some lines below.
|
||||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, ×, &fractions);
|
tzLookup = TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) from->dsc_address, true, ×, &fractions);
|
||||||
timezone = ((ISC_TIMESTAMP_TZ*) from->dsc_address)->time_zone;
|
timezone = ((ISC_TIMESTAMP_TZ*) from->dsc_address)->time_zone;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2302,7 +2303,13 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
|||||||
if (from->dsc_dtype == dtype_sql_time_tz || from->dsc_dtype == dtype_timestamp_tz)
|
if (from->dsc_dtype == dtype_sql_time_tz || from->dsc_dtype == dtype_timestamp_tz)
|
||||||
{
|
{
|
||||||
*p++ = ' ';
|
*p++ = ' ';
|
||||||
|
if (tzLookup)
|
||||||
p += TimeZoneUtil::format(p, sizeof(temp) - (p - temp), timezone);
|
p += TimeZoneUtil::format(p, sizeof(temp) - (p - temp), timezone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(p, TimeZoneUtil::GMT_FALLBACK, sizeof(temp) - (p - temp));
|
||||||
|
p += strlen(TimeZoneUtil::GMT_FALLBACK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the text version of the date/time value into the destination
|
// Move the text version of the date/time value into the destination
|
||||||
@ -3455,7 +3462,7 @@ namespace
|
|||||||
|
|
||||||
ISC_TIMESTAMP CommonCallbacks::getCurrentGmtTimeStamp()
|
ISC_TIMESTAMP CommonCallbacks::getCurrentGmtTimeStamp()
|
||||||
{
|
{
|
||||||
return TimeZoneUtil::timeStampTzToTimeStamp(TimeZoneUtil::getCurrentSystemTimeStamp(), TimeZoneUtil::GMT_ZONE);
|
return TimeZoneUtil::getCurrentGmtTimeStamp().utc_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
USHORT CommonCallbacks::getSessionTimeZone()
|
USHORT CommonCallbacks::getSessionTimeZone()
|
||||||
|
@ -5448,7 +5448,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const
|
|||||||
case blr_extract_second:
|
case blr_extract_second:
|
||||||
case blr_extract_millisecond:
|
case blr_extract_millisecond:
|
||||||
TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) value->dsc_address,
|
TimeZoneUtil::decodeTime(*(ISC_TIME_TZ*) value->dsc_address,
|
||||||
&EngineCallbacks::instance, ×, &fractions);
|
false, &EngineCallbacks::instance, ×, &fractions);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case blr_extract_timezone_hour:
|
case blr_extract_timezone_hour:
|
||||||
@ -5508,7 +5508,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) value->dsc_address, ×, &fractions);
|
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) value->dsc_address, false, ×, &fractions);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3785,7 +3785,7 @@ dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestVal
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case dtype_timestamp_tz:
|
case dtype_timestamp_tz:
|
||||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) valueDsc->dsc_address, ×, &fractions);
|
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) valueDsc->dsc_address, false, ×, &fractions);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -674,7 +674,8 @@ void UtilInterface::decodeTimeTz(CheckStatusWrapper* status, const ISC_TIME_TZ*
|
|||||||
{
|
{
|
||||||
tm times;
|
tm times;
|
||||||
int intFractions;
|
int intFractions;
|
||||||
TimeZoneUtil::decodeTime(*timeTz, CVT_commonCallbacks, ×, &intFractions);
|
bool tzLookup = TimeZoneUtil::decodeTime(*timeTz, timeZoneBuffer != nullptr, CVT_commonCallbacks,
|
||||||
|
×, &intFractions);
|
||||||
|
|
||||||
if (hours)
|
if (hours)
|
||||||
*hours = times.tm_hour;
|
*hours = times.tm_hour;
|
||||||
@ -689,7 +690,12 @@ void UtilInterface::decodeTimeTz(CheckStatusWrapper* status, const ISC_TIME_TZ*
|
|||||||
*fractions = (unsigned) intFractions;
|
*fractions = (unsigned) intFractions;
|
||||||
|
|
||||||
if (timeZoneBuffer)
|
if (timeZoneBuffer)
|
||||||
|
{
|
||||||
|
if (tzLookup)
|
||||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeTz->time_zone);
|
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeTz->time_zone);
|
||||||
|
else
|
||||||
|
strncpy(timeZoneBuffer, TimeZoneUtil::GMT_FALLBACK, timeZoneBufferLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const Exception& ex)
|
catch (const Exception& ex)
|
||||||
{
|
{
|
||||||
@ -720,7 +726,7 @@ void UtilInterface::decodeTimeStampTz(CheckStatusWrapper* status, const ISC_TIME
|
|||||||
{
|
{
|
||||||
tm times;
|
tm times;
|
||||||
int intFractions;
|
int intFractions;
|
||||||
TimeZoneUtil::decodeTimeStamp(*timeStampTz, ×, &intFractions);
|
bool tzLookup = TimeZoneUtil::decodeTimeStamp(*timeStampTz, timeZoneBuffer != nullptr, ×, &intFractions);
|
||||||
|
|
||||||
if (year)
|
if (year)
|
||||||
*year = times.tm_year + 1900;
|
*year = times.tm_year + 1900;
|
||||||
@ -744,7 +750,12 @@ void UtilInterface::decodeTimeStampTz(CheckStatusWrapper* status, const ISC_TIME
|
|||||||
*fractions = (unsigned) intFractions;
|
*fractions = (unsigned) intFractions;
|
||||||
|
|
||||||
if (timeZoneBuffer)
|
if (timeZoneBuffer)
|
||||||
|
{
|
||||||
|
if (tzLookup)
|
||||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeStampTz->time_zone);
|
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeStampTz->time_zone);
|
||||||
|
else
|
||||||
|
strncpy(timeZoneBuffer, TimeZoneUtil::GMT_FALLBACK, timeZoneBufferLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const Exception& ex)
|
catch (const Exception& ex)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user