mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 00:43:02 +01:00
Internal support for time zone regions in addition to offsets.
Simulate regions with fixed BRT and BRST regions.
This commit is contained in:
parent
4812ce8ee8
commit
8792dff315
@ -33,14 +33,37 @@ using namespace Firebird;
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
|
struct TimeZoneDesc
|
||||||
|
{
|
||||||
|
USHORT id;
|
||||||
|
const char* abbr;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
|
static SSHORT getDisplacement(const ISC_TIMESTAMP_TZ& timeStampTz);
|
||||||
|
static SSHORT getDisplacement(const ISC_TIMESTAMP& timeStampUtc, USHORT timeZone);
|
||||||
|
static inline bool isOffset(USHORT timeZone);
|
||||||
|
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
|
||||||
|
static USHORT makeFromRegion(const char* str, unsigned strLen);
|
||||||
|
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone);
|
||||||
static int parseNumber(const char*& p, const char* end);
|
static int parseNumber(const char*& p, const char* end);
|
||||||
static void skipSpaces(const char*& p, const char* end);
|
static void skipSpaces(const char*& p, const char* end);
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
|
static const TimeZoneDesc TIME_ZONE_LIST[] = { //// FIXME:
|
||||||
|
{0, "BRT"},
|
||||||
|
{1, "BRST"}
|
||||||
|
};
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
//// FIXME: Windows and others ports.
|
//// FIXME: Windows and others ports.
|
||||||
|
// Return the current user's time zone.
|
||||||
USHORT TimeZoneUtil::getCurrent()
|
USHORT TimeZoneUtil::getCurrent()
|
||||||
{
|
{
|
||||||
|
//// FIXME: Return the time zone region instead of the offset.
|
||||||
time_t rawtime;
|
time_t rawtime;
|
||||||
time(&rawtime);
|
time(&rawtime);
|
||||||
|
|
||||||
@ -48,9 +71,14 @@ USHORT TimeZoneUtil::getCurrent()
|
|||||||
if (!localtime_r(&rawtime, &tm1))
|
if (!localtime_r(&rawtime, &tm1))
|
||||||
system_call_failed::raise("localtime_r");
|
system_call_failed::raise("localtime_r");
|
||||||
|
|
||||||
return (USHORT) SSHORT(tm1.tm_gmtoff / 60);
|
int sign = tm1.tm_gmtoff < 0 ? -1 : 1;
|
||||||
|
unsigned tzh = (unsigned) abs(int(tm1.tm_gmtoff / 60 / 60));
|
||||||
|
unsigned tzm = (unsigned) abs(int(tm1.tm_gmtoff / 60 % 60));
|
||||||
|
|
||||||
|
return makeFromOffset(sign, tzh, tzm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses a time zone, offset- or region-based.
|
||||||
USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
|
USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
|
||||||
{
|
{
|
||||||
const char* end = str + strLen;
|
const char* end = str + strLen;
|
||||||
@ -59,97 +87,137 @@ USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
|
|||||||
skipSpaces(p, end);
|
skipSpaces(p, end);
|
||||||
|
|
||||||
int sign = 1;
|
int sign = 1;
|
||||||
|
bool signPresent = false;
|
||||||
|
|
||||||
if (*p == '-' || *p == '+')
|
if (*p == '-' || *p == '+')
|
||||||
{
|
{
|
||||||
|
signPresent = true;
|
||||||
sign = *p == '-' ? -1 : 1;
|
sign = *p == '-' ? -1 : 1;
|
||||||
++p;
|
++p;
|
||||||
skipSpaces(p, end);
|
skipSpaces(p, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tzh = parseNumber(p, end);
|
if (signPresent || (*p >= '0' && *p <= '9'))
|
||||||
int tzm = 0;
|
|
||||||
|
|
||||||
skipSpaces(p, end);
|
|
||||||
|
|
||||||
if (*p == ':')
|
|
||||||
{
|
{
|
||||||
++p;
|
int tzh = parseNumber(p, end);
|
||||||
skipSpaces(p, end);
|
int tzm = 0;
|
||||||
tzm = (unsigned) parseNumber(p, end);
|
|
||||||
skipSpaces(p, end);
|
skipSpaces(p, end);
|
||||||
|
|
||||||
|
if (*p == ':')
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
skipSpaces(p, end);
|
||||||
|
tzm = (unsigned) parseNumber(p, end);
|
||||||
|
skipSpaces(p, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != end)
|
||||||
|
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
|
||||||
|
|
||||||
|
return makeFromOffset(sign, tzh, tzm);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (p != end)
|
return makeFromRegion(p, str + strLen - p);
|
||||||
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
|
|
||||||
|
|
||||||
if (!isValidOffset(sign, tzh, tzm))
|
|
||||||
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
|
|
||||||
|
|
||||||
return (USHORT)(SSHORT) (tzh * 60 + tzm) * sign;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned TimeZoneUtil::format(char* buffer, USHORT zone)
|
// Format a time zone to string, as offset or region.
|
||||||
|
unsigned TimeZoneUtil::format(char* buffer, size_t bufferSize, USHORT timeZone)
|
||||||
{
|
{
|
||||||
char* p = buffer;
|
char* p = buffer;
|
||||||
|
|
||||||
SSHORT displacement = (SSHORT) zone;
|
if (isOffset(timeZone))
|
||||||
|
{
|
||||||
|
SSHORT displacement = offsetZoneToDisplacement(timeZone);
|
||||||
|
|
||||||
*p++ = displacement < 0 ? '-' : '+';
|
*p++ = displacement < 0 ? '-' : '+';
|
||||||
|
|
||||||
if (displacement < 0)
|
if (displacement < 0)
|
||||||
displacement = -displacement;
|
displacement = -displacement;
|
||||||
|
|
||||||
sprintf(p, "%2.2d:%2.2d", displacement / 60, displacement % 60);
|
p += fb_utils::snprintf(p, bufferSize - 1, "%2.2d:%2.2d", displacement / 60, displacement % 60);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (MAX_USHORT - timeZone < FB_NELEM(TIME_ZONE_LIST))
|
||||||
|
strncpy(buffer, TIME_ZONE_LIST[MAX_USHORT - timeZone].abbr, bufferSize);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fb_assert(false);
|
||||||
|
strncpy(buffer, "*Invalid*", bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
while (*p)
|
p += strlen(buffer);
|
||||||
p++;
|
}
|
||||||
|
|
||||||
return p - buffer;
|
return p - buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimeZoneUtil::isValidOffset(int sign, int tzh, unsigned tzm)
|
// Returns if the offsets are valid.
|
||||||
|
bool TimeZoneUtil::isValidOffset(int sign, unsigned tzh, unsigned tzm)
|
||||||
{
|
{
|
||||||
fb_assert(sign >= -1 && sign <= 1);
|
fb_assert(sign >= -1 && sign <= 1);
|
||||||
return tzm <= 59 && (tzh < 14 || (tzh == 14 && tzm == 0));
|
return tzm <= 59 && (tzh < 14 || (tzh == 14 && tzm == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extracts the offsets from a offset- or region-based datetime with time zone.
|
||||||
void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign, unsigned* tzh, unsigned* tzm)
|
void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign, unsigned* tzh, unsigned* tzm)
|
||||||
{
|
{
|
||||||
SSHORT offset = (SSHORT) timeStampTz.timestamp_zone;
|
SSHORT displacement;
|
||||||
|
|
||||||
*sign = offset < 0 ? -1 : 1;
|
if (isOffset(timeStampTz.timestamp_zone))
|
||||||
offset = offset < 0 ? -offset : offset;
|
displacement = offsetZoneToDisplacement(timeStampTz.timestamp_zone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ISC_TIMESTAMP ts1 = *(ISC_TIMESTAMP*) &timeStampTz;
|
||||||
|
ISC_TIMESTAMP ts2 = timeStampTzAtZone(timeStampTz, UTC_ZONE);
|
||||||
|
|
||||||
*tzh = offset / 60;
|
displacement =
|
||||||
*tzm = offset % 60;
|
((ts1.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY + ts1.timestamp_time) -
|
||||||
|
(ts2.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY + ts2.timestamp_time)) /
|
||||||
|
(ISC_TIME_SECONDS_PRECISION * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
*sign = displacement < 0 ? -1 : 1;
|
||||||
|
displacement = displacement < 0 ? -displacement : displacement;
|
||||||
|
|
||||||
|
*tzh = displacement / 60;
|
||||||
|
*tzm = displacement % 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISC_TIME TimeZoneUtil::timeTzAtZone(const ISC_TIME_TZ& timeTz, USHORT zone)
|
// Moves a time from one time zone to another.
|
||||||
|
ISC_TIME TimeZoneUtil::timeTzAtZone(const ISC_TIME_TZ& timeTz, USHORT atTimeZone)
|
||||||
{
|
{
|
||||||
SSHORT zoneDisplacement = (SSHORT) zone;
|
ISC_TIMESTAMP_TZ tempTimeStampTz;
|
||||||
|
tempTimeStampTz.timestamp_date = TimeStamp::getCurrentTimeStamp().value().timestamp_date;
|
||||||
|
tempTimeStampTz.timestamp_time = timeTz.time_time;
|
||||||
|
tempTimeStampTz.timestamp_zone = timeTz.time_zone;
|
||||||
|
|
||||||
SLONG ticks = timeTz.time_time -
|
return timeStampTzAtZone(tempTimeStampTz, atTimeZone).timestamp_time;
|
||||||
((SSHORT) timeTz.time_zone - zoneDisplacement) * 60 * ISC_TIME_SECONDS_PRECISION;
|
|
||||||
|
|
||||||
// Make the result positive
|
|
||||||
while (ticks < 0)
|
|
||||||
ticks += TimeStamp::ISC_TICKS_PER_DAY;
|
|
||||||
|
|
||||||
// And make it in the range of values for a day
|
|
||||||
ticks %= TimeStamp::ISC_TICKS_PER_DAY;
|
|
||||||
|
|
||||||
fb_assert(ticks >= 0 && ticks < TimeStamp::ISC_TICKS_PER_DAY);
|
|
||||||
|
|
||||||
return (ISC_TIME) ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ISC_TIMESTAMP TimeZoneUtil::timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT zone)
|
// Moves a timestamp from one time zone to another.
|
||||||
|
ISC_TIMESTAMP TimeZoneUtil::timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT atTimeZone)
|
||||||
{
|
{
|
||||||
SSHORT zoneDisplacement = (SSHORT) zone;
|
SSHORT timeDisplacement = getDisplacement(timeStampTz);
|
||||||
|
|
||||||
SINT64 ticks = timeStampTz.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY + timeStampTz.timestamp_time -
|
SINT64 ticks = timeStampTz.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY + timeStampTz.timestamp_time -
|
||||||
((SSHORT) timeStampTz.timestamp_zone - zoneDisplacement) * 60 * ISC_TIME_SECONDS_PRECISION;
|
(timeDisplacement * 60 * ISC_TIME_SECONDS_PRECISION);
|
||||||
|
|
||||||
|
SSHORT atDisplacement;
|
||||||
|
|
||||||
|
if (isOffset(atTimeZone))
|
||||||
|
atDisplacement = offsetZoneToDisplacement(atTimeZone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ISC_TIMESTAMP tempTimeStampUtc;
|
||||||
|
tempTimeStampUtc.timestamp_date = ticks / TimeStamp::ISC_TICKS_PER_DAY;
|
||||||
|
tempTimeStampUtc.timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
|
||||||
|
|
||||||
|
atDisplacement = getDisplacement(tempTimeStampUtc, atTimeZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
ticks -= -atDisplacement * 60 * ISC_TIME_SECONDS_PRECISION;
|
||||||
|
|
||||||
ISC_TIMESTAMP ts;
|
ISC_TIMESTAMP ts;
|
||||||
ts.timestamp_date = ticks / TimeStamp::ISC_TICKS_PER_DAY;
|
ts.timestamp_date = ticks / TimeStamp::ISC_TICKS_PER_DAY;
|
||||||
@ -160,12 +228,110 @@ ISC_TIMESTAMP TimeZoneUtil::timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampT
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
static void skipSpaces(const char*& p, const char* end)
|
// Gets the displacement that a timestamp (with time zone) is from UTC.
|
||||||
|
static SSHORT getDisplacement(const ISC_TIMESTAMP_TZ& timeStampTz)
|
||||||
{
|
{
|
||||||
while (p < end && (*p == ' ' || *p == '\t'))
|
const USHORT timeZone = timeStampTz.timestamp_zone;
|
||||||
++p;
|
|
||||||
|
if (isOffset(timeZone))
|
||||||
|
return offsetZoneToDisplacement(timeZone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//// FIXME:
|
||||||
|
switch (MAX_USHORT - timeZone)
|
||||||
|
{
|
||||||
|
case 0: // BRT
|
||||||
|
return -(3 * 60);
|
||||||
|
|
||||||
|
case 1: // BRST
|
||||||
|
return -(2 * 60);
|
||||||
|
|
||||||
|
default:
|
||||||
|
fb_assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the displacement necessary to convert a UTC timestamp to another time zone.
|
||||||
|
static SSHORT getDisplacement(const ISC_TIMESTAMP& timeStampUtc, USHORT timeZone)
|
||||||
|
{
|
||||||
|
if (isOffset(timeZone))
|
||||||
|
return offsetZoneToDisplacement(timeZone);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//// FIXME:
|
||||||
|
switch (MAX_USHORT - timeZone)
|
||||||
|
{
|
||||||
|
case 0: // BRT
|
||||||
|
return -(3 * 60);
|
||||||
|
|
||||||
|
case 1: // BRST
|
||||||
|
return -(2 * 60);
|
||||||
|
|
||||||
|
default:
|
||||||
|
fb_assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the time zone is offset-based or false if region-based.
|
||||||
|
static inline bool isOffset(USHORT timeZone)
|
||||||
|
{
|
||||||
|
return timeZone <= TimeZoneUtil::ONE_DAY * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a time zone id from offsets.
|
||||||
|
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm)
|
||||||
|
{
|
||||||
|
if (!TimeZoneUtil::isValidOffset(sign, tzh, tzm))
|
||||||
|
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
|
||||||
|
|
||||||
|
return (USHORT)((tzh * 60 + tzm) * sign + TimeZoneUtil::ONE_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a time zone id from a region.
|
||||||
|
static USHORT makeFromRegion(const char* str, unsigned strLen)
|
||||||
|
{
|
||||||
|
const char* end = str + strLen;
|
||||||
|
|
||||||
|
skipSpaces(str, end);
|
||||||
|
|
||||||
|
const char* start = str;
|
||||||
|
|
||||||
|
while (str < end && ((*str >= 'a' && *str <= 'z') || (*str >= 'A' && *str <= 'Z')))
|
||||||
|
++str;
|
||||||
|
|
||||||
|
unsigned len = str - start;
|
||||||
|
|
||||||
|
skipSpaces(str, end);
|
||||||
|
|
||||||
|
if (str == end)
|
||||||
|
{
|
||||||
|
//// FIXME:
|
||||||
|
for (unsigned i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
|
||||||
|
{
|
||||||
|
const char* abbr = TIME_ZONE_LIST[i].abbr;
|
||||||
|
|
||||||
|
if (len == strlen(abbr) && fb_utils::strnicmp(start, abbr, len) == 0)
|
||||||
|
return MAX_USHORT - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone region"); //// TODO:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the displacement from a offset-based time zone id.
|
||||||
|
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone)
|
||||||
|
{
|
||||||
|
fb_assert(isOffset(timeZone));
|
||||||
|
|
||||||
|
return (SSHORT) (int(timeZone) - TimeZoneUtil::ONE_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a integer number.
|
||||||
static int parseNumber(const char*& p, const char* end)
|
static int parseNumber(const char*& p, const char* end)
|
||||||
{
|
{
|
||||||
const char* start = p;
|
const char* start = p;
|
||||||
@ -179,3 +345,10 @@ static int parseNumber(const char*& p, const char* end)
|
|||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip spaces and tabs.
|
||||||
|
static void skipSpaces(const char*& p, const char* end)
|
||||||
|
{
|
||||||
|
while (p < end && (*p == ' ' || *p == '\t'))
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
@ -34,21 +34,22 @@ namespace Firebird {
|
|||||||
class TimeZoneUtil
|
class TimeZoneUtil
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const USHORT UTC_ZONE = 0;
|
static const unsigned ONE_DAY = 24 * 60; // used for offset encoding
|
||||||
|
static const USHORT UTC_ZONE = ONE_DAY + 0;
|
||||||
static const unsigned MAX_LEN = 6;
|
static const unsigned MAX_LEN = 6;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static USHORT getCurrent();
|
static USHORT getCurrent();
|
||||||
|
|
||||||
static USHORT parse(const char* str, unsigned strLen);
|
static USHORT parse(const char* str, unsigned strLen);
|
||||||
static unsigned format(char* buffer, USHORT zone);
|
static unsigned format(char* buffer, size_t bufferSize, USHORT timeZone);
|
||||||
|
|
||||||
static bool isValidOffset(int sign, int tzh, unsigned tzm);
|
static bool isValidOffset(int sign, unsigned tzh, unsigned tzm);
|
||||||
|
|
||||||
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign, unsigned* tzh, unsigned* tzm);
|
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign, unsigned* tzh, unsigned* tzm);
|
||||||
|
|
||||||
static ISC_TIME timeTzAtZone(const ISC_TIME_TZ& timeTz, USHORT zone);
|
static ISC_TIME timeTzAtZone(const ISC_TIME_TZ& timeTz, USHORT atTimeZone);
|
||||||
static ISC_TIMESTAMP timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT zone);
|
static ISC_TIMESTAMP timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, USHORT atTimeZone);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Firebird
|
} // namespace Firebird
|
||||||
|
@ -2298,7 +2298,7 @@ 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++ = ' ';
|
||||||
p += TimeZoneUtil::format(p, timezone);
|
p += TimeZoneUtil::format(p, sizeof(temp) - (p - temp), timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the text version of the date/time value into the destination
|
// Move the text version of the date/time value into the destination
|
||||||
|
@ -7428,8 +7428,9 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length)
|
|||||||
|
|
||||||
if (dtype == SQL_TIMESTAMP_TZ)
|
if (dtype == SQL_TIMESTAMP_TZ)
|
||||||
{
|
{
|
||||||
strcat(d + strlen(d), " ");
|
size_t len = strlen(d);
|
||||||
TimeZoneUtil::format(d + strlen(d), var->value.asDateTimeTz->timestamp_zone);
|
strcat(d + len, " ");
|
||||||
|
TimeZoneUtil::format(d + len + 1, sizeof(d) - (len + 1), var->value.asDateTimeTz->timestamp_zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(p, "%-*.*s ", length, length, d);
|
sprintf(p, "%-*.*s ", length, length, d);
|
||||||
@ -7448,8 +7449,9 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length)
|
|||||||
|
|
||||||
if (dtype == SQL_TIME_TZ)
|
if (dtype == SQL_TIME_TZ)
|
||||||
{
|
{
|
||||||
strcat(d + strlen(d), " ");
|
size_t len = strlen(d);
|
||||||
TimeZoneUtil::format(d + strlen(d), var->value.asTimeTz->time_zone);
|
strcat(d + len, " ");
|
||||||
|
TimeZoneUtil::format(d + len + 1, sizeof(d) - (len + 1), var->value.asTimeTz->time_zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(p, "%-*.*s ", length, length, d);
|
sprintf(p, "%-*.*s ", length, length, d);
|
||||||
|
Loading…
Reference in New Issue
Block a user