mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16: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
|
||||
|
||||
```
|
||||
|
@ -189,6 +189,8 @@ static InitInstance<TimeZoneStartup> timeZoneStartup;
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
const char TimeZoneUtil::GMT_FALLBACK[5] = "GMT*";
|
||||
|
||||
// Return the current user's time zone.
|
||||
USHORT TimeZoneUtil::getSystemTimeZone()
|
||||
{
|
||||
@ -498,7 +500,7 @@ ISC_TIMESTAMP TimeZoneUtil::timeStampTzToTimeStamp(const ISC_TIMESTAMP_TZ& timeS
|
||||
|
||||
struct tm times;
|
||||
int fractions;
|
||||
decodeTimeStamp(tempTimeStampTz, ×, &fractions);
|
||||
decodeTimeStamp(tempTimeStampTz, false, ×, &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;
|
||||
}
|
||||
|
||||
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);
|
||||
decodeTimeStamp(timeStampTz, times, fractions);
|
||||
bool tzLookup = true;
|
||||
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;
|
||||
}
|
||||
|
||||
decodeTimeStamp(timeStampTz, false, times, fractions);
|
||||
return tzLookup;
|
||||
}
|
||||
|
||||
void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct tm* times, int* fractions)
|
||||
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 +
|
||||
timeStampTz.utc_timestamp.timestamp_time;
|
||||
bool icuFail = false;
|
||||
int displacement;
|
||||
|
||||
if (timeStampTz.time_zone == GMT_ZONE)
|
||||
@ -615,32 +640,45 @@ void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct t
|
||||
{
|
||||
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
||||
|
||||
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
|
||||
|
||||
UCalendar* icuCalendar = icuLib.ucalOpen(
|
||||
getDesc(timeStampTz.time_zone)->icuName, -1, NULL, UCAL_GREGORIAN, &icuErrorCode);
|
||||
|
||||
if (!icuCalendar)
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open.");
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, ticksToIcuDate(ticks), &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
try
|
||||
{
|
||||
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
|
||||
|
||||
UCalendar* icuCalendar = icuLib.ucalOpen(
|
||||
getDesc(timeStampTz.time_zone)->icuName, -1, NULL, UCAL_GREGORIAN, &icuErrorCode);
|
||||
|
||||
if (!icuCalendar)
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open.");
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, ticksToIcuDate(ticks), &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_setMillis.");
|
||||
}
|
||||
|
||||
displacement = (icuLib.ucalGet(icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
|
||||
icuLib.ucalGet(icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_get.");
|
||||
}
|
||||
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_setMillis.");
|
||||
}
|
||||
|
||||
displacement = (icuLib.ucalGet(icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
|
||||
icuLib.ucalGet(icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
catch (const Exception&)
|
||||
{
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_get.");
|
||||
if (gmtFallback)
|
||||
{
|
||||
icuFail = true;
|
||||
displacement = 0;
|
||||
}
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
TimeStamp::decode_timestamp(ts, times, fractions);
|
||||
|
||||
return !icuFail;
|
||||
}
|
||||
|
||||
ISC_TIMESTAMP_TZ TimeZoneUtil::getCurrentSystemTimeStamp()
|
||||
|
@ -59,8 +59,9 @@ 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_SIZE = MAX_LEN + 1;
|
||||
|
||||
@ -99,8 +100,10 @@ public:
|
||||
static void localTimeStampToUtc(ISC_TIMESTAMP& timeStamp, Callbacks* cb);
|
||||
static void localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz);
|
||||
|
||||
static void decodeTime(const ISC_TIME_TZ& timeTz, Callbacks* cb, struct tm* times, int* fractions = NULL);
|
||||
static void decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct tm* times, int* fractions = NULL);
|
||||
static bool decodeTime(const ISC_TIME_TZ& timeTz, bool gmtFallback, Callbacks* cb,
|
||||
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 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
|
||||
|
||||
bool tzLookup = true;
|
||||
tm times;
|
||||
memset(×, 0, sizeof(struct tm));
|
||||
|
||||
@ -2222,7 +2223,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
||||
break;
|
||||
|
||||
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;
|
||||
break;
|
||||
|
||||
@ -2237,7 +2238,7 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
|
||||
|
||||
case dtype_timestamp_tz:
|
||||
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;
|
||||
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)
|
||||
{
|
||||
*p++ = ' ';
|
||||
p += TimeZoneUtil::format(p, sizeof(temp) - (p - temp), timezone);
|
||||
if (tzLookup)
|
||||
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
|
||||
@ -3455,7 +3462,7 @@ namespace
|
||||
|
||||
ISC_TIMESTAMP CommonCallbacks::getCurrentGmtTimeStamp()
|
||||
{
|
||||
return TimeZoneUtil::timeStampTzToTimeStamp(TimeZoneUtil::getCurrentSystemTimeStamp(), TimeZoneUtil::GMT_ZONE);
|
||||
return TimeZoneUtil::getCurrentGmtTimeStamp().utc_timestamp;
|
||||
}
|
||||
|
||||
USHORT CommonCallbacks::getSessionTimeZone()
|
||||
|
@ -5448,7 +5448,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,
|
||||
&EngineCallbacks::instance, ×, &fractions);
|
||||
false, &EngineCallbacks::instance, ×, &fractions);
|
||||
break;
|
||||
|
||||
case blr_extract_timezone_hour:
|
||||
@ -5508,7 +5508,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
break;
|
||||
|
||||
default:
|
||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) value->dsc_address, ×, &fractions);
|
||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) value->dsc_address, false, ×, &fractions);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3785,7 +3785,7 @@ dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestVal
|
||||
break;
|
||||
|
||||
case dtype_timestamp_tz:
|
||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) valueDsc->dsc_address, ×, &fractions);
|
||||
TimeZoneUtil::decodeTimeStamp(*(ISC_TIMESTAMP_TZ*) valueDsc->dsc_address, false, ×, &fractions);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -674,7 +674,8 @@ void UtilInterface::decodeTimeTz(CheckStatusWrapper* status, const ISC_TIME_TZ*
|
||||
{
|
||||
tm times;
|
||||
int intFractions;
|
||||
TimeZoneUtil::decodeTime(*timeTz, CVT_commonCallbacks, ×, &intFractions);
|
||||
bool tzLookup = TimeZoneUtil::decodeTime(*timeTz, timeZoneBuffer != nullptr, CVT_commonCallbacks,
|
||||
×, &intFractions);
|
||||
|
||||
if (hours)
|
||||
*hours = times.tm_hour;
|
||||
@ -689,7 +690,12 @@ void UtilInterface::decodeTimeTz(CheckStatusWrapper* status, const ISC_TIME_TZ*
|
||||
*fractions = (unsigned) intFractions;
|
||||
|
||||
if (timeZoneBuffer)
|
||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeTz->time_zone);
|
||||
{
|
||||
if (tzLookup)
|
||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeTz->time_zone);
|
||||
else
|
||||
strncpy(timeZoneBuffer, TimeZoneUtil::GMT_FALLBACK, timeZoneBufferLength);
|
||||
}
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
@ -720,7 +726,7 @@ void UtilInterface::decodeTimeStampTz(CheckStatusWrapper* status, const ISC_TIME
|
||||
{
|
||||
tm times;
|
||||
int intFractions;
|
||||
TimeZoneUtil::decodeTimeStamp(*timeStampTz, ×, &intFractions);
|
||||
bool tzLookup = TimeZoneUtil::decodeTimeStamp(*timeStampTz, timeZoneBuffer != nullptr, ×, &intFractions);
|
||||
|
||||
if (year)
|
||||
*year = times.tm_year + 1900;
|
||||
@ -744,7 +750,12 @@ void UtilInterface::decodeTimeStampTz(CheckStatusWrapper* status, const ISC_TIME
|
||||
*fractions = (unsigned) intFractions;
|
||||
|
||||
if (timeZoneBuffer)
|
||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeStampTz->time_zone);
|
||||
{
|
||||
if (tzLookup)
|
||||
TimeZoneUtil::format(timeZoneBuffer, timeZoneBufferLength, timeStampTz->time_zone);
|
||||
else
|
||||
strncpy(timeZoneBuffer, TimeZoneUtil::GMT_FALLBACK, timeZoneBufferLength);
|
||||
}
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user