Query based on datetime interval - google-sheets

I would like to find out which driver is available during a certain time based on his start and end datetime.
I have two worksheets.
Worksheet1 displayes the start and end time of drivers in datetime:
Driver | Start Time | End Time
------------------------------------------
Driver 1 | 25-05-2015 09:00 | 25-05-2015 15:00
Driver 2 | 25-05-2015 15:00 | 25-05-2015 21:00
Driver 2 | 26-05-2015 09:00 | 26-05-2015 15:00
Driver 1 | 26-05-2015 12:00 | 26-05-2015 17:00
Worksheet2 displays the start date of a tour
Tour 1 | 25-05-2015 11:00
Tour 2 | 25-05-2015 16:00
Tour 3 | 25-05-2015 17:00
Tour 4 | 26-05-2015 09:00
I would like to query in worksheet 2 which driver from worksheet 1 is available during the time of the tour start.
=QUERY(Worksheet1!A:C,"select A where C <= date '"&text(A2,"yyyy-MM-dd")&"'",0)
But I understand that I will need to be working with timedate instead (to get the actual times of the start and end of the shifts) and I will need to query an interval.

If you want to use dates and times, you need datetime instead of date. And since there is an interval, another comparison should be made with column B. The query string, shown with linebreaks for readability, would be
"select A
where B <= datetime '" & text(A2, "yyyy-MM-dd hh:mm:ss") & "'
and C >= datetime '" & text(A2, "yyyy-MM-dd hh:mm:ss") & "'"
This ensures that the datetime in A2 is between the datetimes in columns B and C.

Related

Find the earliest and latest time for each day based on a time range criteria in Google Sheets

I have two columns in Google Sheets:
B | C
Date | Time
2/21/2022 | 7:30:02 AM
2/21/2022 | 7:48:02 AM
2/21/2022 | 8:24:05 AM
2/21/2022 | 2:34:02 PM
2/21/2022 | 2:56:02 PM
2/22/2022 | 8:05:00 AM
2/22/2022 | 8:15:00 AM
2/22/2022 | 3:00:02 PM
2/22/2022 | 3:15:02 PM
2/22/2022 | 3:25:14 PM
etc...
DateTimeData Image
There are dozens of entries for Time in a single day. I would like to find the earliest Time and latest Time for each day within a Time range between (7:30:00 AM to 8:30:00 AM) AND (2:30:00 PM to 3:30:00 PM).
The result will be two Time values for each day.
i.e.
B | C
Date | Time
2/21/2022 | 7:48:02 AM
2/21/2022 | 2:56:02 PM
2/22/2022 | 8:05:00 AM
2/22/2022 | 3:15:02 PM
etc..
GoalResult Image
I was able to find the values of each day within the Time Range using this formula, but have not been able to extract the Lowest and Highest Time values for each day.
=ArrayFormula((IF(C2:C="","",IF((C2:C1000>VALUE(timevalue("7:30 AM")))(C2:C1000<VALUE(timevalue("8:30 AM"))),C2:C1000,IF(C2:C="","",IF((C2:C1000>VALUE(timevalue("2:30 PM")))(C2:C1000<VALUE(timevalue("3:30 PM"))),C2:C1000,0))))))
Any ideas? Thanks!
Try
=query(A:B,"select A,min(B),max(B) group by A label max(B) 'End time' , min(B) 'Start time' ")

Google Sheets duration between 2 timestamps where the ending timestamp crosses into midnight

I have a sheet where I'm calculating the durations of time between two timestamps.
As an example, I have column A, which is the starting time, and column B, which is the stopping time. Both A and B are formatted as Time. Column C holds the duration between A and B, and is formatted as Duration.
The formula =SUM(B1 - A1) works fine for the most part, until I reach a situation in which B1 has crossed into midnight of the next day
For example if I have the following:
A | B | C
------------------------------
23:30 | 23:40 | 0:10:00
23:30 | 23:50 | 0:20:00
23:30 | 00:00 | -23:30:00
23:30 | 00:10 | -23:20:00
How do I circumvent this issue and have C3 & C4 properly calculate the value to be 0:30:00 and 0:40:00 respectively? My timestamps don't include date-related information, columns A and B only hold HH:MM:SS times. These durations will also never be longer than a maximum of 3 hours, so there's no need to worry about it correctly detecting overflow situations like Day 1 12:00:00 -> Day 2: 12:00:00 = 24:00:00
For mixed duration that crossed midnight of the next day (overlaps midnight)
try either of the following formulas.
=ArrayFormula(IF(A42:A="",,MOD(B42:B-A42:A, 1)))
OR
=ArrayFormula(IF(LEN(A42:A)<>0,
IF(B42:B55<A42:A,B42:B-A42:A+1,B42:B-A42:A),""))
Please adjust ranges to your needs
If you format the result column as Time, you don't need to change anything. If not, here is another option:
=B2-A2+(B2<A2)
Edit
An attempt with an array formulas:
First two results formatted as Time, last as Duration

