提高timestamp/time/date输出函数的速度

This commit is contained in:
ab2020c
2023-04-24 11:08:14 +08:00
parent ce357b14c9
commit 0837cd8309
2 changed files with 308 additions and 199 deletions

View File

@ -43,9 +43,12 @@ static int DecodeTimezone(const char* str, int* tzp);
static const datetkn* datebsearch(const char* key, const datetkn* base, int nel); static const datetkn* datebsearch(const char* key, const datetkn* base, int nel);
static int DecodeDate(char* str, unsigned int fmask, unsigned int* tmask, bool* is2digits, struct pg_tm* tm); static int DecodeDate(char* str, unsigned int fmask, unsigned int* tmask, bool* is2digits, struct pg_tm* tm);
static int ValidateDate(unsigned int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm* tm); static int ValidateDate(unsigned int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm* tm);
static void TrimTrailingZeros(char* str);
static void AppendTrailingZeros(char* str); static void AppendTrailingZeros(char* str);
static void AppendSeconds(char* cp, int sec, fsec_t fsec, int precision, bool fillzeros); #ifndef HAVE_INT64_TIMESTAMP
static char* TrimTrailingZeros(char* str);
#endif /* HAVE_INT64_TIMESTAMP */
static char* AppendSeconds(char* cp, int sec, fsec_t fsec, int precision, bool fillzeros);
static void AdjustFractSeconds(double frac, struct pg_tm* tm, fsec_t* fsec, int scale); static void AdjustFractSeconds(double frac, struct pg_tm* tm, fsec_t* fsec, int scale);
static void AdjustFractDays(double frac, struct pg_tm* tm, fsec_t* fsec, int scale); static void AdjustFractDays(double frac, struct pg_tm* tm, fsec_t* fsec, int scale);
@ -399,54 +402,120 @@ void GetCurrentTimeUsec(struct pg_tm* tm, fsec_t* fsec, int* tzp)
/* TrimTrailingZeros() /* TrimTrailingZeros()
* ... resulting from printing numbers with full precision. * ... resulting from printing numbers with full precision.
* *
* Returns a pointer to the new end of string. No NUL terminator is put
* there; callers are responsible for NUL terminating str themselves.
*
* Before Postgres 8.4, this always left at least 2 fractional digits, * Before Postgres 8.4, this always left at least 2 fractional digits,
* but conversations on the lists suggest this isn't desired * but conversations on the lists suggest this isn't desired
* since showing '0.10' is misleading with values of precision(1). * since showing '0.10' is misleading with values of precision(1).
*/ */
static void TrimTrailingZeros(char* str) #ifndef HAVE_INT64_TIMESTAMP
static char* TrimTrailingZeros(char* str)
{ {
int len = strlen(str); int len = strlen(str);
while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.') { while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.') {
len--; len--;
*(str + len) = '\0';
} }
return str + len;
} }
#endif
/* /*
* Append sections and fractional seconds (if any) at *cp. * Append seconds and fractional seconds (if any) at *cp.
*
* precision is the max number of fraction digits, fillzeros says to * precision is the max number of fraction digits, fillzeros says to
* pad to two integral-seconds digits. * pad to two integral-seconds digits.
*
* Returns a pointer to the new end of string. No NUL terminator is put
* there; callers are responsible for NUL terminating str themselves.
*
* Note that any sign is stripped from the input seconds values. * Note that any sign is stripped from the input seconds values.
*/ */
static void AppendSeconds(char* cp, int sec, fsec_t fsec, int precision, bool fillzeros) static char* AppendSeconds(char* cp, int sec, fsec_t fsec, int precision, bool fillzeros)
{ {
errno_t rc; Assert(precision >= 0);
if (fsec == 0) {
if (fillzeros)
rc = sprintf_s(cp, MAXDATELEN, "%02d", abs(sec));
else
rc = sprintf_s(cp, MAXDATELEN, "%d", abs(sec));
securec_check_ss(rc, "\0", "\0");
} else {
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (fillzeros) /* fsec_t is just an int32 */
rc = sprintf_s(cp, MAXDATELEN, "%02d.%0*d", abs(sec), precision, (int)Abs(fsec));
else if (fillzeros)
rc = sprintf_s(cp, MAXDATELEN, "%d.%0*d", abs(sec), precision, (int)Abs(fsec)); cp = pg_ultostr_zeropad(cp, Abs(sec), 2);
else
cp = pg_ultostr(cp, Abs(sec));
if (fsec != 0)
{
int32 value = Abs(fsec);
char *end = &cp[precision + 1];
bool gotnonzero = false;
*cp++ = '.';
/*
* Append the fractional seconds part. Note that we don't want any
* trailing zeros here, so since we're building the number in reverse
* we'll skip appending zeros until we've output a non-zero digit.
*/
while (precision--)
{
int32 oldval = value;
int32 remainder;
value /= 10;
remainder = oldval - value * 10;
/* check if we got a non-zero */
if (remainder)
gotnonzero = true;
if (gotnonzero)
cp[precision] = '0' + remainder;
else
end = &cp[precision];
}
/*
* If we still have a non-zero value then precision must have not been
* enough to print the number. We punt the problem to pg_ultostr(),
* which will generate a correct answer in the minimum valid width.
*/
if (value)
return pg_ultostr(cp, Abs(fsec));
return end;
}
else
return cp;
#else #else
/* fsec_t is a double */
if (fsec == 0)
{
if (fillzeros)
return pg_ultostr_zeropad(cp, Abs(sec), 2);
else
return pg_ultostr(cp, Abs(sec));
}
else
{
if (fillzeros) if (fillzeros)
rc = sprintf_s(cp, MAXDATELEN, "%0*.*f", precision + 3, precision, fabs(sec + fsec)); rc = sprintf_s(cp, MAXDATELEN, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
else else
rc = sprintf_s(cp, MAXDATELEN, "%.*f", precision, fabs(sec + fsec)); rc = sprintf_s(cp, MAXDATELEN, "%.*f", precision, fabs(sec + fsec));
#endif
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
TrimTrailingZeros(cp); return TrimTrailingZeros(cp);
} }
#endif /* HAVE_INT64_TIMESTAMP */
} }
/* Variant of above that's specialized to timestamp case */ /*
static void AppendTimestampSeconds(char* cp, struct pg_tm* tm, fsec_t fsec) * Variant of above that's specialized to timestamp case.
*
* Returns a pointer to the new end of string. No NUL terminator is put
* there; callers are responsible for NUL terminating str themselves.
*/
static char* AppendTimestampSeconds(char* cp, struct pg_tm* tm, fsec_t fsec)
{ {
/* /*
* In float mode, don't print fractional seconds before 1 AD, since it's * In float mode, don't print fractional seconds before 1 AD, since it's
@ -456,7 +525,7 @@ static void AppendTimestampSeconds(char* cp, struct pg_tm* tm, fsec_t fsec)
if (tm->tm_year <= 0) if (tm->tm_year <= 0)
fsec = 0; fsec = 0;
#endif #endif
AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true); return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
} }
/* /*
@ -3301,29 +3370,40 @@ static const datetkn* datebsearch(const char* key, const datetkn* base, int nel)
} }
/* EncodeTimezone() /* EncodeTimezone()
* Append representation of a numeric timezone offset to str. * Copies representation of a numeric timezone offset to str.
*
* Returns a pointer to the new end of string. No NUL terminator is put
* there; callers are responsible for NUL terminating str themselves.
*/ */
static void EncodeTimezone(char* str, int tz, int style) static char* EncodeTimezone(char* str, int tz, int style)
{ {
int hour, min, sec; int hour, min, sec;
errno_t rc;
sec = abs(tz); sec = abs(tz);
min = sec / SECS_PER_MINUTE; min = sec / SECS_PER_MINUTE;
sec -= min * SECS_PER_MINUTE; sec -= min * SECS_PER_MINUTE;
hour = min / MINS_PER_HOUR; hour = min / MINS_PER_HOUR;
min -= hour * MINS_PER_HOUR; min -= hour * MINS_PER_HOUR;
str += strlen(str);
/* TZ is negated compared to sign we wish to display ... */ /* TZ is negated compared to sign we wish to display ... */
*str++ = ((tz <= 0) ? '+' : '-'); *str++ = ((tz <= 0) ? '+' : '-');
if (sec != 0) if (sec != 0)
rc = sprintf_s(str, MAXDATELEN, "%02d:%02d:%02d", hour, min, sec); {
str = pg_ultostr_zeropad(str, hour, 2);
*str++ = ':';
str = pg_ultostr_zeropad(str, min, 2);
*str++ = ':';
str = pg_ultostr_zeropad(str, sec, 2);
}
else if (min != 0 || style == USE_XSD_DATES) else if (min != 0 || style == USE_XSD_DATES)
rc = sprintf_s(str, MAXDATELEN, "%02d:%02d", hour, min); {
str = pg_ultostr_zeropad(str, hour, 2);
*str++ = ':';
str = pg_ultostr_zeropad(str, min, 2);
}
else else
rc = sprintf_s(str, MAXDATELEN, "%02d", hour); str = pg_ultostr_zeropad(str, hour, 2);
securec_check_ss(rc, "\0", "\0"); return str;
} }
/* EncodeDateOnly() /* EncodeDateOnly()
@ -3331,71 +3411,72 @@ static void EncodeTimezone(char* str, int tz, int style)
*/ */
void EncodeDateOnly(struct pg_tm* tm, int style, char* str) void EncodeDateOnly(struct pg_tm* tm, int style, char* str)
{ {
errno_t rc;
Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR); Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
size_t str_len = 0;
switch (style) { switch (style) {
case USE_ISO_DATES: case USE_ISO_DATES:
case USE_XSD_DATES: case USE_XSD_DATES:
/* compatible with ISO date formats */ /* compatible with ISO date formats */
if (tm->tm_year > 0) str = pg_ultostr_zeropad(str,
rc = sprintf_s(str, MAXDATELEN + 1, "%04d-%02d-%02d", tm->tm_year, tm->tm_mon, tm->tm_mday); (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
else *str++ = '-';
rc = sprintf_s( str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
str, MAXDATELEN + 1, "%04d-%02d-%02d %s", -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC"); *str++ = '-';
securec_check_ss(rc, "\0", "\0"); str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
break; break;
case USE_SQL_DATES: case USE_SQL_DATES:
/* compatible with A db/Ingres date formats */ /* compatible with A db/Ingres date formats */
if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) {
rc = sprintf_s(str, MAXDATELEN + 1, "%02d/%02d", tm->tm_mday, tm->tm_mon); str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
else *str++ = '/';
rc = sprintf_s(str, MAXDATELEN + 1, "%02d/%02d", tm->tm_mon, tm->tm_mday); str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
securec_check_ss(rc, "\0", "\0"); }
else {
str_len = strlen(str); str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
*str++ = '/';
if (tm->tm_year > 0) str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, "/%04d", tm->tm_year); }
else *str++ = '/';
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, "/%04d %s", -(tm->tm_year - 1), "BC"); str = pg_ultostr_zeropad(str,
securec_check_ss(rc, "\0", "\0"); (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
break; break;
case USE_GERMAN_DATES: case USE_GERMAN_DATES:
/* German-style date format */ /* German-style date format */
rc = sprintf_s(str, MAXDATELEN + 1, "%02d.%02d", tm->tm_mday, tm->tm_mon); str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
securec_check_ss(rc, "\0", "\0"); *str++ = '.';
str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
str_len = strlen(str); *str++ = '.';
str = pg_ultostr_zeropad(str,
if (tm->tm_year > 0) (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, ".%04d", tm->tm_year);
else
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, ".%04d %s", -(tm->tm_year - 1), "BC");
securec_check_ss(rc, "\0", "\0");
break; break;
case USE_POSTGRES_DATES: case USE_POSTGRES_DATES:
default: default:
/* traditional date-only style for openGauss */ /* traditional date-only style for openGauss */
if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) {
rc = sprintf_s(str, MAXDATELEN + 1, "%02d-%02d", tm->tm_mday, tm->tm_mon); str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
else *str++ = '-';
rc = sprintf_s(str, MAXDATELEN + 1, "%02d-%02d", tm->tm_mon, tm->tm_mday); str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
securec_check_ss(rc, "\0", "\0"); }
else {
str_len = strlen(str); str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
*str++ = '-';
if (tm->tm_year > 0) str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, "-%04d", tm->tm_year); }
else *str++ = '-';
rc = sprintf_s(str + str_len, MAXDATELEN + 1 - str_len, "-%04d %s", -(tm->tm_year - 1), "BC"); str = pg_ultostr_zeropad(str,
securec_check_ss(rc, "\0", "\0"); (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
break; break;
} }
if (tm->tm_year <= 0)
{
errno_t rc = memcpy_s(str, 3, " BC", 3); /* Don't copy NUL */
securec_check(rc, "", "");
str += 3;
}
*str = '\0';
} }
/* EncodeTimeOnly() /* EncodeTimeOnly()
@ -3408,17 +3489,14 @@ void EncodeDateOnly(struct pg_tm* tm, int style, char* str)
*/ */
void EncodeTimeOnly(struct pg_tm* tm, fsec_t fsec, bool print_tz, int tz, int style, char* str) void EncodeTimeOnly(struct pg_tm* tm, fsec_t fsec, bool print_tz, int tz, int style, char* str)
{ {
errno_t rc = EOK; str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
*str++ = ':';
/*The length of str is defined where the function is called.*/ str = pg_ultostr_zeropad(str, tm->tm_min, 2);
rc = sprintf_s(str, MAXDATELEN + 1, "%02d:%02d:", tm->tm_hour, tm->tm_min); *str++ = ':';
securec_check_ss(rc, "\0", "\0"); str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
str += strlen(str);
AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
if (print_tz) if (print_tz)
EncodeTimezone(str, tz, style); str = EncodeTimezone(str, tz, style);
*str = '\0';
} }
/* EncodeDateTime() /* EncodeDateTime()
@ -3453,137 +3531,120 @@ void EncodeDateTime(struct pg_tm* tm, fsec_t fsec, bool print_tz, int tz, const
case USE_ISO_DATES: case USE_ISO_DATES:
case USE_XSD_DATES: case USE_XSD_DATES:
/* Compatible with ISO-8601 date formats */ /* Compatible with ISO-8601 date formats */
str = pg_ultostr_zeropad(str,
if (style == USE_ISO_DATES) (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
rc = sprintf_s(str, *str++ = '-';
MAXDATELEN + 1, str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
"%04d-%02d-%02d %02d:%02d:", *str++ = '-';
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
tm->tm_mon, *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
tm->tm_mday, str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
tm->tm_hour, *str++ = ':';
tm->tm_min); str = pg_ultostr_zeropad(str, tm->tm_min, 2);
*str++ = ':';
else str = AppendTimestampSeconds(str, tm, fsec);
rc = sprintf_s(str,
MAXDATELEN + 1,
"%04d-%02d-%02dT%02d:%02d:",
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
tm->tm_mon,
tm->tm_mday,
tm->tm_hour,
tm->tm_min);
securec_check_ss(rc, "\0", "\0");
AppendTimestampSeconds(str + strlen(str), tm, fsec);
if (print_tz) if (print_tz)
EncodeTimezone(str, tz, style); str = EncodeTimezone(str, tz, style);
if (tm->tm_year <= 0) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " BC");
securec_check_ss(rc, "\0", "\0");
}
break; break;
case USE_SQL_DATES: case USE_SQL_DATES:
/* Compatible with A db/Ingres date formats */ /* Compatible with A db/Ingres date formats */
if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) {
if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
rc = sprintf_s(str, MAXDATELEN + 1, "%02d/%02d", tm->tm_mday, tm->tm_mon); *str++ = '/';
else str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
rc = sprintf_s(str, MAXDATELEN + 1, "%02d/%02d", tm->tm_mon, tm->tm_mday); } else {
securec_check_ss(rc, "\0", "\0"); str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
rc = sprintf_s(str + 5, *str++ = '/';
MAXDATELEN - 4, str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
"/%04d %02d:%02d:", }
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), *str++ = '/';
tm->tm_hour, str = pg_ultostr_zeropad(str,
tm->tm_min); (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
securec_check_ss(rc, "\0", "\0"); *str++ = ' ';
str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
AppendTimestampSeconds(str + strlen(str), tm, fsec); *str++ = ':';
str = pg_ultostr_zeropad(str, tm->tm_min, 2);
*str++ = ':';
str = AppendTimestampSeconds(str, tm, fsec);
/* /*
* Note: the uses of %.*s in this function would be risky if the * Note: the uses of %.*s in this function would be risky if the
* timezone names ever contain non-ASCII characters. However, all * timezone names ever contain non-ASCII characters. However, all
* TZ abbreviations in the Olson database are plain ASCII. * TZ abbreviations in the IANA database are plain ASCII.
*/ */
if (print_tz) { if (print_tz) {
if (NULL != tzn) { if (NULL != tzn) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " %.*s", MAXTZLEN, tzn); rc = sprintf_s(str, MAXDATELEN + 1, " %.*s", MAXTZLEN, tzn);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
str += strlen(str);
} else } else
EncodeTimezone(str, tz, style); str = EncodeTimezone(str, tz, style);
}
if (tm->tm_year <= 0) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " BC");
securec_check_ss(rc, "\0", "\0");
} }
break; break;
case USE_GERMAN_DATES: case USE_GERMAN_DATES:
/* German variant on European style */ /* German variant on European style */
rc = sprintf_s(str, MAXDATELEN + 1, "%02d.%02d", tm->tm_mday, tm->tm_mon); str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
securec_check_ss(rc, "\0", "\0"); *str++ = '.';
str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
rc = sprintf_s(str + 5, *str++ = '.';
MAXDATELEN - 4, str = pg_ultostr_zeropad(str,
".%04d %02d:%02d:", (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), *str++ = ' ';
tm->tm_hour, str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
tm->tm_min); *str++ = ':';
securec_check_ss(rc, "\0", "\0"); str = pg_ultostr_zeropad(str, tm->tm_min, 2);
*str++ = ':';
AppendTimestampSeconds(str + strlen(str), tm, fsec); str = AppendTimestampSeconds(str, tm, fsec);
if (print_tz) { if (print_tz) {
if (NULL != tzn) { if (NULL != tzn) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " %.*s", MAXTZLEN, tzn); rc = sprintf_s(str, MAXDATELEN + 1, " %.*s", MAXTZLEN, tzn);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
str += strlen(str);
} else } else
EncodeTimezone(str, tz, style); str = EncodeTimezone(str, tz, style);
}
if (tm->tm_year <= 0) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " BC");
securec_check_ss(rc, "\0", "\0");
} }
break; break;
case USE_POSTGRES_DATES: case USE_POSTGRES_DATES:
default: default:
/* Backward-compatible with traditional openGauss abstime dates */ /* Backward-compatible with traditional openGauss abstime dates */
day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
tm->tm_wday = j2day(day); tm->tm_wday = j2day(day);
rc = memcpy_s(str, 3, days[tm->tm_wday], 3);
rc = strncpy_s(str, MAXDATELEN + 1, days[tm->tm_wday], 3); securec_check(rc, "", "");
securec_check(rc, "\0", "\0"); str += 3;
rc = strcpy_s(str + 3, MAXDATELEN - 2, " "); *str++ = ' ';
securec_check(rc, "\0", "\0"); if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) {
str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
if (u_sess->time_cxt.DateOrder == DATEORDER_DMY) *str++ = ' ';
rc = sprintf_s(str + 4, MAXDATELEN - 3, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]); rc = memcpy_s(str, 3, months[tm->tm_mon - 1], 3);
else securec_check(rc, "", "");
rc = sprintf_s(str + 4, MAXDATELEN - 3, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday); str += 3;
securec_check_ss(rc, "\0", "\0"); } else {
rc = sprintf_s(str + 10, MAXDATELEN - 9, " %02d:%02d:", tm->tm_hour, tm->tm_min); rc = memcpy_s(str, 3, months[tm->tm_mon - 1], 3);
securec_check_ss(rc, "\0", "\0"); securec_check(rc, "", "");
AppendTimestampSeconds(str + strlen(str), tm, fsec); str += 3;
*str++ = ' ';
rc = sprintf_s(str + strlen(str), str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
MAXDATELEN + 1 - strlen(str), }
" %04d", *str++ = ' ';
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)); str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
securec_check_ss(rc, "\0", "\0"); *str++ = ':';
str = pg_ultostr_zeropad(str, tm->tm_min, 2);
*str++ = ':';
str = AppendTimestampSeconds(str, tm, fsec);
*str++ = ' ';
str = pg_ultostr_zeropad(str,
(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
if (print_tz) { if (print_tz) {
if (NULL != tzn) { if (NULL != tzn) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " %.*s", MAXTZLEN, tzn); rc = sprintf_s(str, MAXDATELEN + 1, " %.*s", MAXTZLEN, tzn);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
str += strlen(str);
} else { } else {
/* /*
* We have a time zone, but no string version. Use the * We have a time zone, but no string version. Use the
@ -3591,18 +3652,18 @@ void EncodeDateTime(struct pg_tm* tm, fsec_t fsec, bool print_tz, int tz, const
* avoid formatting something which would be rejected by * avoid formatting something which would be rejected by
* the date/time parser later. - thomas 2001-10-19 * the date/time parser later. - thomas 2001-10-19
*/ */
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " "); *str++ = ' ';
securec_check_ss(rc, "\0", "\0"); str = EncodeTimezone(str, tz, style);
EncodeTimezone(str, tz, style);
} }
} }
if (tm->tm_year <= 0) {
rc = sprintf_s(str + strlen(str), MAXDATELEN + 1 - strlen(str), " BC");
securec_check_ss(rc, "\0", "\0");
}
break; break;
} }
if (tm->tm_year <= 0) {
rc = memcpy_s(str, 3, " BC", 3); /* Don't copy NUL */
securec_check(rc, "", "");
str += 3;
}
*str = '\0';
} }
/* /*
@ -3783,7 +3844,8 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
abs(min)); abs(min));
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
cp += strlen(cp); cp += strlen(cp);
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
} }
/* the format for has_year_month */ /* the format for has_year_month */
else if (has_year_month) { else if (has_year_month) {
@ -3864,7 +3926,8 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
abs(min)); abs(min));
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
cp += strlen(cp); cp += strlen(cp);
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
} else if (has_year_month) { } else if (has_year_month) {
rc = sprintf_s(cp, curlen, "%d-%d", year, mon); rc = sprintf_s(cp, curlen, "%d-%d", year, mon);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
@ -3872,12 +3935,14 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
rc = sprintf_s(cp, curlen, "%d %d:%02d:", mday, hour, min); rc = sprintf_s(cp, curlen, "%d %d:%02d:", mday, hour, min);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
cp += strlen(cp); cp += strlen(cp);
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
} else { } else {
rc = sprintf_s(cp, curlen, "%d:%02d:", hour, min); rc = sprintf_s(cp, curlen, "%d:%02d:", hour, min);
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
cp += strlen(cp); cp += strlen(cp);
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
} }
} break; } break;
@ -3900,8 +3965,7 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
if (sec != 0 || fsec != 0) { if (sec != 0 || fsec != 0) {
if (sec < 0 || fsec < 0) if (sec < 0 || fsec < 0)
*cp++ = '-'; *cp++ = '-';
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
cp += strlen(cp);
*cp++ = 'S'; *cp++ = 'S';
*cp++ = '\0'; *cp++ = '\0';
} }
@ -3930,7 +3994,8 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
abs(min)); abs(min));
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
cp += strlen(cp); cp += strlen(cp);
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
} }
break; break;
@ -3960,10 +4025,7 @@ void EncodeInterval(struct pg_tm* tm, fsec_t fsec, int style, char* str)
*cp++ = '-'; *cp++ = '-';
curlen--; curlen--;
} }
AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false); cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
int len = strlen(cp);
cp += len;
curlen -= len;
rc = sprintf_s(cp, curlen, " sec%s", (abs(sec) != 1 || fsec != 0) ? "s" : ""); rc = sprintf_s(cp, curlen, " sec%s", (abs(sec) != 1 || fsec != 0) ? "s" : "");
securec_check_ss(rc, "\0", "\0"); securec_check_ss(rc, "\0", "\0");
is_zero = FALSE; is_zero = FALSE;

