mirror of
https://github.com/postgres/postgres.git
synced 2025-06-25 01:02:05 +03:00
In my mind there were two categories of open issues
a) ones that are 100% backward (such as the comment about outputting this format) and b) ones that aren't (such as deprecating the current postgresql shorthand of '1Y1M'::interval = 1 year 1 minute in favor of the ISO-8601 'P1Y1M'::interval = 1 year 1 month. Attached is a patch that addressed all the discussed issues that did not break backward compatability, including the ability to output ISO-8601 compliant intervals by setting datestyle to iso8601basic. Interval values can now be written as ISO 8601 time intervals, using the "Format with time-unit designators". This format always starts with the character 'P', followed by a string of values followed by single character time-unit designators. A 'T' separates the date and time parts of the interval. Ron Mayer
This commit is contained in:
@ -21,6 +21,7 @@ typedef double fsec_t;
|
||||
#define USE_ISO_DATES 1
|
||||
#define USE_SQL_DATES 2
|
||||
#define USE_GERMAN_DATES 3
|
||||
#define USE_ISO8601BASIC_DATES 4
|
||||
|
||||
#define DAGO "ago"
|
||||
#define EPOCH "epoch"
|
||||
|
@ -704,6 +704,16 @@ EncodeDateOnly(struct tm * tm, int style, char *str, bool EuroDates)
|
||||
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
|
||||
break;
|
||||
|
||||
case USE_ISO8601BASIC_DATES:
|
||||
/* compatible with ISO date formats */
|
||||
if (tm->tm_year > 0)
|
||||
sprintf(str, "%04d%02d%02d",
|
||||
tm->tm_year, tm->tm_mon, tm->tm_mday);
|
||||
else
|
||||
sprintf(str, "%04d%02d%02d %s",
|
||||
-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
|
||||
break;
|
||||
|
||||
case USE_SQL_DATES:
|
||||
/* compatible with Oracle/Ingres date formats */
|
||||
if (EuroDates)
|
||||
@ -820,6 +830,51 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
|
||||
}
|
||||
break;
|
||||
|
||||
case USE_ISO8601BASIC_DATES:
|
||||
/* Compatible with ISO-8601 date formats */
|
||||
|
||||
sprintf(str, "%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);
|
||||
|
||||
/*
|
||||
* Print fractional seconds if any. The field widths here
|
||||
* should be at least equal to MAX_TIMESTAMP_PRECISION.
|
||||
*
|
||||
* In float mode, don't print fractional seconds before 1 AD,
|
||||
* since it's unlikely there's any precision left ...
|
||||
*/
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
if (fsec != 0)
|
||||
{
|
||||
sprintf((str + strlen(str)), "%02d.%06d", tm->tm_sec, fsec);
|
||||
#else
|
||||
if ((fsec != 0) && (tm->tm_year > 0))
|
||||
{
|
||||
sprintf((str + strlen(str)), "%09.6f", tm->tm_sec + fsec);
|
||||
#endif
|
||||
TrimTrailingZeros(str);
|
||||
}
|
||||
else
|
||||
sprintf((str + strlen(str)), "%02d", tm->tm_sec);
|
||||
|
||||
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))
|
||||
{
|
||||
hour = -(*tzp / 3600);
|
||||
min = ((abs(*tzp) / 60) % 60);
|
||||
sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
|
||||
}
|
||||
break;
|
||||
|
||||
case USE_SQL_DATES:
|
||||
/* Compatible with Oracle/Ingres date formats */
|
||||
|
||||
|
@ -442,6 +442,17 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
|
||||
return (fmask != 0) ? 0 : -1;
|
||||
} /* DecodeInterval() */
|
||||
|
||||
|
||||
/*
|
||||
* Small helper function to avoid cut&paste in EncodeInterval below
|
||||
*/
|
||||
static char * AppendISO8601Fragment(char * cp, int value, char character)
|
||||
{
|
||||
sprintf(cp,"%d%c",value,character);
|
||||
return cp + strlen(cp);
|
||||
}
|
||||
|
||||
|
||||
/* EncodeInterval()
|
||||
* Interpret time structure as a delta time and convert to string.
|
||||
*
|
||||
@ -449,6 +460,14 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
|
||||
* Actually, afaik ISO does not address time interval formatting,
|
||||
* but this looks similar to the spec for absolute date/time.
|
||||
* - thomas 1998-04-30
|
||||
*
|
||||
* Actually, afaik, ISO 8601 does specify formats for "time
|
||||
* intervals...[of the]...format with time-unit designators", which
|
||||
* are pretty ugly. The format looks something like
|
||||
* P1Y1M1DT1H1M1.12345S
|
||||
* If you want this (perhaps for interoperability with computers
|
||||
* rather than humans), datestyle 'iso8601basic' will output these.
|
||||
* - ron 2003-07-14
|
||||
*/
|
||||
int
|
||||
EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
|
||||
@ -465,7 +484,12 @@ EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
|
||||
*/
|
||||
switch (style)
|
||||
{
|
||||
/* compatible with ISO date formats */
|
||||
/* compatible with ISO date formats
|
||||
([ram] Not for ISO 8601, perhaps some other ISO format.
|
||||
but I'm leaving it that way because it's more human
|
||||
readable than ISO8601 time intervals and for backwards
|
||||
compatability.)
|
||||
*/
|
||||
case USE_ISO_DATES:
|
||||
if (tm->tm_year != 0)
|
||||
{
|
||||
@ -533,6 +557,48 @@ EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
|
||||
}
|
||||
break;
|
||||
|
||||
case USE_ISO8601BASIC_DATES:
|
||||
sprintf(cp,"P");
|
||||
cp++;
|
||||
if (tm->tm_year != 0) cp = AppendISO8601Fragment(cp,tm->tm_year,'Y');
|
||||
if (tm->tm_mon != 0) cp = AppendISO8601Fragment(cp,tm->tm_mon ,'M');
|
||||
if (tm->tm_mday != 0) cp = AppendISO8601Fragment(cp,tm->tm_mday,'D');
|
||||
if ((tm->tm_hour != 0) || (tm->tm_min != 0) ||
|
||||
(tm->tm_sec != 0) || (fsec != 0))
|
||||
{
|
||||
sprintf(cp,"T"),
|
||||
cp++;
|
||||
}
|
||||
if (tm->tm_hour != 0) cp = AppendISO8601Fragment(cp,tm->tm_hour,'H');
|
||||
if (tm->tm_min != 0) cp = AppendISO8601Fragment(cp,tm->tm_min ,'M');
|
||||
|
||||
if ((tm->tm_year == 0) && (tm->tm_mon == 0) && (tm->tm_mday == 0) &&
|
||||
(tm->tm_hour == 0) && (tm->tm_min == 0) && (tm->tm_sec == 0) &&
|
||||
(fsec == 0))
|
||||
{
|
||||
sprintf(cp,"T0S"),
|
||||
cp+=2;
|
||||
}
|
||||
else if (fsec != 0)
|
||||
{
|
||||
#ifdef HAVE_INT64_TIMESTAMP
|
||||
sprintf(cp, "%d", abs(tm->tm_sec));
|
||||
cp += strlen(cp);
|
||||
sprintf(cp, ".%6dS", ((fsec >= 0) ? fsec : -(fsec)));
|
||||
#else
|
||||
fsec += tm->tm_sec;
|
||||
sprintf(cp, "%fS", fabs(fsec));
|
||||
#endif
|
||||
TrimTrailingZeros(cp);
|
||||
cp += strlen(cp);
|
||||
}
|
||||
else if (tm->tm_sec != 0)
|
||||
{
|
||||
cp = AppendISO8601Fragment(cp,tm->tm_sec ,'S');
|
||||
cp += strlen(cp);
|
||||
}
|
||||
break;
|
||||
|
||||
case USE_POSTGRES_DATES:
|
||||
default:
|
||||
strcpy(cp, "@ ");
|
||||
|
Reference in New Issue
Block a user