Converting time to seconds then calculations based on this new field - sql

I have a simple table with a number of fields but the only 3 that I'm interested in are below. I want to see if the time of the advert goes past 00:00 (midnight);
ID
start_advert_time
advert_duration
The problem is the above time fields are just text string. So a table would look like this.
ID start_advert_time advert_duration
---- ----------------- ---------------
1234 162438 0060
1235 235900 0080
This means;
ID 1234, time 16:24:38 and duration is 60 seconds
ID 1235, time 23:59:00 and duration is 80 seconds
As you can see the second row will go over the midnight range and this is the rows i want to pull out. I am stump how to do this but think I'm on the right lines
I wanted to convert the string to Time if possible. Then add the seconds to this which would create an "end time". How to do this I'm not sure.
SO 16:24:38 = 59078 seconds + 60 seconds duration = 59138, which in turn is 16:25:38
SO 23:59:00 = 86340 seconds + 80 seconds duration = 86420, which in turn is 00:00:20 <- over midnight and what I need

tl;dr
Assuming start_advert_time always contains exactly 6 characters, this should do what you want:
SELECT *
FROM YourTableNameHere
WHERE DateDiff("s", CDate(0), CDate(Format(start_advert_time, "00\:00\:00"))) + Val(advert_duration) > 86399
Here is an Immediate window session to explain how that works.
' first transform start_advert_time so that CDate() will accept it ...
start_advert_time = "162438"
? Format(start_advert_time, "00\:00\:00")
16:24:38
? CDate(Format(start_advert_time, "00\:00\:00"))
4:24:38 PM
' what is the total of seconds in a day at the last second before midnight?
? DateDiff("s", CDate(0), #23:59:59#)
86399
' how many seconds does start_advert_time represent?
? DateDiff("s", CDate(0), CDate(Format(start_advert_time, "00\:00\:00")))
59078
' and now how many seconds when you add advert_duration?
advert_duration = "0060"
? DateDiff("s", CDate(0), CDate(Format(start_advert_time, "00\:00\:00"))) + Val(advert_duration)
59158
' is that total seconds greater than 86399?
? DateDiff("s", CDate(0), CDate(Format(start_advert_time, "00\:00\:00"))) + Val(advert_duration) > 86399
False
' now do same for second row of sample data ...
start_advert_time = "235900"
advert_duration = "0080"
? DateDiff("s", CDate(0), CDate(Format(start_advert_time, "00\:00\:00"))) + Val(advert_duration) > 86399
True

CONVERTED TO ACCESS :
SELECT ID, start_advert_time, advert_duration
FROM Tabela1
WHERE (((CInt([advert_duration]))>(86400-DateDiff('s',#1/1/1999#,'1999-1-1 '+Mid([start_advert_time],1,2)+':'+Mid([start_advert_time],3,2)+':'+Mid([start_advert_time],5,2)))));
This should work (TSQL):
SELECT [ID]
,[start_advert_time]
,[advert_duration]
FROM Time where CAST(advert_duration as int) > (86400- DATEDIFF(ss, cast('00:00:00.0000000' as time), STUFF(Stuff([start_advert_time],3,0,':'), 6,0,':')))

Related

Hive/SQL Error when converting milliseconds to DDDD:HH:mm:ss

I have an output in milliseconds that is too big to be described in HH:mm:ss format, I will need to expand to DDDD:HH:mm:ss.
The code I'm currently using only works on big numbers:
select from_unixtime(cast(floor(2513702864/1000) as bigint), 'DDDD:HH:mm:ss');
gives: 0030:02:15:02 , this is correct
select from_unixtime(cast(floor(17259/1000) as bigint), 'DDDD:HH:mm:ss');
gives: 0001:00:00:17 , this is not correct.
select from_unixtime(cast(floor(127259/1000) as bigint), 'DDDD:HH:mm:ss');
gives: 0001:00:02:07, this is also not correct.
How to fix the erroneous 1 in DDDD part when dealing with smaller milliseconds?
The logic is simple math. BIGINT timestamp is the number of seconds or milliseconds passed from Unix Epoch (1970-01-01 00:00:00 UTC).
To get milliseconds part use (ts % 1000) - returns reminder after division by 1000
To get total whole seconds passed, use (ts div 1000) - returns integer part, all other figures will be calculated from this number: days, hours, minutes, seconds.
days: (ts div 1000) div 86400 - returns integer part after division of total seconds by number of seconds in a day
To get hours left after whole days calculation: take reminder after days calculation ((ts div 1000) % 86400) and divide by number of seconds in hour, take integer part (((ts div 1000) % 86400) div 3600)
And so on.
Demo:
with your_data as (
select 1 id, bigint(2513702864) ts union all
select 2, bigint(17259) union all
select 3,bigint(127259) union all
select 4,bigint(1272) union all
select 5,bigint(127)
)
select --format output as required. For example days:hours:minutes:seconds.millis
concat(days,':',hours,':',minutes,':',seconds,'.',millis)
from
(
select ((ts div 1000) div 86400) days, --number of whole days
lpad(((ts div 1000) % 86400) div 3600, 2, 0) hours, --whole hours left
lpad((((ts div 1000) % 86400) % 3600) div 60, 2, 0) minutes, --whole minutes left
lpad((((ts div 1000) % 86400) % 3600) % 60, 2, 0) seconds, --seconds left
(ts % 1000) as millis
from your_data
)s
Result:
1 29:02:15:02.864 --29 whole days, 2 hours, 15 minutes, 2 seconds, 864 millis
2 0:00:00:17.259 --17 whole seconds and 259 millis
3 0:00:02:07.259 --two whole minutes, 7 seconds and 259 millis
4 0:00:00:01.272 --one whole second and millis
5 0:00:00:00.127 --we have only milliseconds
Now you can see the difference between this calculation and what from_unixtime returns.
For record id=1 the number of whole days is 29. Why from_unixtime returns 30 (for pattern 'D')? Because 29 whole days passed and we are 2 hrs 15 min 2 sec 864 mi in a new day 30. In other words, from_unixtime returns timestamp formatted and calculation in my query returns interval formatted, "day in a year" and "whole days passed from" are different things.
Hope, now it is as clear as a day.
See also similar question: https://stackoverflow.com/a/57497316/2700344
And if you need to convert bigint timestamp in milliseconds to string with milliseconds preserved (yyyy-MM-dd HH:mm:ss.SSS) use this:
select concat(from_unixtime(ts div 1000), '.', (ts % 1000)) as timestamp_with_millis
from (select bigint(2513702864) as ts) s
Result:
1970-01-30 02:15:02.864

Any better way to convert timestamp (HH:mm:ss) to Seconds in Hive

I have one hive field of type string which has timestamp in following format:
HH:mm:ss
mm:ss
ss
I need to convert them as below:
Input:
10:30:40
30:40
40
Output Expected:
10:30:40 = (10*3600) + (30 * 60) + 40 = 37,840
30:40 = (30 * 60) + 40 = 1840
40 = 40 = 40
I tried doing something like this
case
when duration like '%:%:%' then
split(duration, ':')[0] * 3600 +
split(duration, ':')[1] * 60 +
split(duration, ':')[2]
when duration like '%:%' then
split(duration, ':')[0] * 60 +
split(duration, ':')[1]
else
duration
end
This works but seems inefficient way. is there a better way to do the same when I have to process billions of records.
Your expressions will not create much additional load when executed in hive. You can simplify query a bit using unix_timestamp function, but it will run not faster.
with input as(--use your table instead of this
select stack(3, '10:30:40',
'30:40',
'40') as duration
)
select duration, case when duration like '%:%:%' then unix_timestamp(duration,'HH:mm:ss')
when duration like '%:%' then unix_timestamp(duration,'mm:ss')
else duration
end as result
from input
Result:
duration result
10:30:40 37840
30:40 1840
40 40
Or even simpler:
select duration, coalesce(unix_timestamp(duration,'HH:mm:ss'), unix_timestamp(duration,'mm:ss'), duration) as result
returns exactly the same.

How to format seconds as integer into HH:MM:SS in Presto

I have a table as:
id time_seconds
5 140
6 5
7 15000
I want to get it as:
id time_format
5 23:52
6 00:05
7 04:10:00
Basically format of HH:MM:SS
Now the thing is that I don't have many records with hours so the HH: should be in the output only if there are hours. Otherwise it should be just MM:SS
Presto has function that does similar thing
SELECT parse_duration('60s');
But the output isn't what I need
0 00:01:00.000
and I'm not sure this is the way to use it?
This is what I did so far:
select id, concat(cast(time_seconds as varchar(10)),'s')
from mytable
not sure how to continue
You can readily convert this to a time:
select time '00:00:00' + time_seconds * interval '1' second
I think that that is the best way to represent the value. Adding the hour conditionally seems like a bad idea -- it can lead to confusion.
I think the following will work on times:
select (case when time_seconds < 60 * 60
then date_format(time '00:00:00' + time_seconds * interval '1' second, '%i:%s')
else date_format(time '00:00:00' + time_seconds * interval '1' second, '%H:%i:%s')
end) as hhmmss
This is not a particularly elegant approach, but it'll produce the targeted output format.
Without dropping the potential '00:' hours portion:
select regexp_replace(concat(
cast(round(time_seconds/3600) as varchar),':',
cast(round((time_seconds%3600)/60) as varchar),':',
cast(round((time_seconds%3600)%60) as varchar)
),'(\d+)(:|\b)', match -> if(length(match)=1,'0'||match[1],match[1]) || match[2])
If '00:' hours must be dropped, then one could wrap the above in an additional: regexp_replace(<THE_INNER_RESULT>,'(^00:)(.*)','$2').
Note: This also doesn't satisfy the possible case of time_seconds>86400 (handling days) which #hkravitz points out in their comment.

TeradataSQL: How to Standardize Times that are in Both 24 and 12 Hour Format

I'm using a database which is a complete mess and need to figure out the best way to fix it. Right now I have times that are coming into this table that are in 12 hour and 24 hour format (some are 01:30 PM and some are 13:30 PM). To properly compare the scheduled times to the actual times I need them in a standardized 24 hour format.
Example Table Below:
Note I can not do any permanent changes as I don't have those privileges with this data.
Sched St Date Sched Time Actual Start Actual End
09/11/2017 00:00:00 11:30 AM 09/11/2017 11:34:16 09/11/2017 11:58:00
05/26/2017 00:00:00 03:30 PM 05/26/2017 15:40:00 05/26/2017 15:55:15
11/06/2017 00:00:00 19:30 PM 11/06/2017 21:25:00 11/06/2017 21:45:00
Right now I have:
Select DB.TBL.*,
case when left (Sched_Time, 2) > 12
and Sched_Time like '%PM%'
then concat((left(Sched_Time, 2) - 12),right(Sched_Time, 6))
Else concat(Left(Sched_Time,2)right(Sched_Time, 6))
End;
I'm getting an error when I try to concat to get the full time (i.e. 07:00 PM). Can anyone help me out with a clean way to do this?
I suggest you convert Sched_Time from VARCHAR to TIME. Then you can normalize Sched_Time to 24-hour format and combine it with Sched_St_Date to get the TIMESTAMP field that you want.
Clean up, extract and convert the numeric time portion to a TIME field:
CAST(
SUBSTRING(Sched_Time FROM 0 FOR POSITION(' ' IN Sched_Time)) AS TIME
) AS NumericTime
This assumes that all fields have a " " separator. You may get errors with the CAST depending on how bad your data is. Just keep adding logic to handle the different conditions.
Determine if the value is AM or PM:
CASE
WHEN POSITION('AM' IN Sched_Time) > 0 THEN 'AM'
WHEN POSITION('PM' IN Sched_Time) > 0 THEN 'PM'
ELSE 'PM' -- Assume 'PM' if not specified (can change as needed)
END AS TimeOfDay
Use the pieces above to normalize NumericTime to 24-hour format:
CASE
WHEN CAST(SUBSTRING(TRIM(Sched_Time) FROM 0 FOR 2) AS INTEGER) > 12 THEN NumericTime -- Already in 24-hour format
ELSE CASE
WHEN TimeOfDay = 'AM' THEN NumericTime -- Already suitable for 24-hour format
ELSE NumericTime + INTERVAL '12' HOUR -- Convert 'PM' time to 24-hour format
END
END AS MyFinalTime
Follow dnoeth's advice to combine the MyFinalTime field with the Sched_Dt field:
https://stackoverflow.com/a/47526411/8772888
CAST(Sched_St_Date AS DATE) + CAST(MyFinalTime AS INTERVAL HOUR TO
MINUTE) AS Sched_St_Timestamp
Put it all together:
SELECT CAST(
SUBSTRING(Sched_Time FROM 0 FOR POSITION(' ' IN Sched_Time)) AS TIME
) AS NumericTime,
CASE
WHEN POSITION('AM' IN Sched_Time) > 0 THEN 'AM'
WHEN POSITION('PM' IN Sched_Time) > 0 THEN 'PM'
ELSE 'PM' -- Assume 'PM' if not specified (can change as needed)
END AS TimeOfDay,
CASE
WHEN CAST(SUBSTRING(TRIM(Sched_Time) FROM 0 FOR 2) AS INTEGER) > 12 THEN NumericTime -- Already in 24-hour format
ELSE CASE
WHEN TimeOfDay = 'AM' THEN NumericTime -- Already suitable for 24-hour format
ELSE NumericTime + INTERVAL '12' HOUR -- Convert 'PM' time to 24-hour format
END
END AS MyFinalTime,
CAST(Sched_St_Date AS DATE) + CAST(MyFinalTime AS INTERVAL HOUR TO MINUTE) AS Sched_St_Timestamp
FROM DB.TBL
I don't have access to a TD system to check, so there may be some syntax errors. You probably will need to modify the logic to fit your actual data. Let me know how it turns out.

T-SQL Format seconds as HH:MM:SS time

Is there any tricky way to format seconds like hours:minutes:seconds. For example,
3660
seconds will be displayed as
01h 01m 00s
or
01:01:00
I am aware of the standard way of doing this:
Divide all seconds on 3600 to get the hours
Divide the rest seconds on 60 to get the minutes
The rest are the seconds
I met the following issues:
I am not able to create separate function that do this.
My code is in view using several CTEs. So, variables can be declare
using the CTEs only.
I am not able to use the standard solution because I will have
results bigger then one day - How to convert Seconds to HH:MM:SS using T-SQL
SELECT Seconds,
RIGHT('00'+CONVERT(VARCHAR(10),Seconds/3600),2)
+':'
+ RIGHT('00'+CONVERT(VARCHAR(2),(Seconds%3600)/60),2)
+':'
+ RIGHT('00'+CONVERT(VARCHAR(2),Seconds%60),2) AS [HH:MM:SS]
FROM table1
Result:
Seconds
HH:MM:SS
3660
01:01:00
3800
01:03:20
4200
01:10:00
600
00:10:00
60
00:01:00
86400
24:00:00
86800
24:06:40
See this SQLFiddle
Update
The above query works fine if the total number of hours are less than 100 i.e. (99:59:59). If you need more than that you can use the following query:
SELECT
Seconds,
CASE Seconds/3600
WHEN 0 THEN RIGHT('00'+CONVERT(VARCHAR(10),Seconds/3600),2)
ELSE CONVERT(VARCHAR(10),Seconds/3600) END
+':'
+ RIGHT('00'+CONVERT(VARCHAR(2),(Seconds%3600)/60),2)
+':'
+ RIGHT('00'+CONVERT(VARCHAR(2),Seconds%60),2) AS [HH:MM:SS]
FROM table1
Result:
Seconds
HH:MM:SS
3660
1:01:00
3800
1:03:20
4200
1:10:00
600
00:10:00
60
00:01:00
9999930
2777:45:30
359999
99:59:59
360000
100:00:00
86800
24:06:40
See this SQLFiddle
Try this:
SELECT CONVERT(TIME(0), DATEADD(SS,***seconds***,0),108) as 'FieldName'
will return HH:MM:SS
Example:
46800 seconds = 1:00 PM
FieldName = 13:00:00
Hope this helps.
SELECT cast(seconds/3600 as varchar(6)) +
right(cast(dateadd(second, seconds,0) as time(0)), 6)
FROM <yourtable>