View File

@ -602,6 +602,53 @@ pg_ultostr(char *str, uint32 value)
return str + len; return str + len;
} }
/*
* pg_ultostr_zeropad
* Converts 'value' into a decimal string representation stored at 'str'.
* 'minwidth' specifies the minimum width of the result; any extra space
* is filled up by prefixing the number with zeros.
*
* Returns the ending address of the string result (the last character written
* plus 1). Note that no NUL terminator is written.
*
* The intended use-case for this function is to build strings that contain
* multiple individual numbers, for example:
*
* str = pg_ltostr_zeropad(str, hours, 2);
* *str++ = ':';
* str = pg_ltostr_zeropad(str, mins, 2);
* *str++ = ':';
* str = pg_ltostr_zeropad(str, secs, 2);
* *str = '\0';
*
* Note: Caller must ensure that 'str' points to enough memory to hold the
* result.
*/
char* pg_ultostr_zeropad(char* str, uint32 value, int32 minwidth)
{
int len;
errno_t rc = EOK;
Assert(minwidth > 0);
if (value < 100 && minwidth == 2) /* Short cut for common case */
{
rc = memcpy_s(str, 2, DIGIT_TABLE + value * 2, 2);
securec_check(rc, "\0", "\0");
return str + 2;
}
len = pg_ultoa_n(value, str);
if (len >= minwidth)
return str + len;
rc = memmove_s(str + minwidth - len, len, str, len);
securec_check(rc, "\0", "\0");
rc = memset_s(str, minwidth - len, '0', minwidth - len);
securec_check(rc, "\0", "\0");
return str + minwidth;
}
/* /*
* pg_strtouint64 * pg_strtouint64
* Converts 'str' into an unsigned 64-bit integer. * Converts 'str' into an unsigned 64-bit integer.