MySQL and datetime - mysql

If I have a table with a DATETIME column I can insert dates that have a format like:
2015-03-25 10:10:10
2015-03-25 10:10
2015-03-25 10
2015-03-25
It will fill in the remainder with zeros. I can't however use
2015-03
2015
As it will give an 'Incorrect datetime value' error. It is however possible to use these last two in a SELECT like [..] WHERE timestamp < '2015-03' ..
Is there a way that MySQL will fill in the remainder of datetimes with 01-01 for the month and day if omitted in datetimes or do I have to do that manually myself?
I.e. I would like to use '2015-03' in an INSERT statement, or do something like SELECT DATE_FORMAT('2015-03', '%Y%m%dT%H%i%S')

As stated in Date and Time Literals:
MySQL recognizes DATE values in these formats:
As a string in either 'YYYY-MM-DD' or 'YY-MM-DD' format. A “relaxed” syntax is permitted: Any punctuation character may be used as the delimiter between date parts. For example, '2012-12-31', '2012/12/31', '2012^12^31', and '2012#12#31' are equivalent.
As a string with no delimiters in either 'YYYYMMDD' or 'YYMMDD' format, provided that the string makes sense as a date. For example, '20070523' and '070523' are interpreted as '2007-05-23', but '071332' is illegal (it has nonsensical month and day parts) and becomes '0000-00-00'.
As a number in either YYYYMMDD or YYMMDD format, provided that the number makes sense as a date. For example, 19830905 and 830905 are interpreted as '1983-09-05'.
MySQL recognizes DATETIME and TIMESTAMP values in these formats:
As a string in either 'YYYY-MM-DD HH:MM:SS' or 'YY-MM-DD HH:MM:SS' format. A “relaxed” syntax is permitted here, too: Any punctuation character may be used as the delimiter between date parts or time parts. For example, '2012-12-31 11:30:45', '2012^12^31 11+30+45', '2012/12/31 11*30*45', and '2012#12#31 11^30^45' are equivalent.
The only delimiter recognized between a date and time part and a fractional seconds part is the decimal point.
The date and time parts can be separated by T rather than a space. For example, '2012-12-31 11:30:45' '2012-12-31T11:30:45' are equivalent.
As a string with no delimiters in either 'YYYYMMDDHHMMSS' or 'YYMMDDHHMMSS' format, provided that the string makes sense as a date. For example, '20070523091528' and '070523091528' are interpreted as '2007-05-23 09:15:28', but '071122129015' is illegal (it has a nonsensical minute part) and becomes '0000-00-00 00:00:00'.
As a number in either YYYYMMDDHHMMSS or YYMMDDHHMMSS format, provided that the number makes sense as a date. For example, 19830905132800 and 830905132800 are interpreted as '1983-09-05 13:28:00'.
Notably, MySQL does not support the incomplete formats that you wish to use.
That MySQL happens to accept some of the incomplete formats you've tried (apparently by padding with zeroes) is undocumented behaviour, quite possibly unintended by the developers. It cannot (and should not) be relied upon, not least because edge cases could exist under which the behaviour breaks; or because the behaviour could be changed without warning in a future release.
If it's absolutely necessary to provide such incomplete temporal literals to MySQL (which it shouldn't be, as your data access layer ought to be aware of the type of values it is handling and provide them to MySQL in a supported format), you can use its STR_TO_DATE() function to parse them accordingly:
Unspecified date or time parts have a value of 0, so incompletely specified values in str produce a result with some or all parts set to 0:
mysql> SELECT STR_TO_DATE('abc','abc');
-> '0000-00-00'
mysql> SELECT STR_TO_DATE('9','%m');
-> '0000-09-00'
mysql> SELECT STR_TO_DATE('9','%s');
-> '00:00:09'
Range checking on the parts of date values is as described in Section 11.3.1, “The DATE, DATETIME, and TIMESTAMP Types”. This means, for example, that “zero” dates or dates with part values of 0 are permitted unless the SQL mode is set to disallow such values.
So, for example, you might use:
STR_TO_DATE('2015-03', '%Y-%m');

Try unix_timestamp()
SELECT like [..] WHERE unix_timestamp(timestamp) < '2015-03'

Related

MySQL equivalent of php's strtotime()

