Patch applied.

---------------------------------------------------------------------------

On Sat, Sep  1, 2012 at 05:14:39PM -0400, Bruce Momjian wrote:
> [Properly posted to hackers list]
> 
> On Fri, Apr  1, 2011 at 02:27:02AM +1100, Brendan Jurd wrote:
> > On 1 April 2011 02:00, Adrian Klaver <adrian.kla...@gmail.com> wrote:
> > > On Wednesday, March 30, 2011 8:39:25 pm Brendan Jurd wrote:
> > >> If we wanted to make it "work", then I think the thing to do would be
> > >> to add a new set of formatting tokens IDY, IDAY etc.  I don't like the
> > >> idea of interpreting DY and co. differently depending on whether the
> > >> other tokens happen to be ISO week or Gregorian.
> > >
> > > Just to play Devils advocate here, but why not? The day name is the same 
> > > either
> > > way, it is the index that changes. I am not sure why that could not be 
> > > context
> > > specific?
> > >
> > 
> > To be perfectly honest, it's mostly because I was hoping not to spend
> > very much more of my time in formatting.c.  Every time I go in there I
> > come out a little bit less sane.  I'm concerned that if I do anything
>   -------------------------------
> 
> Agreed!
> 
> > further to it, I might inadvertently summon Chattur'gha or something.
> > But since you went to the trouble of calling me on my laziness, let's
> > take a look at the problem.
> > 
> > At the time when the day-of-week token gets converted into a numeric
> > value and put into the TmFromChar.d field, the code has no knowledge
> > of whether the overall pattern is Gregorian or ISO (the DY field could
> > well be at the front of the pattern, for example).
> > 
> > Later on, in do_to_timestamp, the code expects the 'd' value to make
> > sense given the mode (it should be zero-based on Sunday for Gregorian,
> > or one-based on Monday for ISO).  That's all well and good *except* in
> > the totally bizarre case raised by the OP.
> > 
> > To resolve it, we could make TmFromChar.d always stored using the ISO
> > convention (because zero then has the useful property of meaning "not
> > set") and converted to the Gregorian convention as necessary in
> > do_to_timestamp.
> 
> I did quite a bit if study on this and have a fix in the attached patch.
> Brendan above is correct about the cause of the problems.  Basically,
> 'd' was sometimes numbered 1-7 with Monday as week start, and 'd' was at
> other times 0-6 with Sunday as start.  Plus, zero was used to designate
> "not supplied" in ISO tests.  Obviously the number and the start value
> both caused problems.
> 
> The attached patch fixes this by using Gregorian 1-7 (Sunday=7) format
> throughout, allowing any mix of Gregorian and ISO week designations.  It
> is converted to ISO (or Unix format 0-6, Sunday=0) as needed.
> 
> Sample output:
> 
>         test=> select to_date('2011-13-MON', 'IYYY-IW-DY');
>           to_date
>         ------------
>          2011-03-28
>         (1 row)
> 
>         test=> select to_date('2011-13-SUN', 'IYYY-IW-DY');
>           to_date
>         ------------
>          2011-04-03
>         (1 row)
> 
>         test=> select to_date('2011-13-SAT', 'IYYY-IW-DY');
>           to_date
>         ------------
>          2011-04-02
>         (1 row)
> 
>         test=> select to_date('2011-13-1', 'IYYY-IW-ID');
>           to_date
>         ------------
>          2011-03-28
>         (1 row)
> 
>         test=> select to_date('2011-13-7', 'IYYY-IW-ID');
>           to_date
>         ------------
>          2011-04-03
>         (1 row)
> 
>         test=> select to_date('2011-13-0', 'IYYY-IW-ID');
>           to_date
>         ------------
>          2011-04-03
>         (1 row)
> 
> -- 
>   Bruce Momjian  <br...@momjian.us>        http://momjian.us
>   EnterpriseDB                             http://enterprisedb.com
> 
>   + It's impossible for everything to be true. +

> diff --git a/src/backend/utils/adt/formatting.c 
> b/src/backend/utils/adt/formatting.c
> new file mode 100644
> index 25af8a2..2aa6df1
> *** a/src/backend/utils/adt/formatting.c
> --- b/src/backend/utils/adt/formatting.c
> *************** typedef struct
> *** 412,418 ****
>                               mi,
>                               ss,
>                               ssss,
> !                             d,
>                               dd,
>                               ddd,
>                               mm,
> --- 412,418 ----
>                               mi,
>                               ss,
>                               ssss,
> !                             d,                              /* stored as 
> 1-7, Sunday = 1, 0 means missing */
>                               dd,
>                               ddd,
>                               mm,
> *************** DCH_from_char(FormatNode *node, char *in
> *** 2897,2902 ****
> --- 2897,2903 ----
>                               from_char_seq_search(&value, &s, days, 
> ONE_UPPER,
>                                                                        
> MAX_DAY_LEN, n);
>                               from_char_set_int(&out->d, value, n);
> +                             out->d++;
>                               break;
>                       case DCH_DY:
>                       case DCH_Dy:
> *************** DCH_from_char(FormatNode *node, char *in
> *** 2904,2909 ****
> --- 2905,2911 ----
>                               from_char_seq_search(&value, &s, days, 
> ONE_UPPER,
>                                                                        
> MAX_DY_LEN, n);
>                               from_char_set_int(&out->d, value, n);
> +                             out->d++;
>                               break;
>                       case DCH_DDD:
>                               from_char_parse_int(&out->ddd, &s, n);
> *************** DCH_from_char(FormatNode *node, char *in
> *** 2919,2929 ****
>                               break;
>                       case DCH_D:
>                               from_char_parse_int(&out->d, &s, n);
> -                             out->d--;
>                               s += SKIP_THth(n->suffix);
>                               break;
>                       case DCH_ID:
>                               from_char_parse_int_len(&out->d, &s, 1, n);
>                               s += SKIP_THth(n->suffix);
>                               break;
>                       case DCH_WW:
> --- 2921,2933 ----
>                               break;
>                       case DCH_D:
>                               from_char_parse_int(&out->d, &s, n);
>                               s += SKIP_THth(n->suffix);
>                               break;
>                       case DCH_ID:
>                               from_char_parse_int_len(&out->d, &s, 1, n);
> +                             /* Shift numbering to match Gregorian where 
> Sunday = 1 */
> +                             if (++out->d > 7)
> +                                     out->d = 1;
>                               s += SKIP_THth(n->suffix);
>                               break;
>                       case DCH_WW:
> *************** do_to_timestamp(text *date_txt, text *fm
> *** 3534,3540 ****
>       if (tmfc.w)
>               tmfc.dd = (tmfc.w - 1) * 7 + 1;
>       if (tmfc.d)
> !             tm->tm_wday = tmfc.d;
>       if (tmfc.dd)
>               tm->tm_mday = tmfc.dd;
>       if (tmfc.ddd)
> --- 3538,3544 ----
>       if (tmfc.w)
>               tmfc.dd = (tmfc.w - 1) * 7 + 1;
>       if (tmfc.d)
> !             tm->tm_wday = tmfc.d - 1;       /* convert to native numbering 
> */
>       if (tmfc.dd)
>               tm->tm_mday = tmfc.dd;
>       if (tmfc.ddd)
> diff --git a/src/backend/utils/adt/timestamp.c 
> b/src/backend/utils/adt/timestamp.c
> new file mode 100644
> index 2adc178..50ef897
> *** a/src/backend/utils/adt/timestamp.c
> --- b/src/backend/utils/adt/timestamp.c
> *************** isoweek2date(int woy, int *year, int *mo
> *** 3775,3792 ****
>   
>   /* 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(jday, year, mon, mday);
>   }
>   
> --- 3775,3796 ----
>   
>   /* isoweekdate2date()
>    *
> !  *  Convert an ISO 8601 week date (ISO year, ISO week) into a Gregorian 
> date.
> !  *  Gregorian day of week sent so weekday strings can be supplied.
>    *  Populates year, mon, and mday with the correct Gregorian values.
>    *  year must be passed in as the ISO year.
>    */
>   void
> ! isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday)
>   {
>       int                     jday;
>   
>       jday = isoweek2j(*year, isoweek);
> !     /* convert Gregorian week start (Sunday=1) to ISO week start (Monday=1) 
> */
> !     if (wday > 1)
> !             jday += wday - 2;
> !     else
> !             jday += 6;
>       j2date(jday, year, mon, mday);
>   }
>   
> diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
> new file mode 100644
> index 665e969..e7cdb41
> *** a/src/include/utils/timestamp.h
> --- b/src/include/utils/timestamp.h
> *************** extern int    timestamp_cmp_internal(Timest
> *** 236,242 ****
>   
>   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);
> --- 236,242 ----
>   
>   extern int  isoweek2j(int year, int week);
>   extern void isoweek2date(int woy, int *year, int *mon, int *mday);
> ! extern void isoweekdate2date(int isoweek, int wday, 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);

> 
> -- 
> Sent via pgsql-general mailing list (pgsql-gene...@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-general


-- 
  Bruce Momjian  <br...@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + It's impossible for everything to be true. +


-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to