I need to store durations such as the following in mysql:
- 30 seconds
- 20 minutes, 12 seconds
- 1 month
What would be the best way to store this, as the definition of a month can't be reduced, for example, to a number of seconds. My first thought was to store each increment separate, for example:
- num_seconds
- num_minutes
- num_hours
- num_days
- num_months
This could store the above, but I was hoping there was a more direct and less verbose way of doing this.
My first option would be to store duration as a number of seconds with datatype INT. This will make computation and comparison operations fast and clean. Also you can easily compute the duration between two datetime values, by substracting their Unix timestamp. When you need to display the values, you can use mysql sec_to_time function :
However if you are looking to manipulate big durations (months, years), this will not work. In that case I would fallback on a VARCHAR column with iso8601 duration format, like P3Y6M4DT12H30M5S for 3 years, 6 months, 4 days, 12 hours, 30 minutes and 5 seconds. It is a well known format that is supported by many applications (but will be more tedious to manipulate).
Store your intervals for years to months as an integer counting the number of months (12 months = 1 year) and your intervals for days to seconds as an integer of number of seconds.
You can then use the DATE_ADD function to figure out your dates. Here's a SQL Fiddle example showing storing intervals as months and seconds, and adding them to a fixed date:
MySQL 5.6 Schema Setup:
create table mytab (dt date);
insert into mytab values (date '2018-01-01');
create table intervals (months int, seconds int);
insert into intervals values (13, 3661), (-13, -3661);
Query 1:
select dt
, months
, seconds
, date_add(date_add(dt, interval months month)
, interval seconds second) result
from mytab cross join intervals
Results:
| dt | months | seconds | result |
|------------|--------|---------|----------------------|
| 2018-01-01 | 13 | 3661 | 2019-02-01T01:01:01Z |
| 2018-01-01 | -13 | -3661 | 2016-11-30T22:58:59Z |
Lets say shop is working from 8:00 till 23:00 and we use time format. Then it's easy. Some kind of:
where NOW() > start and NOW() < end
But what if shop working until 1:00am next day? And now exactly 23:00; So 23 > 1. This is will not gonna work.
So how to store and search business time in the correct way? Maybe in the end field better to store difference in seconds or i even don't know...
UPD: If you recommend use timestamp, then how i will find this time after one year, for example? We need to convert all dates to one?
The only solution that i decided use for now.
select * from times where
('05:00:00' between opens::time and closes::time) or
(
closes::time < opens::time and
'05:00:00' >= opens::time and
'05:00:00' > closes::time
) or
(
closes::time < opens::time and
opens::time > '05:00:00' and
closes::time > '05:00:00'
) and dow = 4
So for 13:00:00 - 04:00:00 I have results when variable is:
05:00:00 - no results
12:00:00 - no results
00:00:00 - 1 row
01:00:00 - 1 row
18:00:00 - 1 row
If you have any better idea, please share
The only correct way to store business hours is to use iCalendar RRules and ExDates
Store the rules a table. Use a library (Postgres has a few) to generate opening hours for the upcoming year. Use a materialized view for this
This lets you handle things like holidays, being closed on the last Thursday of every month, etc.
Its a little bit unclear what language you are in. But here are some examples. If it is formattet as dateTime from the server to example C#, then you can use: start> date1 && end < date2.
If using MySQL then check this post: MySQL "between" clause not inclusive?
t=# create table shop(i int, opens time, closes time, dow int);
CREATE TABLE
t=# insert into shop select g+10,'11:00','23:00', g from generate_series(1,5,1) g;
INSERT 0 5
t=# insert into shop select 23,'12:00','13:00', 6;
INSERT 0 1
then your logic would work:
t=# select * from shop where now()::time between opens and closes and extract(dow from now())::int = dow;
i | opens | closes | dow
----+----------+----------+-----
14 | 11:00:00 | 23:00:00 | 4
(1 row)
it is open on Thursday ATM.
and example for Satruday on and not on time:
t=# select * from shop where '2017-08-12 12:59'::time between opens and closes and extract(dow from '2017-08-12 12:59'::timestamp)::int = dow;
i | opens | closes | dow
----+----------+----------+-----
23 | 12:00:00 | 13:00:00 | 6
(1 row)
Time: 0.240 ms
t=# select * from shop where '2017-08-12 13:01'::time between opens and closes and extract(dow from '2017-08-12 13:01'::timestamp)::int = dow;
i | opens | closes | dow
---+-------+--------+-----
(0 rows)
You should use TIMESTAMP as data_type for start and end column. then you can use where NOW() > start and NOW() < end. It will work fine.
I was writing a mini scheduler that perform certain task.
For calculating trigger time, I am using MYSQL. I am stucked at writing one of the query.
Find immediate DateTime which is greater than the given prevtime,
AND
the Day of the required immediate datetime should be ANY of given days
AND
time(HH:MM:SS) portion of required immediate datetime should be equal to given time.
Examples:
(a)
If given days are ('MON', 'WEDNES', 'SAT'),
given time is 10:15:00,
given prevtime is 2014-11-12 23:17:00
Then MYSQL should return
2014-11-15 10:15:00
(b)
Given Days: ('SUN','SAT','TUES')
Given Time: 09:10:00
Given prevtime is 2014-11-30 07:05:12
MYSQL should return 2014-11-30 09:10:00
(c)
Given Days: ('MON','THURS','SAT')
Given Time: 11:00:00
Given prevtime is 2014-12-29 11:55:12
MYSQL should return 2015-01-01 11:00:00
(d)
Days: (SUN, THURS, SAT)'
Given prevtime is 2014-02-27 18:15:00
Given Time 15:15:00
MYSQL Query result: 2014-03-01 15:15:00
(e)
DAYS: (TUES, WED, FRI)
Prev Date: 2014-12-23 09:30:00
Time : 08:00:00
Expected Result:
2014-12-24 08:00:00
(f)
DAYS: SUN, TUES, THURS
Prev Date: 2014-07-31 10:10:00
Time: 06:07:08
Expected Res:
2014-08-03 06:07:08
Using numeric weekday numbers, 0=Monday, 6=Sunday:
set #day1=0;
set #day2=2;
set #day3=5;
set #time=time('10:15:00');
set #prevtime=timestamp('2014-11-12 23:17:00');
select if(weekday(#nexttime:=date_add(concat(date(#prevtime),' ',#time),interval if(#time>time(#prevtime),0,1) day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,if(weekday(#nexttime:=date_add(#nexttime,interval 1 day)) in (#day1,#day2,#day3),#nexttime,date_add(#nexttime,interval 1 day))))))) as nexttime;
If you have only one weekday, you can set all three variables to the same number.
You should be able to formulate the where clause using the DAYNAME(), HOUR(), MINUTE() and SECOND() functions:
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html
If performance is inadequate and you start wishing you could index on DAYNAME(columname) for example, you can consider denormalizing your data and storing the DAYNAME value separately.
It might be simpler to switch to Postgres at that point though:
http://www.postgresql.org/docs/9.1/static/indexes-expressional.html
I am having one table where data gets every 15 min interval of timestamp like
2014-02-01 23:00:00
2014-02-01 23:15:00
2014-02-01 23:30:00
2014-02-01 23:45:00
2014-02-02 00:00:00
Now i want to dump this table data for last 24 hours. I am using MySQLDump to create dump file which will have following condition in it with where clause
-- WHERE: DATE_FORMAT(emdt,'%d-%m-%Y')= DATE_FORMAT(NOW(),'%d-%m-%Y')
Now even with this condition i want to put one more logic is the data which get recorded at 2014-02-02 00:00:00' should also come under last 24 hours data but when it compared the data it is not picking up this data. Hence it is affecting last day billing.
So if i ran it next day same dump file it should only pickup records from 2014-02-02 00:15:00' and so on.
Please help me to put here right extraction login.
TIA,
Regards,
Nirav
Try using:
WHERE emdt >= DATE_SUB(NOW(), INTERVAL 1 DAY)
Is there any way to get milliseconds out of a timestamp in MySql or PostgreSql (or others just out of curiosity)?
SELECT CURRENT_TIMESTAMP
--> 2012-03-08 20:12:06.032572
Is there anything like this:
SELECT CURRENT_MILLISEC
--> 1331255526000
or the only alternative is to use the DATEDIFF from the era?
For MySQL (5.6+) you can do this:
SELECT ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)
Which will return (e.g.):
1420998416685 --milliseconds
To get the Unix timestamp in seconds in MySQL:
select UNIX_TIMESTAMP();
Details: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_unix-timestamp
Not tested PostgreSQL, but according to this site it should work: http://www.raditha.com/postgres/timestamp.php
select round( date_part( 'epoch', now() ) );
In mysql, it is possible to use the uuid function to extract milliseconds.
select conv(
concat(
substring(uid,16,3),
substring(uid,10,4),
substring(uid,1,8))
,16,10)
div 10000
- (141427 * 24 * 60 * 60 * 1000) as current_mills
from (select uuid() uid) as alias;
Result:
+---------------+
| current_mills |
+---------------+
| 1410954031133 |
+---------------+
It also works in older mysql versions!
Thank you to this page: http://rpbouman.blogspot.com.es/2014/06/mysql-extracting-timstamp-and-mac.html
The main misunderstanding in MySQL with timestamps is that MySQL by default both returns and stores timestamps without a fractional part.
SELECT current_timestamp() => 2018-01-18 12:05:34
which can be converted to seconds timestamp as
SELECT UNIX_TIMESTAMP(current_timestamp()) => 1516272429
To add fractional part:
SELECT current_timestamp(3) => 2018-01-18 12:05:58.983
which can be converted to microseconds timestamp as
SELECT CAST( 1000*UNIX_TIMESTAMP(current_timestamp(3)) AS UNSIGNED INTEGER) ts => 1516272274786
There are few tricks with storing in tables. If your table was created like
CREATE TABLE `ts_test_table` (
`id` int(1) NOT NULL,
`not_fractional_timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
than MySQL will NOT store fractional part within it:
id, not_fractional_timestamp
1, 2018-01-18 11:35:12
If you want to add fractional part into your table, you need to create your table in another way:
CREATE TABLE `ts_test_table2` (
`id` int(1) NOT NULL,
`some_data` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL,
`fractional_timestamp` timestamp(3) NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
that leads to required result:
id, some_data, fractional_timestamp
1, 8, 2018-01-18 11:45:40.811
current_timestamp() function is allowed to receive value up to 6, but I've found out (at least in my installed MySQL 5.7.11 version on Windows) that fraction precision 6 leads to the same constant value of 3 digits at the tail, in my case 688
id, some_data, fractional_timestamp
1, 2, 2018-01-18 12:01:54.167688
2, 4, 2018-01-18 12:01:58.893688
That means that really usable timestamp precision of MySQL is platform-dependent:
on Windows: 3
on Linux: 6
In Mysql 5.7+ you can execute
select current_timestamp(6)
for more details
https://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
Poster is asking for an integer value of MS since Epoch, not a time or S since Epoch.
For that, you need to use NOW(3) which gives you time in fractional seconds to 3 decimal places (ie MS precision): 2020-02-13 16:30:18.236
Then UNIX_TIMESTAMP(NOW(3)) to get the time to fractional seconds since epoc:
1581611418.236
Finally, FLOOR(UNIX_TIMESTAMP(NOW(3))*1000) to get it to a nice round integer, for ms since epoc:
1581611418236
Make it a MySQL Function:
CREATE FUNCTION UNIX_MS() RETURNS BIGINT DETERMINISTIC
BEGIN
RETURN FLOOR(UNIX_TIMESTAMP(NOW(3))*1000);
END
Now run SELECT UNIX_MS();
Note: this was all copied by hand so if there are mistakes feel free to fix ;)
Use:
Select curtime(4);
This will give you milliseconds.
The correct way of extracting miliseconds from a timestamp value on PostgreSQL accordingly to current documentation is:
SELECT date_part('milliseconds', current_timestamp);
--OR
SELECT EXTRACT(MILLISECONDS FROM current_timestamp);
with returns: The seconds field, including fractional parts, multiplied by 1000. Note that this includes full seconds.
Easiest way I found to receive current time in milliseconds in MySql:
SELECT (UNIX_TIMESTAMP(NOW(3)) * 1000)
Since MySql 5.6.
None of these responses really solve the problem in postgreSQL, i.e :
getting the unix timestamp of a date field in milliseconds
I had the same issue and tested the different previous responses without satisfying result.
Finally, I found a really simple way, probably the simplest :
SELECT ROUND(EXTRACT (EPOCH FROM <date_column>::timestamp)::float*1000) as unix_tms
FROM <table>
namely :
We extract the pgSQL EPOCH, i.e. unix timestamp in floatting seconds from our column casted in timestamp prudence (in some complexe queries, pgSQL could trow an error if this cast isn't explicit. See )
then we cast it in float and multiply it by 1000 to get the value in milliseconds
then we round it to drop the fractional part
In PostgreSQL you can use :
SELECT extract(epoch from now());
on MySQL :
SELECT unix_timestamp(now());
Here's an expression that works for MariaDB and MySQL >= 5.6:
SELECT (UNIX_TIMESTAMP(NOW()) * 1000000 + MICROSECOND(NOW(6))) AS unix_now_in_microseconds;
This relies on the fact that NOW() always returns the same time throughout a query; it's possible that a plain UNIX_TIMESTAMP() would work as well, I'm not sure based on the documentation. It also requires MySQL >= 5.6 for the new precision argument for NOW() function (MariaDB works too).
Postgres: SELECT (extract(epoch from now())*1000)::bigint;
In MariaDB you can use
SELECT NOW(4);
To get milisecs. See here, too.
In PostgreSQL we use this approach:
SELECT round(EXTRACT (EPOCH FROM now())::float*1000)
For mysql:
SELECT (UNIX_TIMESTAMP() * 1000) AS unix_now_in_microseconds; --- 1600698677000
I felt the need to continue to refine, so in MySQL:
Current timestamp in milliseconds:
floor(unix_timestamp(current_timestamp(3)) * 1000)
Timestamp in milliseconds from given datetime(3):
floor(unix_timestamp("2015-04-27 15:14:55.692") * 1000)
Convert timestamp in milliseconds to datetime(3):
from_unixtime(1430146422456 / 1000)
Convert datetime(3) to timestamp in milliseconds:
floor(unix_timestamp("2015-04-27 14:53:42.456") * 1000)
For everyone here, just listen / read the comments of Doin very good! The UNIX_TIMESTAMP() function will, when a datatime-string is given, contact a local time, based on the timezone of the MySQL Connection or the server, to a unix timestamp. When in a different timezone and dealing with daylight savings, one hour per year, this will go wrong!
For example, in the Netherlands, the last Sunday of October, a second after reaching 02:59:59 for the first time, the time will be set back to 02:00:00 again. When using the NOW(), CURTIME() or SYSDATE()-functions from MySQL and passing it to the UNIX_TIMESTAMP() function, the timestamps will be wrong for a whole our.
For example, on Satudray 27th of October 2018, the time and timestamps went like this:
Local time | UTC Time | Timestamp | Timestamp using MYSQL's UNIX_TIMESTAMP(NOW(4))
----------------------------------+---------------------------+--------------+-----------------------------------------------------
2018-10-27 01:59:59 CET (+02:00) | 2018-10-26 23:59:59 UTC | 1540598399 | 1540598399
2018-10-27 02:00:00 CET (+02:00) | 2018-10-27 00:00:00 UTC | 1540598400 | 1540598400 + 1 second
2018-10-27 02:59:59 CET (+02:00) | 2018-10-27 00:59:59 UTC | 1540601999 | 1540601999
2018-10-27 03:00:00 CET (+02:00) | 2018-10-27 01:00:00 UTC | 1540602000 | 1540602000 + 1 second
2018-10-27 03:59:59 CET (+02:00) | 2018-10-27 01:59:59 UTC | 1540605599 | 1540605599
2018-10-27 04:00:00 CET (+02:00) | 2018-10-27 02:00:00 UTC | 1540605600 | 1540605600 + 1 second
But on Sunday 27th of October 2019, when we've adjusted the clock one hour. Because the local time, doensn't include information whether it's +02:00 or +01:00, converting the time 02:00:00 the first time and the second time, both give the same timestamp (from the second 02:00:00) when using MYSQL's UNIX_TIMESTAMP(NOW(4)) function. So, when checking the timestamps in the database, it did this: +1 +1 +3601 +1 +1 ... +1 +1 -3599 +1 +1 etc.
Local time | UTC Time | Timestamp | Timestamp using MYSQL's UNIX_TIMESTAMP(NOW(4))
----------------------------------+---------------------------+--------------+-----------------------------------------------------
2019-10-27 01:59:59 CET (+02:00) | 2019-10-26 23:59:59 UTC | 1572134399 | 1572134399
2019-10-27 02:00:00 CET (+02:00) | 2019-10-27 00:00:00 UTC | 1572134400 | 1572138000 + 3601 seconds
2019-10-27 02:59:59 CET (+02:00) | 2019-10-27 00:59:59 UTC | 1572137999 | 1572141599
2019-10-27 02:00:00 CET (+01:00) | 2019-10-27 01:00:00 UTC | 1572138000 | 1572138000 - 3599 seconds
2019-10-27 02:59:59 CET (+01:00) | 2019-10-27 01:59:59 UTC | 1572141599 | 1572141599
2019-10-27 03:00:00 CET (+01:00) | 2019-10-27 02:00:00 UTC | 1572141600 | 1572141600 + 1 second
Relaying on the UNIX_TIMESTAMP()-function from MySQL when converting local times, unfortunately, is very unreliable! Instead of using SELECT UNIX_TIMESTAMP(NOW(4)), we're now using the code below, which seams to solve the issue.
SELECT ROUND(UNIX_TIMESTAMP() + (MICROSECOND(UTC_TIME(6))*0.000001), 4)
Mysql:
SELECT REPLACE(unix_timestamp(current_timestamp(3)),'.','');
I faced the same issue recently and I created a small github project that contains a new mysql function UNIX_TIMESTAMP_MS() that returns the current timestamp in milliseconds.
Also you can do the following :
SELECT UNIX_TIMESTAMP_MS(NOW(3)) or SELECT UNIX_TIMESTAMP_MS(DateTimeField)
The project is located here : https://github.com/silviucpp/unix_timestamp_ms
To compile you need to Just run make compile in the project root.
Then you need to only copy the shared library in the /usr/lib/mysql/plugin/ (or whatever the plugin folder is on your machine.)
After this just open a mysql console and run :
CREATE FUNCTION UNIX_TIMESTAMP_MS RETURNS INT SONAME 'unix_timestamp_ms.so';
I hope this will help,
Silviu
Do as follows for milliseconds:
select round(date_format(CURTIME(3), "%f")/1000)
You can get microseconds by the following:
select date_format(CURTIME(6), "%f")