@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / utils / adt / datetime . c , v 1.184 2008 / 01 / 01 19 : 45 : 52 momjian Exp $
* $ PostgreSQL : pgsql / src / backend / utils / adt / datetime . c , v 1.184 .2 .1 2008 / 02 / 25 23 : 21 : 08 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -40,7 +40,10 @@ static int DecodeTime(char *str, int fmask, int *tmask,
struct pg_tm * tm , fsec_t * fsec ) ;
static int DecodeTimezone ( char * str , int * tzp ) ;
static const datetkn * datebsearch ( const char * key , const datetkn * base , int nel ) ;
static int DecodeDate ( char * str , int fmask , int * tmask , struct pg_tm * tm ) ;
static int DecodeDate ( char * str , int fmask , int * tmask , bool * is2digits ,
struct pg_tm * tm ) ;
static int ValidateDate ( int fmask , bool is2digits , bool bc ,
struct pg_tm * tm ) ;
static void TrimTrailingZeros ( char * str ) ;
@ -803,7 +806,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
}
else
{
dterr = DecodeDate ( field [ i ] , fmask , & tmask , tm ) ;
dterr = DecodeDate ( field [ i ] , fmask ,
& tmask , & is2digits , tm ) ;
if ( dterr )
return dterr ;
}
@ -998,7 +1002,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
/* Embedded decimal and no date yet? */
if ( cp ! = NULL & & ! ( fmask & DTK_DATE_M ) )
{
dterr = DecodeDate ( field [ i ] , fmask , & tmask , tm ) ;
dterr = DecodeDate ( field [ i ] , fmask ,
& tmask , & is2digits , tm ) ;
if ( dterr )
return dterr ;
}
@ -1232,51 +1237,14 @@ DecodeDateTime(char **field, int *ftype, int nf,
if ( tmask & fmask )
return DTERR_BAD_FORMAT ;
fmask | = tmask ;
}
if ( fmask & DTK_M ( YEAR ) )
{
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if ( bc )
{
if ( tm - > tm_year > 0 )
tm - > tm_year = - ( tm - > tm_year - 1 ) ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " inconsistent use of year %04d and \" BC \" " ,
tm - > tm_year ) ) ) ;
}
else if ( is2digits )
{
if ( tm - > tm_year < 70 )
tm - > tm_year + = 2000 ;
else if ( tm - > tm_year < 100 )
tm - > tm_year + = 1900 ;
}
}
/* now that we have correct year, decode DOY */
if ( fmask & DTK_M ( DOY ) )
{
j2date ( date2j ( tm - > tm_year , 1 , 1 ) + tm - > tm_yday - 1 ,
& tm - > tm_year , & tm - > tm_mon , & tm - > tm_mday ) ;
}
/* check for valid month */
if ( fmask & DTK_M ( MONTH ) )
{
if ( tm - > tm_mon < 1 | | tm - > tm_mon > MONTHS_PER_YEAR )
return DTERR_MD_FIELD_OVERFLOW ;
}
} /* end loop over fields */
/* minimal check for valid day */
if ( fmask & DTK_M ( DAY ) )
{
if ( tm - > tm_mday < 1 | | tm - > tm_mday > 31 )
return DTERR_MD_FIELD_OVERFLOW ;
}
/* do final checking/adjustment of Y/M/D fields */
dterr = ValidateDate ( fmask , is2digits , bc , tm ) ;
if ( dterr )
return dterr ;
/* handle AM/PM */
if ( mer ! = HR24 & & tm - > tm_hour > 12 )
return DTERR_FIELD_OVERFLOW ;
if ( mer = = AM & & tm - > tm_hour = = 12 )
@ -1294,14 +1262,6 @@ DecodeDateTime(char **field, int *ftype, int nf,
return DTERR_BAD_FORMAT ;
}
/*
* Check for valid day of month , now that we know for sure the month
* and year . Note we don ' t use MD_FIELD_OVERFLOW here , since it seems
* unlikely that " Feb 29 " is a YMD - order error .
*/
if ( tm - > tm_mday > day_tab [ isleap ( tm - > tm_year ) ] [ tm - > tm_mon - 1 ] )
return DTERR_FIELD_OVERFLOW ;
/*
* If we had a full timezone spec , compute the offset ( we could not do
* it before , because we need the date to resolve DST status ) .
@ -1484,6 +1444,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
int val ;
int dterr ;
bool is2digits = FALSE ;
bool bc = FALSE ;
int mer = HR24 ;
pg_tz * namedTz = NULL ;
@ -1515,7 +1476,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
if ( i = = 0 & & nf > = 2 & &
( ftype [ nf - 1 ] = = DTK_DATE | | ftype [ 1 ] = = DTK_TIME ) )
{
dterr = DecodeDate ( field [ i ] , fmask , & tmask , tm ) ;
dterr = DecodeDate ( field [ i ] , fmask ,
& tmask , & is2digits , tm ) ;
if ( dterr )
return dterr ;
}
@ -1781,7 +1743,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
*/
if ( i = = 0 & & nf > = 2 & & ftype [ nf - 1 ] = = DTK_DATE )
{
dterr = DecodeDate ( field [ i ] , fmask , & tmask , tm ) ;
dterr = DecodeDate ( field [ i ] , fmask ,
& tmask , & is2digits , tm ) ;
if ( dterr )
return dterr ;
}
@ -1910,6 +1873,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
mer = val ;
break ;
case ADBC :
bc = ( val = = BC ) ;
break ;
case UNITS :
tmask = 0 ;
ptype = val ;
@ -1958,8 +1925,14 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
if ( tmask & fmask )
return DTERR_BAD_FORMAT ;
fmask | = tmask ;
}
} /* end loop over fields */
/* do final checking/adjustment of Y/M/D fields */
dterr = ValidateDate ( fmask , is2digits , bc , tm ) ;
if ( dterr )
return dterr ;
/* handle AM/PM */
if ( mer ! = HR24 & & tm - > tm_hour > 12 )
return DTERR_FIELD_OVERFLOW ;
if ( mer = = AM & & tm - > tm_hour = = 12 )
@ -2045,10 +2018,15 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* Decode date string which includes delimiters .
* Return 0 if okay , a DTERR code if not .
*
* Insist on a complete set of fields .
* str : field to be parsed
* fmask : bitmask for field types already seen
* * tmask : receives bitmask for fields found here
* * is2digits : set to TRUE if we find 2 - digit year
* * tm : field values are stored into appropriate members of this struct
*/
static int
DecodeDate ( char * str , int fmask , int * tmask , struct pg_tm * tm )
DecodeDate ( char * str , int fmask , int * tmask , bool * is2digits ,
struct pg_tm * tm )
{
fsec_t fsec ;
int nf = 0 ;
@ -2056,13 +2034,13 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
len ;
int dterr ;
bool haveTextMonth = FALSE ;
bool bc = FALSE ;
bool is2digits = FALSE ;
int type ,
val ,
dmask = 0 ;
char * field [ MAXDATEFIELDS ] ;
* tmask = 0 ;
/* parse this string... */
while ( * str ! = ' \0 ' & & nf < MAXDATEFIELDS )
{
@ -2088,14 +2066,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
nf + + ;
}
#if 0
/* don't allow too many fields */
if ( nf > 3 )
return DTERR_BAD_FORMAT ;
# endif
* tmask = 0 ;
/* look first for text fields, since that will be unambiguous month */
for ( i = 0 ; i < nf ; i + + )
{
@ -2113,10 +2083,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
haveTextMonth = TRUE ;
break ;
case ADBC :
bc = ( val = = BC ) ;
break ;
default :
return DTERR_BAD_FORMAT ;
}
@ -2142,7 +2108,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
dterr = DecodeNumber ( len , field [ i ] , haveTextMonth , fmask ,
& dmask , tm ,
& fsec , & is2digits ) ;
& fsec , is2digits ) ;
if ( dterr )
return dterr ;
@ -2156,23 +2122,38 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
if ( ( fmask & ~ ( DTK_M ( DOY ) | DTK_M ( TZ ) ) ) ! = DTK_DATE_M )
return DTERR_BAD_FORMAT ;
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if ( bc )
{
if ( tm - > tm_year > 0 )
tm - > tm_year = - ( tm - > tm_year - 1 ) ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " inconsistent use of year %04d and \" BC \" " ,
tm - > tm_year ) ) ) ;
}
else if ( is2digits )
/* validation of the field values must wait until ValidateDate() */
return 0 ;
}
/* ValidateDate()
* Check valid year / month / day values , handle BC and DOY cases
* Return 0 if okay , a DTERR code if not .
*/
static int
ValidateDate ( int fmask , bool is2digits , bool bc , struct pg_tm * tm )
{
if ( fmask & DTK_M ( YEAR ) )
{
if ( tm - > tm_year < 70 )
tm - > tm_year + = 2000 ;
else if ( tm - > tm_year < 100 )
tm - > tm_year + = 1900 ;
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if ( bc )
{
if ( tm - > tm_year > 0 )
tm - > tm_year = - ( tm - > tm_year - 1 ) ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " inconsistent use of year %04d and \" BC \" " ,
tm - > tm_year ) ) ) ;
}
else if ( is2digits )
{
if ( tm - > tm_year < 70 )
tm - > tm_year + = 2000 ;
else if ( tm - > tm_year < 100 )
tm - > tm_year + = 1900 ;
}
}
/* now that we have correct year, decode DOY */
@ -2183,16 +2164,29 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
}
/* check for valid month */
if ( tm - > tm_mon < 1 | | tm - > tm_mon > MONTHS_PER_YEAR )
return DTERR_MD_FIELD_OVERFLOW ;
if ( fmask & DTK_M ( MONTH ) )
{
if ( tm - > tm_mon < 1 | | tm - > tm_mon > MONTHS_PER_YEAR )
return DTERR_MD_FIELD_OVERFLOW ;
}
/* check for valid day */
if ( tm - > tm_mday < 1 | | tm - > tm_mday > 31 )
return DTERR_MD_FIELD_OVERFLOW ;
/* minimal check for valid day */
if ( fmask & DTK_M ( DAY ) )
{
if ( tm - > tm_mday < 1 | | tm - > tm_mday > 31 )
return DTERR_MD_FIELD_OVERFLOW ;
}
/* We don't want to hint about DateStyle for Feb 29 */
if ( tm - > tm_mday > day_tab [ isleap ( tm - > tm_year ) ] [ tm - > tm_mon - 1 ] )
return DTERR_FIELD_OVERFLOW ;
if ( ( fmask & DTK_DATE_M ) = = DTK_DATE_M )
{
/*
* Check for valid day of month , now that we know for sure the month
* and year . Note we don ' t use MD_FIELD_OVERFLOW here , since it seems
* unlikely that " Feb 29 " is a YMD - order error .
*/
if ( tm - > tm_mday > day_tab [ isleap ( tm - > tm_year ) ] [ tm - > tm_mon - 1 ] )
return DTERR_FIELD_OVERFLOW ;
}
return 0 ;
}