From 4ebb0cf9c30c1e477d5e2dfcc1f2c016c3f8bbcf Mon Sep 17 00:00:00 2001 From: Bruce Momjian <bruce@momjian.us> Date: Fri, 16 Feb 2007 03:39:46 +0000 Subject: [PATCH] Add two new format fields for use with to_char(), to_date() and to_timestamp(): - ID for day-of-week - IDDD for day-of-year This makes it possible to convert ISO week dates to and from text fully represented in either week ('IYYY-IW-ID') or day-of-year ('IYYY-IDDD') format. I have also added an 'isoyear' field for use with extract / date_part. Brendan Jurd --- doc/src/sgml/func.sgml | 58 +++- src/backend/utils/adt/date.c | 3 +- src/backend/utils/adt/datetime.c | 3 +- src/backend/utils/adt/formatting.c | 171 ++++++++---- src/backend/utils/adt/timestamp.c | 70 ++++- src/include/utils/datetime.h | 3 +- src/include/utils/timestamp.h | 7 +- src/interfaces/ecpg/pgtypeslib/dt.h | 3 +- src/test/regress/expected/timestamp.out | 247 +++++++++++++++++ src/test/regress/expected/timestamptz.out | 309 +++++++++++++++++----- src/test/regress/sql/timestamp.sql | 25 ++ src/test/regress/sql/timestamptz.sql | 25 +- 12 files changed, 773 insertions(+), 151 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index ae9e9ec1058..76b150a9d22 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.358 2007/02/14 18:46:08 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.359 2007/02/16 03:39:44 momjian Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -4539,7 +4539,7 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); </row> <row> <entry><literal>I</literal></entry> - <entry>last digits of ISO year</entry> + <entry>last digit of ISO year</entry> </row> <row> <entry><literal>BC</literal> or <literal>B.C.</literal> or @@ -4607,6 +4607,10 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); <entry><literal>DDD</literal></entry> <entry>day of year (001-366)</entry> </row> + <row> + <entry><literal>IDDD</literal></entry> + <entry>ISO day of year (001-371; day 1 of the year is Monday of the first ISO week.)</entry> + </row> <row> <entry><literal>DD</literal></entry> <entry>day of month (01-31)</entry> @@ -4615,6 +4619,10 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); <entry><literal>D</literal></entry> <entry>day of week (1-7; Sunday is 1)</entry> </row> + <row> + <entry><literal>ID</literal></entry> + <entry>ISO day of week (1-7; Monday is 1)</entry> + </row> <row> <entry><literal>W</literal></entry> <entry>week of month (1-5) (The first week starts on the first day of the month.)</entry> @@ -4625,7 +4633,7 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); </row> <row> <entry><literal>IW</literal></entry> - <entry>ISO week number of year (The first Thursday of the new year is in week 1.)</entry> + <entry>ISO week number of year (1 - 53; the first Thursday of the new year is in week 1.)</entry> </row> <row> <entry><literal>CC</literal></entry> @@ -4791,6 +4799,27 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); </para> </listitem> + <listitem> + <para> + An ISO week date (as distinct from a Gregorian date) can be specified to <function>to_timestamp</function> and <function>to_date</function> in one of two ways: + <itemizedlist> + <listitem> + <para> + Year, week and weekday, for example <literal>to_date('2006-42-4', 'IYYY-IW-ID')</literal> returns the date <literal>2006-10-19</literal>. If you omit the weekday it is assumed to be 1 (Monday). + </para> + </listitem> + <listitem> + <para> + Year and day of year, for example <literal>to_date('2006-291', 'IYYY-IDDD')</literal> also returns <literal>2006-10-19</literal>. + </para> + </listitem> + </itemizedlist> + </para> + <para> + Attempting to construct a date using a mixture of ISO week and Gregorian date fields is nonsensical, and could yield unexpected results. In the context of an ISO year, the concept of a 'month' or 'day of month' has no meaning. In the context of a Gregorian year, the ISO week has no meaning. Users should take care to keep Gregorian and ISO date specifications separate. + </para> + </listitem> + <listitem> <para> Millisecond (<literal>MS</literal>) and microsecond (<literal>US</literal>) @@ -5776,6 +5805,29 @@ SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 20:38:40'); </listitem> </varlistentry> + <varlistentry> + <term><literal>isoyear</literal></term> + <listitem> + <para> + The <acronym>ISO</acronym> 8601 year that the date falls in (not applicable to intervals). + </para> + +<screen> +SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-01'); +<lineannotation>Result: </lineannotation><computeroutput>2005</computeroutput> +SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-02'); +<lineannotation>Result: </lineannotation><computeroutput>2006</computeroutput> +</screen> + + <para> + Each <acronym>ISO</acronym> year begins with the Monday of the week containing the 4th of January, so in early January or late December the <acronym>ISO</acronym> year may be different from the Gregorian year. See the <literal>week</literal> field for more information. + </para> + <para> + This field is not available in PostgreSQL releases prior to 8.3. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>microseconds</literal></term> <listitem> diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 2360419bb77..c3a6415634b 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.127 2007/01/05 22:19:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.128 2007/02/16 03:39:44 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1748,6 +1748,7 @@ time_part(PG_FUNCTION_ARGS) case DTK_DECADE: case DTK_CENTURY: case DTK_MILLENNIUM: + case DTK_ISOYEAR: default: ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 10fdf66ac87..18226b55082 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.175 2007/01/05 22:19:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.176 2007/02/16 03:39:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -125,6 +125,7 @@ static const datetkn datetktbl[] = { {"h", UNITS, DTK_HOUR}, /* "hour" */ {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */ {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */ + {"isoyear", UNITS, DTK_ISOYEAR}, /* year in terms of the ISO week date */ {"j", UNITS, DTK_JULIAN}, {"jan", MONTH, 1}, {"january", MONTH, 1}, diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 80de2f20e4c..eff890c59b3 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.124 2007/02/14 05:10:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.125 2007/02/16 03:39:45 momjian Exp $ * * * Portions Copyright (c) 1999-2007, PostgreSQL Global Development Group @@ -379,6 +379,7 @@ typedef struct ddd, mm, ms, + iyear, year, bc, iw, @@ -528,7 +529,7 @@ static KeySuffix DCH_suff[] = { * position or -1 if char is not used in the KeyWord. Search example for * string "MM": * 1) see in index to index['M' - 32], - * 2) take keywords position (enum DCH_MM) from index + * 2) take keywords position (enum DCH_MI) from index * 3) run sequential search in keywords[] from this position * * ---------- @@ -554,6 +555,8 @@ typedef enum DCH_HH24, DCH_HH12, DCH_HH, + DCH_IDDD, + DCH_ID, DCH_IW, DCH_IYYY, DCH_IYY, @@ -598,6 +601,8 @@ typedef enum DCH_hh24, DCH_hh12, DCH_hh, + DCH_iddd, + DCH_id, DCH_iw, DCH_iyyy, DCH_iyy, @@ -696,13 +701,15 @@ static const KeyWord DCH_keywords[] = { {"HH24", 4, dch_time, DCH_HH24, TRUE}, /* H */ {"HH12", 4, dch_time, DCH_HH12, TRUE}, {"HH", 2, dch_time, DCH_HH, TRUE}, - {"IW", 2, dch_date, DCH_IW, TRUE}, /* I */ + {"IDDD", 4, dch_date, DCH_IDDD, TRUE}, /* I */ + {"ID", 2, dch_date, DCH_ID, TRUE}, + {"IW", 2, dch_date, DCH_IW, TRUE}, {"IYYY", 4, dch_date, DCH_IYYY, TRUE}, {"IYY", 3, dch_date, DCH_IYY, TRUE}, {"IY", 2, dch_date, DCH_IY, TRUE}, {"I", 1, dch_date, DCH_I, TRUE}, {"J", 1, dch_date, DCH_J, TRUE}, /* J */ - {"MI", 2, dch_time, DCH_MI, TRUE}, + {"MI", 2, dch_time, DCH_MI, TRUE}, /* M */ {"MM", 2, dch_date, DCH_MM, TRUE}, {"MONTH", 5, dch_date, DCH_MONTH, FALSE}, {"MON", 3, dch_date, DCH_MON, FALSE}, @@ -740,7 +747,9 @@ static const KeyWord DCH_keywords[] = { {"hh24", 4, dch_time, DCH_HH24, TRUE}, /* h */ {"hh12", 4, dch_time, DCH_HH12, TRUE}, {"hh", 2, dch_time, DCH_HH, TRUE}, - {"iw", 2, dch_date, DCH_IW, TRUE}, /* i */ + {"iddd", 4, dch_date, DCH_IDDD, TRUE}, /* i */ + {"id", 2, dch_date, DCH_ID, TRUE}, + {"iw", 2, dch_date, DCH_IW, TRUE}, {"iyyy", 4, dch_date, DCH_IYYY, TRUE}, {"iyy", 3, dch_date, DCH_IYY, TRUE}, {"iy", 2, dch_date, DCH_IY, TRUE}, @@ -830,10 +839,10 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1, - DCH_FX, -1, DCH_HH24, DCH_IW, DCH_J, -1, -1, DCH_MI, -1, -1, + DCH_FX, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, -1, DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY, -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc, - DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iw, DCH_j, -1, -1, DCH_mi, + DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi, -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww, -1, DCH_y_yyy, -1, -1, -1, -1 @@ -2429,9 +2438,13 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, return strlen(p_inout); case DCH_DDD: + case DCH_IDDD: if (is_to_char) { - sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday); + sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, + (arg == DCH_DDD) ? + tm->tm_yday : + date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday)); if (S_THth(suf)) str_numth(p_inout, inout, S_TH_TYPE(suf)); return strlen(p_inout); @@ -2473,10 +2486,14 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, } break; case DCH_D: + case DCH_ID: INVALID_FOR_INTERVAL; if (is_to_char) { - sprintf(inout, "%d", tm->tm_wday + 1); + if (arg == DCH_D) + sprintf(inout, "%d", tm->tm_wday + 1); + else + sprintf(inout, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); if (S_THth(suf)) str_numth(p_inout, inout, S_TH_TYPE(suf)); return strlen(p_inout); @@ -2484,7 +2501,8 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, else { sscanf(inout, "%1d", &tmfc->d); - tmfc->d--; + if (arg == DCH_D) + tmfc->d--; return strspace_len(inout) + 1 + SKIP_THth(suf); } break; @@ -2625,15 +2643,18 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, } else { + int *field; + field = (arg == DCH_YYYY) ? &tmfc->year : &tmfc->iyear; + if (S_FM(suf) || is_next_separator(node)) { - sscanf(inout, "%d", &tmfc->year); + sscanf(inout, "%d", field); tmfc->yysz = 4; return strdigits_len(inout) + SKIP_THth(suf); } else { - sscanf(inout, "%04d", &tmfc->year); + sscanf(inout, "%04d", field); tmfc->yysz = 4; return strspace_len(inout) + 4 + SKIP_THth(suf); } @@ -2657,16 +2678,19 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, } else { - sscanf(inout, "%03d", &tmfc->year); + int *field; + field = (arg == DCH_YYY) ? &tmfc->year : &tmfc->iyear; + + sscanf(inout, "%03d", field); /* * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ... * '099' = 2000 ... 2099 */ - if (tmfc->year >= 100) - tmfc->year += 1000; + if (*field >= 100) + *field += 1000; else - tmfc->year += 2000; + *field += 2000; tmfc->yysz = 3; return strspace_len(inout) + 3 + SKIP_THth(suf); } @@ -2689,16 +2713,19 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, } else { - sscanf(inout, "%02d", &tmfc->year); + int *field; + field = (arg == DCH_YY) ? &tmfc->year : &tmfc->iyear; + + sscanf(inout, "%02d", field); /* * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99' * = 1970 ... 1999 */ - if (tmfc->year < 70) - tmfc->year += 2000; + if (*field < 70) + *field += 2000; else - tmfc->year += 1900; + *field += 1900; tmfc->yysz = 2; return strspace_len(inout) + 2 + SKIP_THth(suf); } @@ -2721,12 +2748,15 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, } else { - sscanf(inout, "%1d", &tmfc->year); + int *field; + field = (arg == DCH_Y) ? &tmfc->year : &tmfc->iyear; + + sscanf(inout, "%1d", field); /* * 1-digit year: always +2000 */ - tmfc->year += 2000; + *field += 2000; tmfc->yysz = 1; return strspace_len(inout) + 1 + SKIP_THth(suf); } @@ -3297,7 +3327,8 @@ do_to_timestamp(text *date_txt, text *fmt, { FormatNode *format; TmFromChar tmfc; - int fmt_len; + int fmt_len, + year; ZERO_tm(tm); *fsec = 0; @@ -3447,7 +3478,13 @@ do_to_timestamp(text *date_txt, text *fmt, break; } - if (tmfc.year) + /* + * Only one year value is used. If iyear (the ISO year) is defined, it takes precedence. + * Otherwise year (the Gregorian year) is used. + */ + year = (tmfc.iyear) ? tmfc.iyear : tmfc.year; + + if (year) { /* * If CC and YY (or Y) are provided, use YY as 2 low-order digits @@ -3458,14 +3495,14 @@ do_to_timestamp(text *date_txt, text *fmt, */ if (tmfc.cc && tmfc.yysz <= 2) { - tm->tm_year = tmfc.year % 100; + tm->tm_year = year % 100; if (tm->tm_year) tm->tm_year += (tmfc.cc - 1) * 100; else tm->tm_year = tmfc.cc * 100; } else - tm->tm_year = tmfc.year; + tm->tm_year = year; } else if (tmfc.cc) /* use first year of century */ tm->tm_year = (tmfc.cc - 1) * 100 + 1; @@ -3485,48 +3522,78 @@ do_to_timestamp(text *date_txt, text *fmt, j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); if (tmfc.iw) - isoweek2date(tmfc.iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + { + /* + * Since the user has employed the IW field, it is assumed that the value in tmfc.d + * is in ISO day-of-week form (1 = Monday), as set by the ID field. Mixing IW and D + * will yield weird results. + * + * tmfc.iyear must have been set (e.g., with IYYY) for this to work properly (an ISO week + * without an ISO year is meaningless). + * + * If tmfc.d is not set, then the date is left at the beginning of the ISO week (Monday). + */ + if (tmfc.d) + { + isoweekdate2date(tmfc.iw, tmfc.d, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + } + else + isoweek2date(tmfc.iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + } + if (tmfc.d) tm->tm_wday = tmfc.d; if (tmfc.dd) tm->tm_mday = tmfc.dd; - if (tmfc.ddd) + if (tmfc.ddd && !tmfc.iyear) tm->tm_yday = tmfc.ddd; if (tmfc.mm) tm->tm_mon = tmfc.mm; - /* - * we don't ignore DDD - */ if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1)) { - /* count mday and mon from yday */ - int *y, - i; + /* + * If the iyear field is set, the value of ddd is taken to be an ISO day-of-year. + * Otherwise, it is a Gregorian day-of-year. + * Either way, since the month and day fields have not been set by some other means, + * the value of ddd will be used to compute them. + */ + if (tmfc.iyear) + { + int j0; // zeroth day of the ISO year, in Julian + j0 = isoweek2j(tmfc.iyear, 1) - 1; - int ysum[2][13] = { - {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0}, - {31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}}; + j2date(j0 + tmfc.ddd, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + } + else + { + int *y, + i; - if (!tm->tm_year) - ereport(ERROR, - (errcode(ERRCODE_INVALID_DATETIME_FORMAT), - errmsg("cannot calculate day of year without year information"))); + int ysum[2][13] = { + {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 0}, + {31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 0}}; - y = ysum[isleap(tm->tm_year)]; + if (!tm->tm_year) + ereport(ERROR, + (errcode(ERRCODE_INVALID_DATETIME_FORMAT), + errmsg("cannot calculate day of year without year information"))); - for (i = 0; i <= 11; i++) - { - if (tm->tm_yday < y[i]) - break; - } - if (tm->tm_mon <= 1) - tm->tm_mon = i + 1; + y = ysum[isleap(tm->tm_year)]; - if (tm->tm_mday <= 1) - tm->tm_mday = i == 0 ? tm->tm_yday : - tm->tm_yday - y[i - 1]; + for (i = 0; i <= 11; i++) + { + if (tm->tm_yday < y[i]) + break; + } + if (tm->tm_mon <= 1) + tm->tm_mon = i + 1; + + if (tm->tm_mday <= 1) + tm->tm_mday = i == 0 ? tm->tm_yday : + tm->tm_yday - y[i - 1]; + } } #ifdef HAVE_INT64_TIMESTAMP diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 13dba7cb5c6..7632e1b8961 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.171 2007/01/05 22:19:42 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.172 2007/02/16 03:39:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -3749,32 +3749,57 @@ interval_trunc(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } -/* isoweek2date() - * Convert ISO week of year number to date. - * The year field must be specified with the ISO year! - * karel 2000/08/07 +/* isoweek2j() + * + * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week. + * Julian days are used to convert between ISO week dates and Gregorian dates. */ -void -isoweek2date(int woy, int *year, int *mon, int *mday) +int +isoweek2j(int year, int week) { int day0, - day4, - dayn; + day4; - if (!*year) + if (!year) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot calculate week number without year information"))); /* fourth day of current year */ - day4 = date2j(*year, 1, 4); + day4 = date2j(year, 1, 4); /* day0 == offset to first day of week (Monday) */ day0 = j2day(day4 - 1); - dayn = ((woy - 1) * 7) + (day4 - day0); + return ((week - 1) * 7) + (day4 - day0); +} + +/* isoweek2date() + * Convert ISO week of year number to date. + * The year field must be specified with the ISO year! + * karel 2000/08/07 + */ +void +isoweek2date(int woy, int *year, int *mon, int *mday) +{ + j2date(isoweek2j(*year, woy), year, mon, mday); +} + +/* isoweekdate2date() + * + * Convert an ISO 8601 week date (ISO year, ISO week and day of week) into a Gregorian date. + * Populates year, mon, and mday with the correct Gregorian values. + * year must be passed in as the ISO year. + */ +void +isoweekdate2date(int isoweek, int isowday, int *year, int *mon, int *mday) +{ + int jday; + + jday = isoweek2j(*year, isoweek); + jday += isowday - 1; - j2date(dayn, year, mon, mday); + j2date(jday, year, mon, mday); } /* date2isoweek() @@ -3887,6 +3912,17 @@ date2isoyear(int year, int mon, int mday) } +/* date2isoyearday() + * + * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day. + * Possible return values are 1 through 371 (364 in non-leap years). + */ +int +date2isoyearday(int year, int mon, int mday) +{ + return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1; +} + /* timestamp_part() * Extract specified field from timestamp. */ @@ -4029,6 +4065,10 @@ timestamp_part(PG_FUNCTION_ARGS) #endif break; + case DTK_ISOYEAR: + result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); + break; + case DTK_TZ: case DTK_TZ_MINUTE: case DTK_TZ_HOUR: @@ -4256,6 +4296,10 @@ timestamptz_part(PG_FUNCTION_ARGS) #endif break; + case DTK_ISOYEAR: + result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); + break; + default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 0b19143c286..fc7b16fdb47 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.63 2007/01/05 22:19:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.64 2007/02/16 03:39:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -165,6 +165,7 @@ #define DTK_DOY 33 #define DTK_TZ_HOUR 34 #define DTK_TZ_MINUTE 35 +#define DTK_ISOYEAR 36 /* diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 4ff1ffe869e..0d31cc02940 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.66 2007/01/05 22:19:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.67 2007/02/16 03:39:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -331,8 +331,11 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2); /* timestamp comparison works for timestamptz also */ #define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2) -extern void isoweek2date(int woy, int *year, int *mon, int *mday); +extern int isoweek2j(int year, int week); +extern void isoweek2date(int woy, int *year, int *mon, int *mday); +extern void isoweekdate2date(int isoweek, int isowday, int *year, int *mon, int *mday); extern int date2isoweek(int year, int mon, int mday); extern int date2isoyear(int year, int mon, int mday); +extern int date2isoyearday(int year, int mon, int mday); #endif /* TIMESTAMP_H */ diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h index 95793aabc30..d0bfc00bbdd 100644 --- a/src/interfaces/ecpg/pgtypeslib/dt.h +++ b/src/interfaces/ecpg/pgtypeslib/dt.h @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt.h,v 1.34 2006/03/11 04:38:39 momjian Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt.h,v 1.35 2007/02/16 03:39:45 momjian Exp $ */ #ifndef DT_H #define DT_H @@ -157,6 +157,7 @@ typedef double fsec_t; #define DTK_DOY 33 #define DTK_TZ_HOUR 34 #define DTK_TZ_MINUTE 35 +#define DTK_ISOYEAR 36 /* diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index c26cb9ec993..9f585b68f6b 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -703,6 +703,69 @@ SELECT '' AS "54", d1 as "timestamp", | Mon Jan 01 17:32:01 2001 | 1 | 1000 | 1000000 (55 rows) +SELECT '' AS "54", d1 as "timestamp", + date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, + date_part( 'dow', d1) AS dow + FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; + 54 | timestamp | isoyear | week | dow +----+-----------------------------+---------+------+----- + | Thu Jan 01 00:00:00 1970 | 1970 | 1 | 4 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:02 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.40 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.50 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.60 1997 | 1997 | 7 | 1 + | Thu Jan 02 00:00:00 1997 | 1997 | 1 | 4 + | Thu Jan 02 03:04:05 1997 | 1997 | 1 | 4 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Jun 10 17:32:01 1997 | 1997 | 24 | 2 + | Sat Sep 22 18:19:20 2001 | 2001 | 38 | 6 + | Wed Mar 15 08:14:01 2000 | 2000 | 11 | 3 + | Wed Mar 15 13:14:02 2000 | 2000 | 11 | 3 + | Wed Mar 15 12:14:03 2000 | 2000 | 11 | 3 + | Wed Mar 15 03:14:04 2000 | 2000 | 11 | 3 + | Wed Mar 15 02:14:05 2000 | 2000 | 11 | 3 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:00 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Jun 10 18:32:01 1997 | 1997 | 24 | 2 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Feb 11 17:32:01 1997 | 1997 | 7 | 2 + | Wed Feb 12 17:32:01 1997 | 1997 | 7 | 3 + | Thu Feb 13 17:32:01 1997 | 1997 | 7 | 4 + | Fri Feb 14 17:32:01 1997 | 1997 | 7 | 5 + | Sat Feb 15 17:32:01 1997 | 1997 | 7 | 6 + | Sun Feb 16 17:32:01 1997 | 1997 | 7 | 0 + | Sun Feb 16 17:32:01 1997 | 1997 | 7 | 0 + | Wed Feb 28 17:32:01 1996 | 1996 | 9 | 3 + | Thu Feb 29 17:32:01 1996 | 1996 | 9 | 4 + | Fri Mar 01 17:32:01 1996 | 1996 | 9 | 5 + | Mon Dec 30 17:32:01 1996 | 1997 | 1 | 1 + | Tue Dec 31 17:32:01 1996 | 1997 | 1 | 2 + | Wed Jan 01 17:32:01 1997 | 1997 | 1 | 3 + | Fri Feb 28 17:32:01 1997 | 1997 | 9 | 5 + | Sat Mar 01 17:32:01 1997 | 1997 | 9 | 6 + | Tue Dec 30 17:32:01 1997 | 1998 | 1 | 2 + | Wed Dec 31 17:32:01 1997 | 1998 | 1 | 3 + | Fri Dec 31 17:32:01 1999 | 1999 | 52 | 5 + | Sat Jan 01 17:32:01 2000 | 1999 | 52 | 6 + | Sun Dec 31 17:32:01 2000 | 2000 | 52 | 0 + | Mon Jan 01 17:32:01 2001 | 2001 | 1 | 1 +(55 rows) + -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMP_TBL; @@ -1344,6 +1407,148 @@ SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. H | 2001 A.D. 2001 a.d. 2001 ad 05:32:01 P.M. 05:32:01 p.m. 05:32:01 pm (65 rows) +SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') + FROM TIMESTAMP_TBL; + to_char_10 | to_char +------------+------------------------ + | + | + | 1970 970 70 0 01 004 4 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 01 004 4 + | 1997 997 97 7 01 004 4 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 24 163 2 + | 2001 001 01 1 38 265 6 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 24 163 2 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 044 2 + | 1997 997 97 7 07 045 3 + | 1997 997 97 7 07 046 4 + | 1997 997 97 7 07 047 5 + | 1997 997 97 7 07 048 6 + | 1997 997 97 7 07 049 7 + | 0097 097 97 7 07 044 2 + | 0097 097 97 7 07 048 6 + | 0597 597 97 7 07 046 4 + | 1097 097 97 7 07 044 2 + | 1697 697 97 7 07 048 6 + | 1797 797 97 7 07 046 4 + | 1897 897 97 7 07 044 2 + | 1997 997 97 7 07 049 7 + | 2097 097 97 7 07 048 6 + | 1996 996 96 6 09 059 3 + | 1996 996 96 6 09 060 4 + | 1996 996 96 6 09 061 5 + | 1997 997 97 7 01 001 1 + | 1997 997 97 7 01 002 2 + | 1997 997 97 7 01 003 3 + | 1997 997 97 7 09 061 5 + | 1997 997 97 7 09 062 6 + | 1998 998 98 8 01 002 2 + | 1998 998 98 8 01 003 3 + | 1999 999 99 9 52 362 5 + | 1999 999 99 9 52 363 6 + | 2000 000 00 0 52 364 7 + | 2001 001 01 1 01 001 1 +(65 rows) + +SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') + FROM TIMESTAMP_TBL; + to_char_11 | to_char +------------+------------------------ + | + | + | 1970 970 70 0 1 4 4 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 1 4 4 + | 1997 997 97 7 1 4 4 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 24 163 2 + | 2001 001 01 1 38 265 6 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 24 163 2 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 44 2 + | 1997 997 97 7 7 45 3 + | 1997 997 97 7 7 46 4 + | 1997 997 97 7 7 47 5 + | 1997 997 97 7 7 48 6 + | 1997 997 97 7 7 49 7 + | 97 097 97 7 7 44 2 + | 97 097 97 7 7 48 6 + | 597 597 97 7 7 46 4 + | 1097 097 97 7 7 44 2 + | 1697 697 97 7 7 48 6 + | 1797 797 97 7 7 46 4 + | 1897 897 97 7 7 44 2 + | 1997 997 97 7 7 49 7 + | 2097 097 97 7 7 48 6 + | 1996 996 96 6 9 59 3 + | 1996 996 96 6 9 60 4 + | 1996 996 96 6 9 61 5 + | 1997 997 97 7 1 1 1 + | 1997 997 97 7 1 2 2 + | 1997 997 97 7 1 3 3 + | 1997 997 97 7 9 61 5 + | 1997 997 97 7 9 62 6 + | 1998 998 98 8 1 2 2 + | 1998 998 98 8 1 3 3 + | 1999 999 99 9 52 362 5 + | 1999 999 99 9 52 363 6 + | 2000 000 00 0 52 364 7 + | 2001 001 01 1 1 1 1 +(65 rows) + -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); to_timestamp_1 | to_timestamp @@ -1428,4 +1633,46 @@ SELECT '' AS to_timestamp_14, to_timestamp('995-1116', 'YYY-MMDD'); | Thu Nov 16 00:00:00 1995 PST (1 row) +SELECT '' AS to_timestamp_15, to_timestamp('2005426', 'YYYYWWD'); + to_timestamp_15 | to_timestamp +-----------------+------------------------------ + | Sat Oct 15 00:00:00 2005 PDT +(1 row) + +SELECT '' AS to_timestamp_16, to_timestamp('2005300', 'YYYYDDD'); + to_timestamp_16 | to_timestamp +-----------------+------------------------------ + | Thu Oct 27 00:00:00 2005 PDT +(1 row) + +SELECT '' AS to_timestamp_17, to_timestamp('2005527', 'IYYYIWID'); + to_timestamp_17 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_18, to_timestamp('005527', 'IYYIWID'); + to_timestamp_18 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_19, to_timestamp('05527', 'IYIWID'); + to_timestamp_19 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_20, to_timestamp('5527', 'IIWID'); + to_timestamp_20 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_21, to_timestamp('2005364', 'IYYYIDDD'); + to_timestamp_21 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + SET DateStyle TO DEFAULT; diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index be3d7bed5d1..e875c90d5d1 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -720,6 +720,69 @@ SELECT '' AS "54", d1 as timestamptz, | Mon Jan 01 17:32:01 2001 PST | 1 | 1000 | 1000000 (56 rows) +SELECT '' AS "54", d1 as "timestamp", + date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, + date_part( 'dow', d1) AS dow + FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; + 54 | timestamp | isoyear | week | dow +----+-----------------------------+---------+------+----- + | Thu Jan 01 00:00:00 1970 | 1970 | 1 | 4 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:02 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.40 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.50 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01.60 1997 | 1997 | 7 | 1 + | Thu Jan 02 00:00:00 1997 | 1997 | 1 | 4 + | Thu Jan 02 03:04:05 1997 | 1997 | 1 | 4 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Jun 10 17:32:01 1997 | 1997 | 24 | 2 + | Sat Sep 22 18:19:20 2001 | 2001 | 38 | 6 + | Wed Mar 15 08:14:01 2000 | 2000 | 11 | 3 + | Wed Mar 15 13:14:02 2000 | 2000 | 11 | 3 + | Wed Mar 15 12:14:03 2000 | 2000 | 11 | 3 + | Wed Mar 15 03:14:04 2000 | 2000 | 11 | 3 + | Wed Mar 15 02:14:05 2000 | 2000 | 11 | 3 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:00 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Jun 10 18:32:01 1997 | 1997 | 24 | 2 + | Mon Feb 10 17:32:01 1997 | 1997 | 7 | 1 + | Tue Feb 11 17:32:01 1997 | 1997 | 7 | 2 + | Wed Feb 12 17:32:01 1997 | 1997 | 7 | 3 + | Thu Feb 13 17:32:01 1997 | 1997 | 7 | 4 + | Fri Feb 14 17:32:01 1997 | 1997 | 7 | 5 + | Sat Feb 15 17:32:01 1997 | 1997 | 7 | 6 + | Sun Feb 16 17:32:01 1997 | 1997 | 7 | 0 + | Sun Feb 16 17:32:01 1997 | 1997 | 7 | 0 + | Wed Feb 28 17:32:01 1996 | 1996 | 9 | 3 + | Thu Feb 29 17:32:01 1996 | 1996 | 9 | 4 + | Fri Mar 01 17:32:01 1996 | 1996 | 9 | 5 + | Mon Dec 30 17:32:01 1996 | 1997 | 1 | 1 + | Tue Dec 31 17:32:01 1996 | 1997 | 1 | 2 + | Wed Jan 01 17:32:01 1997 | 1997 | 1 | 3 + | Fri Feb 28 17:32:01 1997 | 1997 | 9 | 5 + | Sat Mar 01 17:32:01 1997 | 1997 | 9 | 6 + | Tue Dec 30 17:32:01 1997 | 1998 | 1 | 2 + | Wed Dec 31 17:32:01 1997 | 1998 | 1 | 3 + | Fri Dec 31 17:32:01 1999 | 1999 | 52 | 5 + | Sat Jan 01 17:32:01 2000 | 1999 | 52 | 6 + | Sun Dec 31 17:32:01 2000 | 2000 | 52 | 0 + | Mon Jan 01 17:32:01 2001 | 2001 | 1 | 1 +(55 rows) + -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMPTZ_TBL; @@ -1374,77 +1437,147 @@ SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. H | 2001 A.D. 2001 a.d. 2001 ad 05:32:01 P.M. 05:32:01 p.m. 05:32:01 pm (66 rows) -SELECT '' AS to_char_10, to_char(d1, 'YYYY WW IYYY IYY IY I IW') - FROM TIMESTAMPTZ_TBL; - to_char_10 | to_char -------------+-------------------------- +SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') + FROM TIMESTAMP_TBL; + to_char_10 | to_char +------------+------------------------ | | - | 1969 53 1970 970 70 0 01 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 01 1997 997 97 7 01 - | 1997 01 1997 997 97 7 01 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 23 1997 997 97 7 24 - | 2001 38 2001 001 01 1 38 - | 2000 11 2000 000 00 0 11 - | 2000 11 2000 000 00 0 11 - | 2000 11 2000 000 00 0 11 - | 2000 11 2000 000 00 0 11 - | 2000 11 2000 000 00 0 11 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 28 1997 997 97 7 28 - | 1997 23 1997 997 97 7 24 - | 1997 06 1997 997 97 7 07 - | 1997 06 1997 997 97 7 07 - | 1997 07 1997 997 97 7 07 - | 1997 07 1997 997 97 7 07 - | 1997 07 1997 997 97 7 07 - | 1997 07 1997 997 97 7 07 - | 1997 07 1997 997 97 7 07 - | 0097 07 0097 097 97 7 07 - | 0097 07 0097 097 97 7 07 - | 0597 07 0597 597 97 7 07 - | 1097 07 1097 097 97 7 07 - | 1697 07 1697 697 97 7 07 - | 1797 07 1797 797 97 7 07 - | 1897 07 1897 897 97 7 07 - | 1997 07 1997 997 97 7 07 - | 2097 07 2097 097 97 7 07 - | 1996 09 1996 996 96 6 09 - | 1996 09 1996 996 96 6 09 - | 1996 09 1996 996 96 6 09 - | 1996 53 1997 997 97 7 01 - | 1996 53 1997 997 97 7 01 - | 1997 01 1997 997 97 7 01 - | 1997 09 1997 997 97 7 09 - | 1997 09 1997 997 97 7 09 - | 1997 52 1998 998 98 8 01 - | 1997 53 1998 998 98 8 01 - | 1999 53 1999 999 99 9 52 - | 2000 01 1999 999 99 9 52 - | 2000 53 2000 000 00 0 52 - | 2001 01 2001 001 01 1 01 -(66 rows) + | 1970 970 70 0 01 004 4 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 01 004 4 + | 1997 997 97 7 01 004 4 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 24 163 2 + | 2001 001 01 1 38 265 6 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 2000 000 00 0 11 073 3 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 24 163 2 + | 1997 997 97 7 07 043 1 + | 1997 997 97 7 07 044 2 + | 1997 997 97 7 07 045 3 + | 1997 997 97 7 07 046 4 + | 1997 997 97 7 07 047 5 + | 1997 997 97 7 07 048 6 + | 1997 997 97 7 07 049 7 + | 0097 097 97 7 07 044 2 + | 0097 097 97 7 07 048 6 + | 0597 597 97 7 07 046 4 + | 1097 097 97 7 07 044 2 + | 1697 697 97 7 07 048 6 + | 1797 797 97 7 07 046 4 + | 1897 897 97 7 07 044 2 + | 1997 997 97 7 07 049 7 + | 2097 097 97 7 07 048 6 + | 1996 996 96 6 09 059 3 + | 1996 996 96 6 09 060 4 + | 1996 996 96 6 09 061 5 + | 1997 997 97 7 01 001 1 + | 1997 997 97 7 01 002 2 + | 1997 997 97 7 01 003 3 + | 1997 997 97 7 09 061 5 + | 1997 997 97 7 09 062 6 + | 1998 998 98 8 01 002 2 + | 1998 998 98 8 01 003 3 + | 1999 999 99 9 52 362 5 + | 1999 999 99 9 52 363 6 + | 2000 000 00 0 52 364 7 + | 2001 001 01 1 01 001 1 +(65 rows) + +SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') + FROM TIMESTAMP_TBL; + to_char_11 | to_char +------------+------------------------ + | + | + | 1970 970 70 0 1 4 4 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 1 4 4 + | 1997 997 97 7 1 4 4 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 24 163 2 + | 2001 001 01 1 38 265 6 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 2000 000 00 0 11 73 3 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 24 163 2 + | 1997 997 97 7 7 43 1 + | 1997 997 97 7 7 44 2 + | 1997 997 97 7 7 45 3 + | 1997 997 97 7 7 46 4 + | 1997 997 97 7 7 47 5 + | 1997 997 97 7 7 48 6 + | 1997 997 97 7 7 49 7 + | 97 097 97 7 7 44 2 + | 97 097 97 7 7 48 6 + | 597 597 97 7 7 46 4 + | 1097 097 97 7 7 44 2 + | 1697 697 97 7 7 48 6 + | 1797 797 97 7 7 46 4 + | 1897 897 97 7 7 44 2 + | 1997 997 97 7 7 49 7 + | 2097 097 97 7 7 48 6 + | 1996 996 96 6 9 59 3 + | 1996 996 96 6 9 60 4 + | 1996 996 96 6 9 61 5 + | 1997 997 97 7 1 1 1 + | 1997 997 97 7 1 2 2 + | 1997 997 97 7 1 3 3 + | 1997 997 97 7 9 61 5 + | 1997 997 97 7 9 62 6 + | 1998 998 98 8 1 2 2 + | 1998 998 98 8 1 3 3 + | 1999 999 99 9 52 362 5 + | 1999 999 99 9 52 363 6 + | 2000 000 00 0 52 364 7 + | 2001 001 01 1 1 1 1 +(65 rows) -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); @@ -1531,16 +1664,46 @@ SELECT '' AS to_timestamp_14, to_timestamp('995-1116', 'YYY-MMDD'); | Thu Nov 16 00:00:00 1995 PST (1 row) -SELECT '' AS to_timestamp_15, to_timestamp('200401', 'IYYYIW'); +SELECT '' AS to_timestamp_15, to_timestamp('2005426', 'YYYYWWD'); to_timestamp_15 | to_timestamp -----------------+------------------------------ - | Mon Dec 29 00:00:00 2003 PST + | Sat Oct 15 00:00:00 2005 PDT (1 row) -SELECT '' AS to_timestamp_16, to_timestamp('200401', 'YYYYWW'); +SELECT '' AS to_timestamp_16, to_timestamp('2005300', 'YYYYDDD'); to_timestamp_16 | to_timestamp -----------------+------------------------------ - | Thu Jan 01 00:00:00 2004 PST + | Thu Oct 27 00:00:00 2005 PDT +(1 row) + +SELECT '' AS to_timestamp_17, to_timestamp('2005527', 'IYYYIWID'); + to_timestamp_17 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_18, to_timestamp('005527', 'IYYIWID'); + to_timestamp_18 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_19, to_timestamp('05527', 'IYIWID'); + to_timestamp_19 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_20, to_timestamp('5527', 'IIWID'); + to_timestamp_20 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST +(1 row) + +SELECT '' AS to_timestamp_21, to_timestamp('2005364', 'IYYYIDDD'); + to_timestamp_21 | to_timestamp +-----------------+------------------------------ + | Sun Jan 01 00:00:00 2006 PST (1 row) SET DateStyle TO DEFAULT; diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql index 34689564dae..81be5b8f33f 100644 --- a/src/test/regress/sql/timestamp.sql +++ b/src/test/regress/sql/timestamp.sql @@ -171,6 +171,11 @@ SELECT '' AS "54", d1 as "timestamp", date_part( 'usec', d1) AS usec FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; +SELECT '' AS "54", d1 as "timestamp", + date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, + date_part( 'dow', d1) AS dow + FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; + -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMP_TBL; @@ -199,6 +204,12 @@ SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMP_TBL; +SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') + FROM TIMESTAMP_TBL; + +SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') + FROM TIMESTAMP_TBL; + -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); @@ -230,4 +241,18 @@ SELECT '' AS to_timestamp_13, to_timestamp('95-1116', 'YY-MMDD'); SELECT '' AS to_timestamp_14, to_timestamp('995-1116', 'YYY-MMDD'); +SELECT '' AS to_timestamp_15, to_timestamp('2005426', 'YYYYWWD'); + +SELECT '' AS to_timestamp_16, to_timestamp('2005300', 'YYYYDDD'); + +SELECT '' AS to_timestamp_17, to_timestamp('2005527', 'IYYYIWID'); + +SELECT '' AS to_timestamp_18, to_timestamp('005527', 'IYYIWID'); + +SELECT '' AS to_timestamp_19, to_timestamp('05527', 'IYIWID'); + +SELECT '' AS to_timestamp_20, to_timestamp('5527', 'IIWID'); + +SELECT '' AS to_timestamp_21, to_timestamp('2005364', 'IYYYIDDD'); + SET DateStyle TO DEFAULT; diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index fc597a6b2cf..6ef216a53a2 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -169,6 +169,11 @@ SELECT '' AS "54", d1 as timestamptz, date_part( 'usec', d1) AS usec FROM TIMESTAMPTZ_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; +SELECT '' AS "54", d1 as "timestamp", + date_part( 'isoyear', d1) AS isoyear, date_part( 'week', d1) AS week, + date_part( 'dow', d1) AS dow + FROM TIMESTAMP_TBL WHERE d1 BETWEEN '1902-01-01' AND '2038-01-01'; + -- TO_CHAR() SELECT '' AS to_char_1, to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMPTZ_TBL; @@ -197,8 +202,11 @@ SELECT '' AS to_char_8, to_char(d1, 'YYYYTH YYYYth Jth') SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. HH:MI:SS p.m. HH:MI:SS pm') FROM TIMESTAMPTZ_TBL; -SELECT '' AS to_char_10, to_char(d1, 'YYYY WW IYYY IYY IY I IW') - FROM TIMESTAMPTZ_TBL; +SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID') + FROM TIMESTAMP_TBL; + +SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID') + FROM TIMESTAMP_TBL; -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); @@ -231,9 +239,18 @@ SELECT '' AS to_timestamp_13, to_timestamp('95-1116', 'YY-MMDD'); SELECT '' AS to_timestamp_14, to_timestamp('995-1116', 'YYY-MMDD'); -SELECT '' AS to_timestamp_15, to_timestamp('200401', 'IYYYIW'); +SELECT '' AS to_timestamp_15, to_timestamp('2005426', 'YYYYWWD'); + +SELECT '' AS to_timestamp_16, to_timestamp('2005300', 'YYYYDDD'); + +SELECT '' AS to_timestamp_17, to_timestamp('2005527', 'IYYYIWID'); + +SELECT '' AS to_timestamp_18, to_timestamp('005527', 'IYYIWID'); + +SELECT '' AS to_timestamp_19, to_timestamp('05527', 'IYIWID'); -SELECT '' AS to_timestamp_16, to_timestamp('200401', 'YYYYWW'); +SELECT '' AS to_timestamp_20, to_timestamp('5527', 'IIWID'); +SELECT '' AS to_timestamp_21, to_timestamp('2005364', 'IYYYIDDD'); SET DateStyle TO DEFAULT; -- GitLab