Convert MySQL now() based on timezone of rows being searched - mysql

Suppose I have data in a table "events" structured like this:
eventid | datetime_start | datetime_end | timezone
001 | 2016-01-01 10:00:00 | 2016-01-01 14:00:00 | America/Los_Angeles
002 | 2016-01-03 19:00:00 | 2016-01-03 22:00:00 | America/Los_Angeles
003 | 2016-01-17 02:00:00 | 2016-01-17 06:00:00 | America/New_York
004 | 2016-01-31 23:00:00 | 2016-02-01 01:00:00 | America/Los_Angeles
The timezone column allows dates/times to be stored exactly as entered rather than normalized to UTC, GMT, etc.
I want to query the table to find eventids where "now()" falls between datetime_start and datetime_end:
SELECT eventid FROM events WHERE now() BETWEEN datetime_start AND datetime_end
However, since "now()" is based on a fixed timezone (UTC in my case), is there any way to convert "now()" to match the timezone column row by row as it searches? Maybe something in the spirit of the following:
SELECT eventid FROM events WHERE CONVERT_TZ(now(),'UTC',timezone) BETWEEN datetime_start
AND datetime_end

Related

Find number of records in a date range

table : booked_timings
id | from date | todate | vehicle id
------------------------------------------------------------------
1 2017-05-04 06:00:00 2017-05-04 08:00:00 98
2 2017-05-04 10:00:00 2017-05-04 12:00:00 98
3 2017-05-04 15:00:00 2017-05-04 18:30:00 98
Above table consists of the booked timings of vehicle with id 98. I need an MySQL query to list out all the booked timings for vehicle with id 98 when user tries to book in following timings
(i) 08:00:00 to 12:00:00
(ii) 05:00:00 to 23:00:00
SELECT * FROM TABLE WHERE 1=1 AND
(
(
STR_TO_DATE(DATE_FORMAT(FROMDATE,'%H:%i:%s'),'%H:%i:%s') > STR_TO_DATE(08:00:00,'%H:%i:%s') AND STR_TO_DATE(DATE_FORMAT(FROMDATE,'%H:%i:%s'),'%H:%i:%s') < STR_TO_DATE(12:00:00,'%H:%i:%s')
)
OR
(
STR_TO_DATE(DATE_FORMAT(FROMDATE,'%H:%i:%s'),'%H:%i:%s') > STR_TO_DATE(05:00:00,'%H:%i:%s') AND STR_TO_DATE(DATE_FORMAT(FROMDATE,'%H:%i:%s'),'%H:%i:%s') < STR_TO_DATE(23:00:00,'%H:%i:%s')
)
)
As shown in above code,assuming that fromdate and todate are COLUMN with DATE datatype.And comparing only time fraction of that column with time duration which specified by you.Again here we provide string,so we need to convert this string to date using
STR_TO_DATE()function.
Try above code,hope this will helps.

How to convert date range into whole number (integer) in SQL

Let's say I have a date 2013-03-01 and date 2013-04-02. How do I get a integer value between that date, for example in this case 2 days. In SQL kindly advise how to construct the query
day_date
---------------------
2005-07-29 00:00:00
2013-03-01 00:00:00
2013-04-02 00:00:00
2013-06-01 00:00:00
2013-10-19 00:00:00
2013-10-23 00:00:00
2013-12-31 00:00:00
The DATEDIFF() function returns the time between two dates.
SELECT DATEDIFF('2014-11-30','2014-11-29') AS DiffDate

Select distinct and get sum of timestamp differences