Is there a way to convert a string such as "-1 week" or "-5 minutes" into a datetime value in MySQL similar to php's extremely convenient strtotime() function?
I have a table that stores a human-readable time interval (such as "2 minutes") in one column and a datetime in another column.
I would like to select the rows where more than the amount of time specified in interval has elapsed since datetime.
MySQL doesn't have an equivalent of PHP's strtotime() in the sense that there is nothing that will automatically attempt to parse and determine the format of a date string using by assuming multiple formats.
What it does have is STR_TO_DATE(str,format) which requires you specify the format of your date, time or date + time string. It is the equivalent of PHP's date_create_from_format(format, str) function (though the format of the format parameter are different).
Here are some examples given from the MySQL documentation. They show a date being passed along with the format string that lets it know how the date string is to be interpreted:
SELECT STR_TO_DATE('01,5,2013','%d,%m,%Y');
SELECT STR_TO_DATE('May 1, 2013','%M %d,%Y');
Alternatively, you can cast a string to a date, time or datetime type, but they require a specific format (YYYY-MM-DD hh:mm:ss.fraction) for it to work:
SELECT CAST("2019-11-21" AS DATE);
If you deviate too far from that format it will make a few assumptions but could produce an incorrect date.

Operators on date/time fields

Is the + and - operator defined in standard sql on date/time types, for example Time, Duration, Date, Datetime, Timestamp. Here is an example from MySQL:
select
date '1983-09-05', time '01:02:03', timestamp '2014-01-01 01:02:03',
date '1983-09-05' + time '01:02:03', timestamp '1983-09-05 01:02:03' + time '01:02:03'
It seems the last three results just give gibberish (in terms of actually giving a meaningful result). Is there a standard in how time-types are supposed to add and subtract or it's undefined behavior and it's suggested to use functions for this kind of stuff?
MySQL doesn't produce gibberish. It just gets confused. The problem is that MySQL is trying to figure out what you mean:
Is + really for numeric addition or for date/times?
Is the constant really a string, date/time, or number?
MySQL makes choices for these that are (perhaps) counterintuitive. For instance, a date/time values such as '2014-01-01 01:02:03' is converted to a number that looks like 20140101010203. This conversion happens implicitly under some circumstances.
Let me illustrate this just with the dates. You might think these are equivalent:
select '2021-01-01' + 40,
date '2021-01-01' + 40,
'2021-01-01' + interval 40 day
And the results are:
2061 20210141 2021-02-10
What is happening? In the first, + is treated a number addition. the first argument is converted to a number -- that is leading digits up to the first non-digit.
In the second, + is treated a number addition as well. The date is converted to a number and it looks like 20,210,101 -- that is YYYYMMDD as an integer.
Finally, the third tells MySQL to do what you intend -- add 40 days.
This has nothing to do with standard SQL, which clearly defines adding and subtracting intervals and the difference of timestamps. These are just the rules that MySQL uses for disambiguating + and - and for converting date/times to numbers.

Why does MySQL ignore mutiple dash delimiters in DATE columns?

Why does the below condition work? Does MySQL convert multiple dashes '---' to a single '-' dash. I tried finding an explanation for this but I couldn't find anything.
WHERE DATE(login_at) = '2019---04---30'
Can anyone explain to me how this works?
I'm using MySQL Version : 5.7.18
MySQL allows a flexibility, by auto-interpreting various delimiters, and converts them to proper date[time] format. It is well explained in documentation:
MySQL permits a “relaxed” format for values specified as strings, in
which any punctuation character may be used as the delimiter between
date parts or time parts. In some cases, this syntax can be deceiving.
For example, a value such as '10:11:12' might look like a time value
because of the :, but is interpreted as the year '2010-11-12' if used
in a date context. The value '10:45:15' is converted to '0000-00-00'
because '45' is not a valid month.
The only delimiter recognized between a date and time part and a
fractional seconds part is the decimal point.
So, in your case, MySQL is considering the delimiter as ---, and thus implicitly converting to Y-m-d format.
However, it is always better and safe to use the standard Y-m-d h:i:s format, to represent a date/datetime value.

MySql not allow to insert full date in datetime field in version 5.7.*

Just want to change this via configuration only. without change my query
Current SQL_MODE is
STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
I have one table dummy_test and there is one field name created_at
created_at => datetime(NULL)
When i perform below query
insert into dummy_test values('2018-08-14 05:38:11 pm')
ITs give me an error
Error in query (1292): Incorrect datetime value: '2018-08-14 05:38:11 pm' for column 'created_at' at row 1
It's working fine with
insert into dummy_test values('2018-08-14 05:38:11')
Per MySQL documentation:
MySQL recognizes TIME values in these formats:
As a string in 'D HH:MM:SS' format. You can also use one of the following “relaxed” syntaxes: 'HH:MM:SS', 'HH:MM', 'D HH:MM', 'D HH', or 'SS'. Here D represents days and can have a value from 0 to 34.
As a string with no delimiters in 'HHMMSS' format, provided that it makes sense as a time. For example, '101112' is understood as '10:11:12', but '109712' is illegal (it has a nonsensical minute part) and becomes '00:00:00'.
As a number in HHMMSS format, provided that it makes sense as a time. For example, 101112 is understood as '10:11:12'. The following alternative formats are also understood: SS, MMSS, or HHMMSS.
So you'll have to use 24-hour time when sending to MySQL. If you're passing the date/time via PHP, this question might be helpful.
you can use STR_TO_DATE function
ie.. insert into dummy_test values(STR_TO_DATE('2018-08-14 05:38:11 PM', '%Y-%m-%d %h:%i:%s %p'))
If you want, you can add a "BeforeInsert" table trigger to intercept and convert those values.