Formula To Calculate Accrued Quarterly Allowance Based On Employment Length

Employees get a specified amount of allowance for each quarter of the year, which accrues as the year progresses, and resets each new year. The amount they get each quarter is determined by how long they have been employed.
YEARS EMPLOYED | $ PER QUARTER
<1 | $0
>=1 | $37.5
>=3 | $66.66
>=5 | $100
Using only one reference cell (the hire date in Cell O1), I'm trying to figure out how to put a formula together which would display the sum of all quarterly amounts accrued this year to date.
The part that is giving me trouble is that when the amount per quarter changes during the year (due to the number of years employed reaching the next level), any previous quarters for the year need to remain at their previously assigned amounts, for example:
If the HIRE DATE is April 16, 2015:
QUARTER | YEARS HIRED | QUARTERLY ALLOWANCE
Q1 | 2.71 | $37.5
Q2 | 2.96 | $37.5
Q3 | 3.21 | $66.66
Q4 | 3.46 | $66.66
If TODAY'S DATE is May 21, 2018, the total allowance amount to date = $75 (Q1+Q2)
If TODAY'S DATE is Oct 7, 2018, the total allowance amount to date = $208.32 (Q1+Q2+Q3+Q4)
How can this be done with one formula, using only the hire date in Cell O1?
I think the easiest way to get the number of complete years hired is to use Datedif:
=DATEDIF(O$1,DATE(YEAR(P$1),RIGHT(A2)*3-2,1),"Y")
placed in (say) E2, where the current date is in P1 and the list of quarters starts in A2
Then a lookup to get the allowance calculated on the first day of each quarter:
=LOOKUP(E2,{0,1,3,5},{0,37.5,66.66,100})
and finally a check to see if the first day of the quarter is on or before the current date:
=P1>=DATE(YEAR(P1),MONTH(RIGHT(A2)*3-2),1)
Then you could get the total from
=SUMPRODUCT(F2:F5*G2:G5)
Or you could combine all this into one big array-type formula:
=SUMPRODUCT((P1>=DATE(YEAR(P1),{1,4,7,10},1))*LOOKUP(DATEDIF(O1,DATE(YEAR(P1),{1,4,7,10},1),"Y"),{0,1,3,5},{0,37.5,66.66,100}))

How to return a list of users who should be notified of an appointment at a set time each day