I don't know if this is possible, but it'd be really awesome. I have a table of sign-ins for people who are logging time on different projects and I need to compile a report of time logged for each project for a given time period.
My table looks something like this:
id | project | time_in | time_out | break
----------------------------------------------------------------
1 | 1 | 2014-12-07 05:00:00 | 2014-12-07 10:00:00 | 30
2 | 2 | 2014-12-07 06:00:00 | 2014-12-07 13:00:00 | 15
3 | 1 | 2014-12-07 14:00:00 | 2014-12-07 18:00:00 | 0
4 | 3 | 2014-12-07 08:30:00 | 2014-12-07 18:45:00 | 75
5 | 2 | 2014-12-07 12:00:00 | 2014-12-07 16:30:00 | 0
What I'd like to be able to do is get a report of the time logged for each project given a date range, i.e. the total time, probably in seconds, logged for each project.
time_in and time_out are fields of type TIMESTAMP; break is an integer representing the number of minutes the person was on break. I need to get the sum of time_out - time_in - break for each project, e.g. for December 7:
project | time
---------------
1 | 34200
2 | 40500
3 | 34200
This is all I have so far:
SELECT DISTINCT
`project`
FROM `sign_ins`
WHERE
`time_in` >= '2014-12-07 00:00:00' AND
`time_out` <= '2014-12-08 00:00:00';
I appreciate your help on this, SO community. You guys are so brilliant.
You can get the difference in seconds by converting the date/time values to Unix time stamps. Then, just aggregate the differences using sum():
SELECT project,
SUM(UNIX_TIMESTAMP(time_out) - UNIX_TIMESTAMP(time_in) - (break * 60)) as DiffSecs
FROM `sign_ins`
WHERE `time_in` >= '2014-12-07 00:00:00' AND
`time_out` <= '2014-12-08 00:00:00'
GROUP BY project;

Wrong TimeZone Conversion in Ruby on Rails

I'm really stucked on this one.
Basically I want to Import a Excel-File. So I ended up using the CSV import from Rails.
CSV.open('[path-to-file]', 'r').each do |row|
Actually the Import is working fine, but there is among other things a Date-Column in the Excel/CSV-File and a DateTime-Column in the Database. I did following in the CSV.open-Method:
date = DateTime.strptime(row[0], "%Y-%m-%d").strftime("%Y-%m-%d")
start_datetime = DateTime.parse(date + " 6:30:00").utc
Event.create(:event_start => start_datetime)
This creates the correct Events with the correct DateTime in the Database. Looks for example like:
2008-11-18 09:30:00
My Problem:
If I choose to comment the line in the application.rb
config.time_zone = 'Berlin'
Rails uses UTC to display my Events and everything looks like the content of the Database.
If I choose to uncomment the config.time_zone part (what I definitely have to), Rails should add 1 hour (Berlin: UTC/GMT +1 hour). Actually it does add at least 1 hour, but sometimes 2 hours. There is no connection (for me), in which case Rails chooses to add 1 or 2 hours.
If I create an Event on the normal Websurface (in the Browser), everything is working fine (subtract on 1 hour to save UTC in DB and add 1 hour to display in correct TimeZone).
It would be really helpful, if you had some tips for me how I can try to localize this problem.
My System: Rails 3.2.3 on Ruby 1.9.3p194, MySQL on Suse Enterprise
Example:
CSV:
2012-01-08
2012-02-09
2012-03-10
2012-04-11
2012-05-12
2012-06-13
2012-07-14
2012-08-15
2012-09-16
2012-10-17
2012-11-18
2012-12-19
DB (MySQL):
| id | event_start
+----+---------------------
| 1 | 2012-01-08 06:30:00
| 2 | 2012-02-09 06:30:00
| 3 | 2012-03-10 06:30:00
| 4 | 2012-04-11 06:30:00
| 5 | 2012-05-12 06:30:00
| 6 | 2012-06-13 06:30:00
| 7 | 2012-07-14 06:30:00
| 8 | 2012-08-15 06:30:00
| 9 | 2012-09-16 06:30:00
| 10 | 2012-10-17 06:30:00
| 11 | 2012-11-18 06:30:00
| 12 | 2012-12-19 06:30:00
View (Browser) - Here I just used an .order("event_start ASC")
events.each do |ev|
ev.event_start
2012-12-19 07:30:00 +0100
2012-11-18 07:30:00 +0100
2012-10-17 08:30:00 +0200
2012-09-16 08:30:00 +0200
2012-08-15 08:30:00 +0200
2012-07-14 08:30:00 +0200
2012-06-13 08:30:00 +0200
2012-05-12 08:30:00 +0200
2012-14-11 08:30:00 +0200
2012-03-10 07:30:00 +0100
2012-02-09 07:30:00 +0100
2012-01-08 07:30:00 +0100
I'm pretty sure that the difference you're seeing is because of Daylight Saving Time (DST).
Note that all dates during summer are +0200, while all the others are +0100. That's the connection you didn't notice.
This is because Berlin is a timezone with DST. The dates you're importing are all UTC, they get written to the DB as UTC, but Rails (correctly) interprets them differently, depending on whether they're in summer or not.
That's why you don't get a constant 1-hour difference.
If you want the times to be 9:30 local time, no matter if in summer or winter, try using
Time.zone.parse("#{date} 09:30:00")
in your import code.
See ActiveSupport::TimeWithZone for details.