MySQL 5.6 calculation with datatype time within function returns wrong value - but outside is ok

I use mySQL 5.6 on Windows 7 Pro x64 and have the following problem.
SELECT fee(100, '12:00:00');
returns 500,000 which is obviously not correct.
But
SELECT 100 * '12:00:00'/24;
returns the correct result which is 50.
DROP FUNCTION IF EXISTS fee;
DELIMITER //
CREATE FUNCTION fee(price INT, duration TIME)
RETURNS DECIMAL(15,2)
BEGIN
RETURN price * duration/24;
END //
DELIMITER ;
Have you ever encountered this problem? What is the reason behind it?
Thanks for any hints for solving this.
My guess is that in 100 * '12:00:00'/24 expression '12:00:00' is evaluated as string, not as a time expression, and in '12:00:00'/24 operation the string is converted to a number, so it is executed as 12/24, which gives the expected result.
However, when the fee() function is called, '12:00:00' is passed to a parameter with TIME data type. In the duration/24 operation duration is converted to integer first, then the division is executed. However, select cast(cast('12:00:00' as time) as integer) conversion yields 120000, not 12. 120000/24*100=500000 - this is the output received from the original function. According to mysql documentation on TIME:
Be careful about assigning abbreviated values to a TIME column. MySQL
interprets abbreviated TIME values with colons as time of the day.
That is, '11:12' means '11:12:00', not '00:11:12'. MySQL interprets
abbreviated values without colons using the assumption that the two
rightmost digits represent seconds (that is, as elapsed time rather
than as time of day). For example, you might think of '1112' and 1112
as meaning '11:12:00' (12 minutes after 11 o'clock), but MySQL
interprets them as '00:11:12' (11 minutes, 12 seconds). Similarly,
'12' and 12 are interpreted as '00:00:12'.
Although the documentation describes integer to time conversion, it is safe to assume that time to integer conversion works the same way. I would use price * time_to_sec(duration)/86400 to get the right result.
Thank you all for your helps and comments.
#Shadow, #B98 – you are right. The problem has to do with converting '12:00:00' to its corresponding numeric value.
I searched a lot about how MySQL performs converting time to number in general, however I didn't find anything.
So I started a little bit experimenting on it and this is what I found out about it yet:
The default datatype in MySQL is VARCHAR, so every value/"variable" which has no explicit datatype its datatype is VARCHAR(length of value/variable) as you've correctly guessed, Shadow.
Converting VARCHAR to a numeric datatype works generally like this: take all digits from the left of the string up to the point you find a character except 0-9. If immediately after the digits there is a dot “.”, take the dot as the decimal point and continue searching for decimal digits till the string ends or you find a character except 0-9.
So in short: take from the left of the string what matches the pattern [0-9][.[0-9]] and throw the rest of it away – as you mentioned it, B98. Examples: '12:30:59' = 12; '12whatever30whatever59' = 12; '12.30.59' = 12.30; '12.30whatever' = 12.30
However, converting TIME to a numeric datatype works a little bit different: First remove the colons then convert it to an integer. Exempels: '12:00:00' = 120000; '12:30:59' = 123059
Converting DATETIME to a numeric datatype works the same way as converting TIME to numeric, except here get the dashes in the date part, the space between date and time and the colons in the time part removed and then gets the whole string converted to an integer. Exempels: '2015-12-24 12:59:59' = '20151224125959'
Below you find a query which shows this behavior of MySQL.
DROP VIEW IF EXISTS datetimeTypes;
CREATE VIEW datetimeTypes AS
SELECT
'12:59:00.50' AS timeImplicit,
CAST('12:59:00.50' AS TIME) AS timeExplicit,
'12:59:00.50' / 1 AS timeImplicitDiv,
CAST('12:59:00.50' AS TIME) / 1 AS timeExplicitDiv,
'2015-12-24 12:59:59' AS datetimeImplicit,
CAST('2015-12-24 12:59:59' AS DATETIME) AS datetimeExplicit,
'2015-12-24 12:59:59' / 1 AS datetimeImplicitDiv,
CAST('2015-12-24 12:59:59' AS DATETIME)/1 AS datetimeExplicitDiv;
SHOW FIELDS FROM datetimeTypes;
SELECT * FROM datetimeTypes;