I have a list of users who need to be notified at different times per day, and based on their timezone.
So I have essentially 2 tables: Schedule and User
Schedule
-id
-scheduled_time
Users
-id
-schedule_id
-timezone
So there could be a schedule for 9AM.
Users who are associated with this 9AM schedule, by they also have their timezone set to UTC -4 or UTC -5.
I will have a service that will Poll every x minutes to query the database.
Is this a query I can perform in postgresql directly or is this application dependant and based on the ORM I am using?
Parsing timestamps for time of day just sounds annoying and I recommend doing it from your Rails models.
If you're dead set on doing it from the database, see this question: Convert a UTC timezone in postgresql to EST (local time), and here's another article describing how to work with time zones in postgres: http://blog.untrod.com/2016/08/actually-understanding-timezones-in-postgresql.html.
But my sense is you'll get a simpler and easier solution if you use ActiveSupport::TimeWithZone maths, in order to associate each user with a given UTC time at which to notify them.
Example:
Schedule X runs at 16:00 UTC and schedule Y runs at 17:00 UTC.
User A is in UTC-0700 (PDT) and User B is in UTC-0600 (Mountain).
If B wants to be notified at 10:00 AM, then do the math in your application in order to associate them with Schedule X.
If A wants to be notified at 10:00 AM, then do the math in your application in order to associate them with Schedule Y.
Let me know if you would need help with the specifics. Hints:
The Time of Day gem offers a nice way to work with times of day, including time zone maths.
https://github.com/jackc/tod
https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
I will have a service that will Poll every x minutes to query the database.
Is this a query I can perform in Postgresql directly ... ?
Yes, you can. Imagine the use of the PostgreSQL-server time (function now() or current_timestamp) something as
SELECT u.*
FROM schedule s INNER JOIN users u
ON u.schedule_id=s.id
WHERE now()::time=(s.scheduled_time+u.timezone)
But, as we will see, the "every x minutes" can't sync with the Schedule table. There are two ways for sync:
using <= and updating the table with a boolean day-flag at users (cleaning every day after 24hs to re-scheduling). No risk.
using = with a round(time,x) here... Perhaps with a very little risk when your hypothesis of "every x minutes" is not perfect.
Lets simulate all the thing:
CREATE TABLE schedule (id serial PRIMARY KEY, scheduled_time time);
INSERT INTO schedule (scheduled_time) VALUES
(now()), ('9:00AM'::time), ('16:30'::time);
CREATE TABLE users (
id serial,
schedule_id int references schedule(id),
timezone interval -- see pg_timezone_names.utc_offset
);
INSERT INTO users (schedule_id,timezone) VALUES
(2, '00:00:00'::interval), (2, '-04:00:00'::interval), (1, '-05:00:00'::interval),
(1, '-05:00:00'::interval), (1, '-04:00:00'::interval), (2, '-05:00:00'::interval);
CREATE VIEW vw_user_scheduled AS
SELECT u.*, s.scheduled_time, s.scheduled_time+u.timezone AS ref_time
FROM schedule s INNER JOIN users u
ON u.schedule_id=s.id
;
The samples vw_user_scheduled are:
id | schedule_id | timezone | scheduled_time | ref_time
----+-------------+-----------+-----------------+-----------------
1 | 2 | 00:00:00 | 09:00:00 | 09:00:00
2 | 2 | -04:00:00 | 09:00:00 | 05:00:00
3 | 1 | -05:00:00 | 20:54:27.898109 | 15:54:27.898109
4 | 1 | -05:00:00 | 20:54:27.898109 | 15:54:27.898109
5 | 1 | -04:00:00 | 20:54:27.898109 | 16:54:27.898109
6 | 2 | -05:00:00 | 09:00:00 | 04:00:00
Supposing you adopt the round-solution and x=5 (service that will Poll every 5 minutes to query the database), the users of scheduled_time 9AM (server time) will be not the set {id=1, id=2 and id=6} as column scheduled_time shows, but only user id=1 of the correct ref_time.
SELECT * from vw_user_scheduled
WHERE round_minutes(ref_time,5)=round_minutes(now(),5)
The cited round time adapted to time datatype.
CREATE FUNCTION round_minutes(time, integer) RETURNS time AS $$
SELECT
date_trunc('hour', $1)::time
+ (cast(($2::varchar||' min') as interval)
* round(
(date_part('minute',$1)::float + date_part('second',$1)/ 60.)::float
/ $2::float
))
$$ LANGUAGE SQL IMMUTABLE STRICT;
PS: as the question not show your SQL table, there are many ways to model time and time zone formats at tables.

Rails finder method that finds an entity that is just more than 24 hours away?

I'm using Rails 5 and PostgreSQL 9.5. I have a table with the following columns .
cindex=# \d crypto_index_values;
Table "public.crypto_index_values"
Column | Type | Modifiers
------------+-----------------------------+------------------------------------------------------------------
id | integer | not null default nextval('crypto_index_values_id_seq'::regclass)
value | double precision |
index_date | timestamp without time zone | not null
created_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
Given a point in time (say "2017-07-29 11:10") stored in a variable "my_date", how would I write a Rails finder query that returns a single Rails entry that returns the row with the smallest "index_date" that was also at least 24 hours away from "my_date"? So if I had this table data
14 | 133.951211424387 | 2017-07-31 19:10:03.235566 | 2017-07-29 19:10:03.267727 | 2017-07-31 19:10:03.267727
15 | 133.951211424387 | 2017-07-31 19:20:03.59569 | 2017-07-28 19:20:03.629418 | 2017-07-31 19:20:03.629418
16 | 139.104155235946 | 2017-07-31 19:30:08.037045 | 2017-07-31 19:30:08.04715 | 2017-07-31 19:30:08.04715
and given my example, I would expect the query to return the row with id "15" because the row with id "14" is not 24 hours away from my example. Normally this kind of query would work
CryptoIndexValue.where('index_date = ?', my_date - 1)
but since there isn't an entry exactly 24 hours away, it doesn't.
I don't think your example values quite work -- at least I don't see how you would choose that value from the table with that index date, subtracting a day. Do you mean "the row with the earliest date which is also at least 1 day in the future from my_date"?
CryptoIndexValue.
where('index_date > ?', my_date + 1.days).
order(:my_date, :id).
take
I added :id to the order by in case you need a tie-break on two rows that have the same index_date, but remove it if that's impossible due to a constraint.
Maybe you mean to subtract one day instead of adding, though.
You should use > operator combined with order.
CryptoIndexValue.order(:index_date).find_by('index_date > ?', DateTime.now - 24.hours)

Resources