Group By sets of values

for a report I'm trying to query events from different shifts. The shifts start on 6 am, 2 pm, and 10 pm every day, and all of the data in the table is tagged with a datetime timestamp. Previously the graveyard shift wasn't doing anything important, so a simple group by DATE(stamp) was sufficient, but now it's 24/7 and I need to break it down into shifts.
Can anyone explain to me how to use a single group by clause to combine datetime values from a range or a set of values? The difficulty is that each graveyard shift spans two calendar days.
I've considered populating a table with 24 hours and shift numbers, then outer joining it and group by DATE(stamp), HOUR(stamp), but that seems hackish and possibly not even working, plus it would give 24 values for each day instead of 3, which then have to be combined in a superquery or script.
MySQL-specific is perfectly ok, that's all we ever use in the reporting.
Since they are all 8-hour shifts, offset by 6 hours from starting at midnight, you turn Stamp into the start-of-shift time like this:
select
stamp,
adddate(date(subdate(stamp, interval 6 hour)),
interval ((hour(subdate(stamp, interval 6 hour))
div 8) * 8) + 6 hour) as shift_start
from mytable;
This substracts 6 hours, then rounds the hour down to either 0 1 or 2 by using integer division, then expands it out again.
Here's the test code with some edge cases:
create table mytable (stamp datetime);
insert into mytable values ('2011-08-17 22:00:00'), ('2011-08-17 23:01:00'),
('2011-08-18 00:02:00'), ('2011-08-18 05:59:00'), ('2011-08-18 06:00:00'),
('2011-08-18 13:59:00'), ('2011-08-18 14:00:00'), ('2011-08-18 17:59:00');
Output of above query:
+---------------------+---------------------+
| stamp | shift_start |
+---------------------+---------------------+
| 2011-08-17 22:00:00 | 2011-08-17 22:00:00 |
| 2011-08-17 23:01:00 | 2011-08-17 22:00:00 |
| 2011-08-18 00:02:00 | 2011-08-17 22:00:00 |
| 2011-08-18 05:59:00 | 2011-08-17 22:00:00 |
| 2011-08-18 06:00:00 | 2011-08-18 06:00:00 |
| 2011-08-18 13:59:00 | 2011-08-18 06:00:00 |
| 2011-08-18 14:00:00 | 2011-08-18 14:00:00 |
| 2011-08-18 17:59:00 | 2011-08-18 14:00:00 |
+---------------------+---------------------+
Try this:
GROUP BY DATE(DATE_ADD(Stamp,INTERVAL -6 HOUR))
That should keep all your shifts on the same day
I think you should pursue your "table with hours and shift numbers" approach. Further, I think you should consider using a calendar table i.e. a table not just covering 24 hours but the whole past, present and future of your enterprise's expected needs. This is not hackish: rather, it is a tried and tested approach. The idea is that SQL is a declarative language designed to query data in tables so a declarative, data-driven solutions make a lot of sense.