88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.185 2008/02/17 02:09:28 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.186 2008/02/25 23:21:01 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -40,7 +40,10 @@ static int DecodeTime(char *str, int fmask, int *tmask,
4040struct pg_tm * tm ,fsec_t * fsec );
4141static int DecodeTimezone (char * str ,int * tzp );
4242static const datetkn * datebsearch (const char * key ,const datetkn * base ,int nel );
43- static int DecodeDate (char * str ,int fmask ,int * tmask ,struct pg_tm * tm );
43+ static int DecodeDate (char * str ,int fmask ,int * tmask ,bool * is2digits ,
44+ struct pg_tm * tm );
45+ static int ValidateDate (int fmask ,bool is2digits ,bool bc ,
46+ struct pg_tm * tm );
4447static void TrimTrailingZeros (char * str );
4548
4649
@@ -805,7 +808,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
805808}
806809else
807810{
808- dterr = DecodeDate (field [i ],fmask ,& tmask ,tm );
811+ dterr = DecodeDate (field [i ],fmask ,
812+ & tmask ,& is2digits ,tm );
809813if (dterr )
810814return dterr ;
811815}
@@ -1000,7 +1004,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
10001004/* Embedded decimal and no date yet? */
10011005if (cp != NULL && !(fmask & DTK_DATE_M ))
10021006{
1003- dterr = DecodeDate (field [i ],fmask ,& tmask ,tm );
1007+ dterr = DecodeDate (field [i ],fmask ,
1008+ & tmask ,& is2digits ,tm );
10041009if (dterr )
10051010return dterr ;
10061011}
@@ -1234,51 +1239,14 @@ DecodeDateTime(char **field, int *ftype, int nf,
12341239if (tmask & fmask )
12351240return DTERR_BAD_FORMAT ;
12361241fmask |=tmask ;
1237- }
1238-
1239- if (fmask & DTK_M (YEAR ))
1240- {
1241- /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1242- if (bc )
1243- {
1244- if (tm -> tm_year > 0 )
1245- tm -> tm_year = - (tm -> tm_year - 1 );
1246- else
1247- ereport (ERROR ,
1248- (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
1249- errmsg ("inconsistent use of year %04d and \"BC\"" ,
1250- tm -> tm_year )));
1251- }
1252- else if (is2digits )
1253- {
1254- if (tm -> tm_year < 70 )
1255- tm -> tm_year += 2000 ;
1256- else if (tm -> tm_year < 100 )
1257- tm -> tm_year += 1900 ;
1258- }
1259- }
1260-
1261- /* now that we have correct year, decode DOY */
1262- if (fmask & DTK_M (DOY ))
1263- {
1264- j2date (date2j (tm -> tm_year ,1 ,1 )+ tm -> tm_yday - 1 ,
1265- & tm -> tm_year ,& tm -> tm_mon ,& tm -> tm_mday );
1266- }
1267-
1268- /* check for valid month */
1269- if (fmask & DTK_M (MONTH ))
1270- {
1271- if (tm -> tm_mon < 1 || tm -> tm_mon > MONTHS_PER_YEAR )
1272- return DTERR_MD_FIELD_OVERFLOW ;
1273- }
1242+ }/* end loop over fields */
12741243
1275- /* minimal check for valid day */
1276- if (fmask & DTK_M (DAY ))
1277- {
1278- if (tm -> tm_mday < 1 || tm -> tm_mday > 31 )
1279- return DTERR_MD_FIELD_OVERFLOW ;
1280- }
1244+ /* do final checking/adjustment of Y/M/D fields */
1245+ dterr = ValidateDate (fmask ,is2digits ,bc ,tm );
1246+ if (dterr )
1247+ return dterr ;
12811248
1249+ /* handle AM/PM */
12821250if (mer != HR24 && tm -> tm_hour > 12 )
12831251return DTERR_FIELD_OVERFLOW ;
12841252if (mer == AM && tm -> tm_hour == 12 )
@@ -1296,14 +1264,6 @@ DecodeDateTime(char **field, int *ftype, int nf,
12961264return DTERR_BAD_FORMAT ;
12971265}
12981266
1299- /*
1300- * Check for valid day of month, now that we know for sure the month
1301- * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
1302- * unlikely that "Feb 29" is a YMD-order error.
1303- */
1304- if (tm -> tm_mday > day_tab [isleap (tm -> tm_year )][tm -> tm_mon - 1 ])
1305- return DTERR_FIELD_OVERFLOW ;
1306-
13071267/*
13081268 * If we had a full timezone spec, compute the offset (we could not do
13091269 * it before, because we need the date to resolve DST status).
@@ -1486,6 +1446,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
14861446int val ;
14871447int dterr ;
14881448bool is2digits = FALSE;
1449+ bool bc = FALSE;
14891450int mer = HR24 ;
14901451pg_tz * namedTz = NULL ;
14911452
@@ -1517,7 +1478,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
15171478if (i == 0 && nf >=2 &&
15181479(ftype [nf - 1 ]== DTK_DATE || ftype [1 ]== DTK_TIME ))
15191480{
1520- dterr = DecodeDate (field [i ],fmask ,& tmask ,tm );
1481+ dterr = DecodeDate (field [i ],fmask ,
1482+ & tmask ,& is2digits ,tm );
15211483if (dterr )
15221484return dterr ;
15231485}
@@ -1783,7 +1745,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
17831745 */
17841746if (i == 0 && nf >=2 && ftype [nf - 1 ]== DTK_DATE )
17851747{
1786- dterr = DecodeDate (field [i ],fmask ,& tmask ,tm );
1748+ dterr = DecodeDate (field [i ],fmask ,
1749+ & tmask ,& is2digits ,tm );
17871750if (dterr )
17881751return dterr ;
17891752}
@@ -1912,6 +1875,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
19121875mer = val ;
19131876break ;
19141877
1878+ case ADBC :
1879+ bc = (val == BC );
1880+ break ;
1881+
19151882case UNITS :
19161883tmask = 0 ;
19171884ptype = val ;
@@ -1960,8 +1927,14 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
19601927if (tmask & fmask )
19611928return DTERR_BAD_FORMAT ;
19621929fmask |=tmask ;
1963- }
1930+ }/* end loop over fields */
19641931
1932+ /* do final checking/adjustment of Y/M/D fields */
1933+ dterr = ValidateDate (fmask ,is2digits ,bc ,tm );
1934+ if (dterr )
1935+ return dterr ;
1936+
1937+ /* handle AM/PM */
19651938if (mer != HR24 && tm -> tm_hour > 12 )
19661939return DTERR_FIELD_OVERFLOW ;
19671940if (mer == AM && tm -> tm_hour == 12 )
@@ -2047,24 +2020,29 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
20472020 * Decode date string which includes delimiters.
20482021 * Return 0 if okay, a DTERR code if not.
20492022 *
2050- * Insist on a complete set of fields.
2023+ *str: field to be parsed
2024+ *fmask: bitmask for field types already seen
2025+ **tmask: receives bitmask for fields found here
2026+ **is2digits: set to TRUE if we find 2-digit year
2027+ **tm: field values are stored into appropriate members of this struct
20512028 */
20522029static int
2053- DecodeDate (char * str ,int fmask ,int * tmask ,struct pg_tm * tm )
2030+ DecodeDate (char * str ,int fmask ,int * tmask ,bool * is2digits ,
2031+ struct pg_tm * tm )
20542032{
20552033fsec_t fsec ;
20562034int nf = 0 ;
20572035int i ,
20582036len ;
20592037int dterr ;
20602038bool haveTextMonth = FALSE;
2061- bool bc = FALSE;
2062- bool is2digits = FALSE;
20632039int type ,
20642040val ,
20652041dmask = 0 ;
20662042char * field [MAXDATEFIELDS ];
20672043
2044+ * tmask = 0 ;
2045+
20682046/* parse this string... */
20692047while (* str != '\0' && nf < MAXDATEFIELDS )
20702048{
@@ -2090,14 +2068,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
20902068nf ++ ;
20912069}
20922070
2093- #if 0
2094- /* don't allow too many fields */
2095- if (nf > 3 )
2096- return DTERR_BAD_FORMAT ;
2097- #endif
2098-
2099- * tmask = 0 ;
2100-
21012071/* look first for text fields, since that will be unambiguous month */
21022072for (i = 0 ;i < nf ;i ++ )
21032073{
@@ -2115,10 +2085,6 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
21152085haveTextMonth = TRUE;
21162086break ;
21172087
2118- case ADBC :
2119- bc = (val == BC );
2120- break ;
2121-
21222088default :
21232089return DTERR_BAD_FORMAT ;
21242090}
@@ -2144,7 +2110,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
21442110
21452111dterr = DecodeNumber (len ,field [i ],haveTextMonth ,fmask ,
21462112& dmask ,tm ,
2147- & fsec ,& is2digits );
2113+ & fsec ,is2digits );
21482114if (dterr )
21492115return dterr ;
21502116
@@ -2158,23 +2124,38 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
21582124if ((fmask & ~(DTK_M (DOY ) |DTK_M (TZ )))!= DTK_DATE_M )
21592125return DTERR_BAD_FORMAT ;
21602126
2161- /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2162- if (bc )
2163- {
2164- if (tm -> tm_year > 0 )
2165- tm -> tm_year = - (tm -> tm_year - 1 );
2166- else
2167- ereport (ERROR ,
2168- (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
2169- errmsg ("inconsistent use of year %04d and \"BC\"" ,
2170- tm -> tm_year )));
2171- }
2172- else if (is2digits )
2127+ /* validation of the field values must wait until ValidateDate() */
2128+
2129+ return 0 ;
2130+ }
2131+
2132+ /* ValidateDate()
2133+ * Check valid year/month/day values, handle BC and DOY cases
2134+ * Return 0 if okay, a DTERR code if not.
2135+ */
2136+ static int
2137+ ValidateDate (int fmask ,bool is2digits ,bool bc ,struct pg_tm * tm )
2138+ {
2139+ if (fmask & DTK_M (YEAR ))
21732140{
2174- if (tm -> tm_year < 70 )
2175- tm -> tm_year += 2000 ;
2176- else if (tm -> tm_year < 100 )
2177- tm -> tm_year += 1900 ;
2141+ /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
2142+ if (bc )
2143+ {
2144+ if (tm -> tm_year > 0 )
2145+ tm -> tm_year = - (tm -> tm_year - 1 );
2146+ else
2147+ ereport (ERROR ,
2148+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
2149+ errmsg ("inconsistent use of year %04d and \"BC\"" ,
2150+ tm -> tm_year )));
2151+ }
2152+ else if (is2digits )
2153+ {
2154+ if (tm -> tm_year < 70 )
2155+ tm -> tm_year += 2000 ;
2156+ else if (tm -> tm_year < 100 )
2157+ tm -> tm_year += 1900 ;
2158+ }
21782159}
21792160
21802161/* now that we have correct year, decode DOY */
@@ -2185,16 +2166,29 @@ DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm)
21852166}
21862167
21872168/* check for valid month */
2188- if (tm -> tm_mon < 1 || tm -> tm_mon > MONTHS_PER_YEAR )
2189- return DTERR_MD_FIELD_OVERFLOW ;
2169+ if (fmask & DTK_M (MONTH ))
2170+ {
2171+ if (tm -> tm_mon < 1 || tm -> tm_mon > MONTHS_PER_YEAR )
2172+ return DTERR_MD_FIELD_OVERFLOW ;
2173+ }
21902174
2191- /* check for valid day */
2192- if (tm -> tm_mday < 1 || tm -> tm_mday > 31 )
2193- return DTERR_MD_FIELD_OVERFLOW ;
2175+ /* minimal check for valid day */
2176+ if (fmask & DTK_M (DAY ))
2177+ {
2178+ if (tm -> tm_mday < 1 || tm -> tm_mday > 31 )
2179+ return DTERR_MD_FIELD_OVERFLOW ;
2180+ }
21942181
2195- /* We don't want to hint about DateStyle for Feb 29 */
2196- if (tm -> tm_mday > day_tab [isleap (tm -> tm_year )][tm -> tm_mon - 1 ])
2197- return DTERR_FIELD_OVERFLOW ;
2182+ if ((fmask & DTK_DATE_M )== DTK_DATE_M )
2183+ {
2184+ /*
2185+ * Check for valid day of month, now that we know for sure the month
2186+ * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
2187+ * unlikely that "Feb 29" is a YMD-order error.
2188+ */
2189+ if (tm -> tm_mday > day_tab [isleap (tm -> tm_year )][tm -> tm_mon - 1 ])
2190+ return DTERR_FIELD_OVERFLOW ;
2191+ }
21982192
21992193return 0 ;
22002194}