summaryrefslogtreecommitdiff
path: root/sql/item_timefunc.cc
diff options
context:
space:
mode:
authorunknown <dlenev@brandersnatch.localdomain>2004-08-06 10:01:29 +0400
committerunknown <dlenev@brandersnatch.localdomain>2004-08-06 10:01:29 +0400
commit1b0f0b0afc6cf33db2b39bf2653bf6f6ddef015b (patch)
treeb0d57ecd55aa0d92a2dc378c028c3acd6b14ab89 /sql/item_timefunc.cc
parentea8ac8ab9845e039e5770821c5dbfc4604390299 (diff)
downloadmariadb-git-1b0f0b0afc6cf33db2b39bf2653bf6f6ddef015b.tar.gz
Fix for bug #4756 "STR_TO_DATE() returning bad results with AM/PM".
Added support of converion specifiers mentioned in manual but missing in code. mysql-test/r/date_formats.result: Added tests of str_to_date() with new %T, %r and %V, %v conversion specifiers, and also some other specifiers for which tests were missing previously. mysql-test/t/date_formats.test: Added tests of str_to_date() and new %T, %r and %V, %v conversion specifiers, and also some other specifiers for which tests were missing previously. sql/item_timefunc.cc: Added support for %T, %r, %V, %v, %X, %x conversion specifiers to extract_date_time() function. Also simplified a bit calculation of dates from week number.
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r--sql/item_timefunc.cc140
1 files changed, 110 insertions, 30 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 786bcf434ed..cc320addd47 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -113,6 +113,12 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
}
+/* Date formats corresponding to compound %r and %T conversion specifiers */
+static DATE_TIME_FORMAT time_ampm_format= {{}, '\0', 0,
+ {(char *)"%I:%i:%S %p", 11}};
+static DATE_TIME_FORMAT time_24hrs_format= {{}, '\0', 0,
+ {(char *)"%H:%i:%S", 8}};
+
/*
Extract datetime value to TIME struct from string value
according to format string.
@@ -126,6 +132,17 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
cached_timestamp_type
It uses to get an appropriate warning
in the case when the value is truncated.
+ sub_pattern_end if non-zero then we are parsing string which
+ should correspond compound specifier (like %T or
+ %r) and this parameter is pointer to place where
+ pointer to end of string matching this specifier
+ should be stored.
+ NOTE
+ Possibility to parse strings matching to patterns equivalent to compound
+ specifiers is mainly intended for use from inside of this function in
+ order to understand %T and %r conversion specifiers, so number of
+ conversion specifiers that can be used in such sub-patterns is limited.
+ Also most of checks are skipped in this case.
RETURN
0 ok
@@ -134,14 +151,18 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
static bool extract_date_time(DATE_TIME_FORMAT *format,
const char *val, uint length, TIME *l_time,
- timestamp_type cached_timestamp_type)
+ timestamp_type cached_timestamp_type,
+ const char **sub_pattern_end)
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
CHARSET_INFO *cs= &my_charset_bin;
int error= 0;
bool usa_time= 0;
- bool sunday_first= 0;
+ bool sunday_first_n_first_week_non_iso;
+ bool strict_week_number;
+ int strict_week_number_year= -1;
+ bool strict_week_number_year_type;
int frac_part;
const char *val_begin= val;
const char *val_end= val + length;
@@ -149,7 +170,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
const char *end= ptr + format->format.length;
DBUG_ENTER("extract_date_time");
- bzero((char*) l_time, sizeof(*l_time));
+ LINT_INIT(sunday_first_n_first_week_non_iso);
+ LINT_INIT(strict_week_number);
+ LINT_INIT(strict_week_number_year_type);
+
+ if (!sub_pattern_end)
+ bzero((char*) l_time, sizeof(*l_time));
for (; ptr != end && val != val_end; ptr++)
{
@@ -160,7 +186,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
char *tmp;
/* Skip pre-space between each argument */
- while (my_isspace(cs, *val) && val != val_end)
+ while (val != val_end && my_isspace(cs, *val))
val++;
val_len= (uint) (val_end - val);
@@ -268,9 +294,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
break;
case 'w':
tmp= (char*) val + 1;
- if ((weekday= (int) my_strtoll10(val, &tmp, &error)) <= 0 ||
+ if ((weekday= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
weekday >= 7)
goto err;
+ /* We should use the same 1 - 7 scale for %w as for %W */
+ if (!weekday)
+ weekday= 7;
val= tmp;
break;
case 'j':
@@ -279,15 +308,45 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
val= tmp;
break;
+ /* Week numbers */
+ case 'V':
case 'U':
- sunday_first= 1;
- /* Fall through */
+ case 'v':
case 'u':
+ sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V');
+ strict_week_number= (*ptr=='V' || *ptr=='v');
tmp= (char*) val + min(val_len, 2);
- week_number= (int) my_strtoll10(val, &tmp, &error);
+ if ((week_number= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
+ strict_week_number && !week_number ||
+ week_number > 53)
+ goto err;
val= tmp;
break;
+ /* Year used with 'strict' %V and %v week numbers */
+ case 'X':
+ case 'x':
+ strict_week_number_year_type= (*ptr=='X');
+ tmp= (char*) val + min(4, val_len);
+ strict_week_number_year= (int) my_strtoll10(val, &tmp, &error);
+ val= tmp;
+ break;
+
+ /* Time in AM/PM notation */
+ case 'r':
+ error= extract_date_time(&time_ampm_format, val,
+ (uint)(val_end - val), l_time,
+ cached_timestamp_type, &val);
+ break;
+
+ /* Time in 24-hour notation */
+ case 'T':
+ error= extract_date_time(&time_24hrs_format, val,
+ (uint)(val_end - val), l_time,
+ cached_timestamp_type, &val);
+ break;
+
+ /* Conversion specifiers that match classes of characters */
case '.':
while (my_ispunct(cs, *val) && val != val_end)
val++;
@@ -320,6 +379,16 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
l_time->hour= l_time->hour%12+daypart;
}
+ /*
+ If we are recursively called for parsing string matching compound
+ specifiers we are already done.
+ */
+ if (sub_pattern_end)
+ {
+ *sub_pattern_end= val;
+ DBUG_RETURN(0);
+ }
+
if (yearday > 0)
{
uint days= calc_daynr(l_time->year,1,1) + yearday - 1;
@@ -330,34 +399,45 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
if (week_number >= 0 && weekday)
{
- int days= calc_daynr(l_time->year,1,1);
+ int days;
uint weekday_b;
-
- if (weekday > 7 || weekday < 0)
- goto err;
- if (sunday_first)
- weekday = weekday%7;
- if (week_number == 53)
- {
- days+= (week_number - 1)*7;
- weekday_b= calc_weekday(days, sunday_first);
- weekday = weekday - weekday_b - !sunday_first;
- days+= weekday;
- }
- else if (week_number == 0)
+ /*
+ %V,%v require %X,%x resprectively,
+ %U,%u should be used with %Y and not %X or %x
+ */
+ if (strict_week_number &&
+ (strict_week_number_year < 0 ||
+ strict_week_number_year_type != sunday_first_n_first_week_non_iso) ||
+ !strict_week_number && strict_week_number_year >= 0)
+ goto err;
+
+ /* Number of days since year 0 till 1st Jan of this year */
+ days= calc_daynr((strict_week_number ? strict_week_number_year :
+ l_time->year),
+ 1, 1);
+ /* Which day of week is 1st Jan of this year */
+ weekday_b= calc_weekday(days, sunday_first_n_first_week_non_iso);
+
+ /*
+ Below we are going to sum:
+ 1) number of days since year 0 till 1st day of 1st week of this year
+ 2) number of days between 1st week and our week
+ 3) and position of our day in the week
+ */
+ if (sunday_first_n_first_week_non_iso)
{
- weekday_b= calc_weekday(days, sunday_first);
- weekday = weekday - weekday_b - !sunday_first;
- days+= weekday;
+ days+= ((weekday_b == 0) ? 0 : 7) - weekday_b +
+ (week_number - 1) * 7 +
+ weekday % 7;
}
else
{
- days+= (week_number - !sunday_first)*7;
- weekday_b= calc_weekday(days, sunday_first);
- weekday =weekday - weekday_b - !sunday_first;
- days+= weekday;
+ days+= ((weekday_b <= 3) ? 0 : 7) - weekday_b +
+ (week_number - 1) * 7 +
+ (weekday - 1);
}
+
if (days <= 0 || days >= MAX_DAY_NUMBER)
goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
@@ -2599,7 +2679,7 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date)
date_time_format.format.str= (char*) format->ptr();
date_time_format.format.length= format->length();
if (extract_date_time(&date_time_format, val->ptr(), val->length(),
- ltime, cached_timestamp_type))
+ ltime, cached_timestamp_type, 0))
goto null_date;
if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day)
{