From ad4fb0d0d2f33ec0165f2a9a50a6d8cbcef4ab82 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 14 Mar 2012 23:03:34 +0200 Subject: [PATCH] Improve EncodeDateTime and EncodeTimeOnly APIs Use an explicit argument to tell whether to include the time zone in the output, rather than using some undocumented pointer magic. --- src/backend/utils/adt/date.c | 4 +- src/backend/utils/adt/datetime.c | 65 ++++++++++++---------- src/backend/utils/adt/nabstime.c | 2 +- src/backend/utils/adt/timestamp.c | 7 +-- src/backend/utils/adt/xml.c | 5 +- src/include/utils/datetime.h | 4 +- src/interfaces/ecpg/pgtypeslib/dt.h | 6 +- src/interfaces/ecpg/pgtypeslib/dt_common.c | 59 +++++++++++--------- src/interfaces/ecpg/pgtypeslib/timestamp.c | 3 +- 9 files changed, 84 insertions(+), 71 deletions(-) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 85e8fd00ff..2da4e04a28 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -1131,7 +1131,7 @@ time_out(PG_FUNCTION_ARGS) char buf[MAXDATELEN + 1]; time2tm(time, tm, &fsec); - EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf); + EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -1918,7 +1918,7 @@ timetz_out(PG_FUNCTION_ARGS) char buf[MAXDATELEN + 1]; timetz2tm(time, tm, &fsec, &tz); - EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf); + EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf); result = pstrdup(buf); PG_RETURN_CSTRING(result); diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index f495c3f010..56515f15b9 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -3679,39 +3679,54 @@ EncodeDateOnly(struct pg_tm * tm, int style, char *str) /* EncodeTimeOnly() * Encode time fields only. + * + * tm and fsec are the value to encode, print_tz determines whether to include + * a time zone (the difference between time and timetz types), tz is the + * numeric time zone offset, style is the date style, str is where to write the + * output. */ void -EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, int *tzp, int style, char *str) +EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str) { sprintf(str, "%02d:%02d:", tm->tm_hour, tm->tm_min); str += strlen(str); AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true); - if (tzp != NULL) - EncodeTimezone(str, *tzp, style); + if (print_tz) + EncodeTimezone(str, tz, style); } /* EncodeDateTime() * Encode date and time interpreted as local time. - * Support several date styles: + * + * tm and fsec are the value to encode, print_tz determines whether to include + * a time zone (the difference between timestamp and timestamptz types), tz is + * the numeric time zone offset, tzn is the textual time zone, which if + * specified will be used instead of tz by some styles, style is the date + * style, str is where to write the output. + * + * Supported date styles: * Postgres - day mon hh:mm:ss yyyy tz * SQL - mm/dd/yyyy hh:mm:ss.ss tz * ISO - yyyy-mm-dd hh:mm:ss+/-tz * German - dd.mm.yyyy hh:mm:ss tz * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz - * Variants (affects order of month and day for Postgres and SQL styles): - * US - mm/dd/yyyy - * European - dd/mm/yyyy */ void -EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str) +EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str) { int day; Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR); + /* + * Negative tm_isdst means we have no valid time zone translation. + */ + if (tm->tm_isdst < 0) + print_tz = false; + switch (style) { case USE_ISO_DATES: @@ -3729,14 +3744,8 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, AppendTimestampSeconds(str + strlen(str), tm, fsec); - /* - * tzp == NULL indicates that we don't want *any* time zone info - * in the output string. *tzn != NULL indicates that we have alpha - * time zone info available. tm_isdst != -1 indicates that we have - * a valid time zone translation. - */ - if (tzp != NULL && tm->tm_isdst >= 0) - EncodeTimezone(str, *tzp, style); + if (print_tz) + EncodeTimezone(str, tz, style); if (tm->tm_year <= 0) sprintf(str + strlen(str), " BC"); @@ -3762,12 +3771,12 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, * TZ abbreviations in the Olson database are plain ASCII. */ - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else - EncodeTimezone(str, *tzp, style); + EncodeTimezone(str, tz, style); } if (tm->tm_year <= 0) @@ -3785,12 +3794,12 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, AppendTimestampSeconds(str + strlen(str), tm, fsec); - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else - EncodeTimezone(str, *tzp, style); + EncodeTimezone(str, tz, style); } if (tm->tm_year <= 0) @@ -3819,10 +3828,10 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, sprintf(str + strlen(str), " %04d", (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1)); - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else { /* @@ -3832,7 +3841,7 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, * the date/time parser later. - thomas 2001-10-19 */ sprintf(str + strlen(str), " "); - EncodeTimezone(str, *tzp, style); + EncodeTimezone(str, tz, style); } } diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index d35645df33..dbd9e926c2 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -311,7 +311,7 @@ abstimeout(PG_FUNCTION_ARGS) break; default: abstime2tm(time, &tz, tm, &tzn); - EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); + EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf); break; } diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index db434dcb3c..edcce5f31e 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -215,13 +215,12 @@ timestamp_out(PG_FUNCTION_ARGS) struct pg_tm tt, *tm = &tt; fsec_t fsec; - char *tzn = NULL; char buf[MAXDATELEN + 1]; if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) - EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf); + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), @@ -501,7 +500,7 @@ timestamptz_out(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(dt)) EncodeSpecialTimestamp(dt, buf); else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0) - EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); + EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), @@ -1422,7 +1421,7 @@ timestamptz_to_str(TimestampTz t) if (TIMESTAMP_NOT_FINITE(t)) EncodeSpecialTimestamp(t, buf); else if (timestamp2tm(t, &tz, tm, &fsec, &tzn, NULL) == 0) - EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); + EncodeDateTime(tm, fsec, true, tz, tzn, USE_ISO_DATES, buf); else strlcpy(buf, "(timestamp out of range)", sizeof(buf)); diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index d7b637c1c4..b042e6b36b 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -1987,7 +1987,6 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings) Timestamp timestamp; struct pg_tm tm; fsec_t fsec; - char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(value); @@ -1999,7 +1998,7 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings) errmsg("timestamp out of range"), errdetail("XML does not support infinite timestamp values."))); else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0) - EncodeDateTime(&tm, fsec, NULL, &tzn, USE_XSD_DATES, buf); + EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), @@ -2026,7 +2025,7 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings) errmsg("timestamp out of range"), errdetail("XML does not support infinite timestamp values."))); else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0) - EncodeDateTime(&tm, fsec, &tz, &tzn, USE_XSD_DATES, buf); + EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 358e2a67b0..d73cc8dcfe 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -290,8 +290,8 @@ extern void DateTimeParseError(int dterr, const char *str, extern int DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp); extern void EncodeDateOnly(struct pg_tm * tm, int style, char *str); -extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, int *tzp, int style, char *str); -extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str); +extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str); +extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str); extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str); extern int DecodeSpecial(int field, char *lowtoken, int *val); diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h index ca9f5effcc..269af7822b 100644 --- a/src/interfaces/ecpg/pgtypeslib/dt.h +++ b/src/interfaces/ecpg/pgtypeslib/dt.h @@ -334,12 +334,12 @@ do { \ int DecodeInterval(char **, int *, int, int *, struct tm *, fsec_t *); int DecodeTime(char *, int *, struct tm *, fsec_t *); -int EncodeDateTime(struct tm *, fsec_t, int *, char **, int, char *, bool); -int EncodeInterval(struct tm *, fsec_t, int, char *); +int EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates); +int EncodeInterval(struct tm *tm, fsec_t fsec, int style, char *str); int tm2timestamp(struct tm *, fsec_t, int *, timestamp *); int DecodeUnits(int field, char *lowtoken, int *val); bool CheckDateTokenTables(void); -int EncodeDateOnly(struct tm *, int, char *, bool); +int EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates); int GetEpochTime(struct tm *); int ParseDateTime(char *, char *, char **, int *, int *, char **); int DecodeDateTime(char **, int *, int, int *, struct tm *, fsec_t *, bool); diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c index 45f1f8affd..ff53340079 100644 --- a/src/interfaces/ecpg/pgtypeslib/dt_common.c +++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c @@ -759,7 +759,14 @@ TrimTrailingZeros(char *str) /* EncodeDateTime() * Encode date and time interpreted as local time. - * Support several date styles: + * + * tm and fsec are the value to encode, print_tz determines whether to include + * a time zone (the difference between timestamp and timestamptz types), tz is + * the numeric time zone offset, tzn is the textual time zone, which if + * specified will be used instead of tz by some styles, style is the date + * style, str is where to write the output. + * + * Supported date styles: * Postgres - day mon hh:mm:ss yyyy tz * SQL - mm/dd/yyyy hh:mm:ss.ss tz * ISO - yyyy-mm-dd hh:mm:ss+/-tz @@ -769,12 +776,18 @@ TrimTrailingZeros(char *str) * European - dd/mm/yyyy */ int -EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str, bool EuroDates) +EncodeDateTime(struct tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates) { int day, hour, min; + /* + * Negative tm_isdst means we have no valid time zone translation. + */ + if (tm->tm_isdst < 0) + print_tz = false; + switch (style) { case USE_ISO_DATES: @@ -808,16 +821,10 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha if (tm->tm_year <= 0) sprintf(str + strlen(str), " BC"); - /* - * tzp == NULL indicates that we don't want *any* time zone info - * in the output string. *tzn != NULL indicates that we have alpha - * time zone info available. tm_isdst != -1 indicates that we have - * a valid time zone translation. - */ - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - hour = -(*tzp / SECS_PER_HOUR); - min = (abs(*tzp) / MINS_PER_HOUR) % MINS_PER_HOUR; + hour = -(tz / SECS_PER_HOUR); + min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR; if (min != 0) sprintf(str + strlen(str), "%+03d:%02d", hour, min); else @@ -867,14 +874,14 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha * TZ abbreviations in the Olson database are plain ASCII. */ - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else { - hour = -(*tzp / SECS_PER_HOUR); - min = (abs(*tzp) / MINS_PER_HOUR) % MINS_PER_HOUR; + hour = -(tz / SECS_PER_HOUR); + min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR; if (min != 0) sprintf(str + strlen(str), "%+03d:%02d", hour, min); else @@ -916,14 +923,14 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha if (tm->tm_year <= 0) sprintf(str + strlen(str), " BC"); - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else { - hour = -(*tzp / SECS_PER_HOUR); - min = (abs(*tzp) / MINS_PER_HOUR) % MINS_PER_HOUR; + hour = -(tz / SECS_PER_HOUR); + min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR; if (min != 0) sprintf(str + strlen(str), "%+03d:%02d", hour, min); else @@ -975,10 +982,10 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha if (tm->tm_year <= 0) sprintf(str + strlen(str), " BC"); - if (tzp != NULL && tm->tm_isdst >= 0) + if (print_tz) { - if (*tzn != NULL) - sprintf(str + strlen(str), " %.*s", MAXTZLEN, *tzn); + if (tzn) + sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn); else { /* @@ -987,8 +994,8 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha * avoid formatting something which would be rejected by * the date/time parser later. - thomas 2001-10-19 */ - hour = -(*tzp / SECS_PER_HOUR); - min = (abs(*tzp) / MINS_PER_HOUR) % MINS_PER_HOUR; + hour = -(tz / SECS_PER_HOUR); + min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR; if (min != 0) sprintf(str + strlen(str), " %+03d:%02d", hour, min); else diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c index dc5ff72b60..b0e99766f5 100644 --- a/src/interfaces/ecpg/pgtypeslib/timestamp.c +++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c @@ -354,7 +354,6 @@ PGTYPEStimestamp_to_asc(timestamp tstamp) struct tm tt, *tm = &tt; char buf[MAXDATELEN + 1]; - char *tzn = NULL; fsec_t fsec; int DateStyle = 1; /* this defaults to ISO_DATES, shall we make * it an option? */ @@ -362,7 +361,7 @@ PGTYPEStimestamp_to_asc(timestamp tstamp) if (TIMESTAMP_NOT_FINITE(tstamp)) EncodeSpecialTimestamp(tstamp, buf); else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0) - EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf, 0); + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0); else { errno = PGTYPES_TS_BAD_TIMESTAMP; -- 2.40.0