Convert seconds to human readable time duration - mysql

How would I best convert 90060 (seconds) to a string of "25h 1m"?
Currently I'm doing this in SQL:
SELECT
IF(
HOUR(
sec_to_time(
sum(time_to_sec(task_records.time_spent))
)
) > 0,
CONCAT(
HOUR(sec_to_time(sum(time_to_sec(task_records.time_spent)))),
'h ',
MINUTE(sec_to_time(sum(time_to_sec(task_records.time_spent)))),
'm'
),
CONCAT(
MINUTE(sec_to_time(sum(time_to_sec(task_records.time_spent)))),
'm'
)
) as time
FROM myTable;
But I'm not sure it's the most convenient method :-)
I'm open to suggestions on doing this both in SQL (differently than I'm already doing) or in PHP.
EDIT:
Examples of desired strings: "5m", "40m", "1h 35m", "45h" "46h 12m".

TIME_FORMAT(SEC_TO_TIME(task_records.time_spent),'%Hh %im')
Documentation is your friend:
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html
According to comment:
DROP FUNCTION IF EXISTS GET_HOUR_MINUTES;
CREATE FUNCTION GET_HOUR_MINUTES(seconds INT)
RETURNS VARCHAR(16)
BEGIN
DECLARE result VARCHAR(16);
IF seconds >= 3600 THEN SET result = TIME_FORMAT(SEC_TO_TIME(seconds),'%kh %lm');
ELSE SET result = TIME_FORMAT(SEC_TO_TIME(seconds),'%lm');
RETURN result;
END
DELIMETER ;
Usage:
SELECT GET_HOUR_MINUTES(task_records.time_spent) FROM table

you can use predefined function sec_to_time()
sec_to_time(number_of_seconds)
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_sec-to-time

Try out this trick (on MySQL)
select date_format("1970-01-01 00:00:00" + interval <value_here> second, "%H:%i:%S")
SEC_TO_TIME sometime produces wrong values

try this: (input your pure seconds time)
var hours = Math.floor(input/3600);
var minutes = Math.floor((input-hours*3600)/60);
var seconds = input-(minutes*60)-(hours*3600);
function convertTime(){
return hours":"minutes":"seconds;
}

Related

Validating age using a function in MySQL

so i'm having some issues using a function in SQL where i calculate the age given a certain date. The thing is that i need to validate with the current date and the date of birth if it's already the year or not.
For example the date i have in a register is 1994-11-15 and when consulting the information with
select EmployeeID Num_Emloyee, concat(FirstName, " . ", LastName) Name_Employee, Title Puesto, fn_Age(BirthDate) Edad, fn_Age(HireDate) WorkYears
from employees;
It returns 24, however if i only consult with select the function it returns 23, the correct answer.
At the moment this is the function i'm using to validate the age is this:
create function fn_Age(dateVal date)
returns int
begin
declare age int;
if day(now()) and month(now()) >= day(dateVal) and month(dateVal) then
set age=year(now())-year(dateVal);
else
set age=(year(now())-year(dateVal)) - 1;
end if;
return age;
end
Is there anything i'm not considering in the function?
day(now()) and month(now()) >= day(dateVal) and month(dateVal)
This logic doesn't make sense. I don't know if an if supports tuples in MySQL. If so, you can do:
(month(now()), date(now())) >= ( month(dateval), day(dateval) )
(this works in a MySQL WHERE clause.)
You can also do:
month(now()) * 100 date(now()) >= month(dateval) * 100 + day(dateval)
You can also use timestampdiff-function
select timestampdiff(year, '1994-11-15', now());

Error in plpgsql function: array value must start with "{" or dimension information

I'm trying to format the results of this query:
CREATE OR REPLACE FUNCTION "alarmEventList"(sampleid integer
, starttime timestamp without time zone
, stoptime timestamp without time zone)
RETURNS text[] AS
$BODY$DECLARE
result text[];
BEGIN
select into result array_agg(res::text)
from (
select to_char("Timestamp", 'YYYY-MM-DD HH24:MI:SS')
,"AlertLevel"
,"Timestamp" - lag("Timestamp") over (order by "Timestamp")
from "Judgements"
WHERE "SampleID" = sampleid
and "Timestamp" >= starttime
and "Timestamp" <= stoptime
) res
where "AlertLevel" > 0;
select into result array_to_string(result,',');
return result;
END
$BODY$
LANGUAGE plpgsql VOLATILE
Right now without array_to_string() I get something like this:
{"(\"2013-10-16 15:10:40\",1,00:00:00)","(\"2013-10-16 15:11:52\",1,00:00:48)"}
and I want something like this:
2013-10-16 15:10:40,1,00:00:00 | 2013-10-16 15:11:52,1,00:00:48 |
But when I run the query I get error:
array value must start with "{" or dimension information
You do not actually want an array type, but a string representation.
Can be achieved like this:
CREATE OR REPLACE FUNCTION "alarmEventList"(sampleid integer
, starttime timestamp
, stoptime timestamp
, OUT result text) AS
$func$
BEGIN
SELECT INTO result string_agg(concat_ws(','
,to_char("Timestamp", 'YYYY-MM-DD HH24:MI:SS')
,"AlertLevel"
,"Timestamp" - ts_lag)
, ' | ')
FROM (
SELECT "Timestamp"
,"AlertLevel"
,lag("Timestamp") OVER (ORDER BY "Timestamp") AS ts_lag
FROM "Judgements"
WHERE "SampleID" = sampleid
AND "Timestamp" >= starttime
AND "Timestamp" <= stoptime
) res
WHERE "AlertLevel" > 0;
END
$func$ LANGUAGE plpgsql
The manual on string_agg() and concat_ws().

summing time column in mysql

I have a column in one of my tables, which is TIME format (00:00:00). I am trying to sum the entire column and display it as same (00:00:00).
I have tried using the following but it is not giving me anywhere near the correct answer.It's giving me 22.12:44:00 and manual calcaulation tells me it should be close to 212:something:something
SELECT SEC_TO_TIME( SUM( TIME_TO_SEC( vluchttijd ) ) ) AS totaltime FROM tbl_vluchtgegevens
Any recommendations?
You can try like this:-
SELECT SEC_TO_TIME(SUM(SECOND(vluchttijd ))) AS totaltime FROM tbl_vluchtgegevens;
or try this(althoug this is not a good approach):
SELECT concat(floor(SUM( TIME_TO_SEC( `vluchttijd ` ))/3600),":",floor(SUM( TIME_TO_SEC( `vluchttijd ` ))/60)%60,":",SUM( TIME_TO_SEC( `vluchttijd ` ))%60) AS total_time
FROM tbl_vluchtgegevens;
Edit:-
Try this:-
select cast(sum(datediff(second,0,dt))/3600 as varchar(12)) + ':' +
right('0' + cast(sum(datediff(second,0,dt))/60%60 as varchar(2)),2) +
':' + right('0' + cast(sum(datediff(second,0,dt))%60 as varchar(2)),2)
from TestTable
Working SQL Fidlle
In MySQL, the TIME type is rather limited in range. Moreover many time function do not accept values greater that 23:59:59, making it really usable only to represent the time of the day.
Given your needs, your best bet is probably to write a custom function that will mimic SEC_TO_TIME but allowing much greater range:
CREATE FUNCTION SEC_TO_BIGTIME(sec INT)
RETURNS CHAR(10) DETERMINISTIC
BEGIN
SET #h = sec DIV 3600;
SET #m = sec DIV 60 MOD 60;
SET #s = sec MOD 60;
RETURN CONCAT(
LPAD(#h, 4, '0'),
':',
LPAD(#m, 2, '0'),
':',
LPAD(#s, 2, '0')
);
END;
And here is how to use it:
create table tbl (dt time);
insert tbl values
('09:00:00'), ('01:00:00'), ('07:50:15'), ('12:00:00'),
('08:30:00'), ('00:45:00'), ('12:10:30');
select SEC_TO_BIGTIME(sum(time_to_sec(dt))) from tbl;
Producing:
+--------------------------------------+
| SEC_TO_BIGTIME(SUM(TIME_TO_SEC(DT))) |
+--------------------------------------+
| 0051:15:45 |
+--------------------------------------+
See http://sqlfiddle.com/#!8/aaab8/1
Please note the result is a CHAR(10) in order to overcome TIMEtype limitations. Depending how you plan to use that result, that means that you may have to convert from that string to the appropriate type in your host language.
This worked for me:
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(vluchttijd))) AS totaltime FROM tbl_vluchtgegevens;

Hundred year date conversion in ssis derived column

I'm trying to convert a Hundred Year Date (HYD) format to a regular date format through SSIS derived column transform. For example: Convert 41429
to 06/04/2013. I can do it with formatinng code within a script (and maybe I simply have to go this route) but feel there has to be a way to do so within a derived column that I'm just not getting. Any help is appreciated.
This is what I came up with. Are you sure your conversion is correct? My answer is 1 day. off.
DECLARE #t1 as date = '01/01/1900';
DECLARE #t2 as DATE = '12/31/1900';
DECLARE #hyd as INT;
-- This example shows that we need to add 1
SELECT #hyd = DATEDIFF (d, #t1, #t2) + 1 -- 364 + 1
SELECT #hyd
set #t2 = '06/04/2013';
SELECT #hyd = DATEDIFF (d,#t1, '06/04/2013') + 1-- 41427
SELECT #hyd
SELECT DATEADD (d, #hyd, '01-JAN-1900')
SELECT DATEADD (d, 41429, '01-JAN-1900')
A hundred year date is a calculation based on the number of days since 1899-12-31. It's an "Excel Thing". It also has a bug in it that you must account for.
The equivalent TSQL logic would be
DECLARE
#HYD int = 41429;
SELECT
#HYD =
CASE
WHEN #HYD > 60
THEN #HYD -1
ELSE
#HYD
END;
SELECT
DATEADD(d, #HYD, '1899-12-31') AS HYD;
Armed with that formula, I can write the following Expression in a Derived Column Transformation (assuming you have a column named HYD)
(HYD > 60) ? DATEADD("d",HYD - 1,(DT_DATE)"1899-12-31") : DATEADD("d",HYD,(DT_DATE)"1899-12-31")
And the results
--or inline SQL...using this
SELECT
case when ([HYD] > 60) then
DATEADD(day,[HYD] - 1,'1899-12-31')
else
DATEADD(day,[HYD],'1899-12-31')
end 'HYD_conv'
FROM
TableName
--and in the where clause if you like...
WHERE
(case when ([HYD] > 60) then DATEADD(day,[HYD] - 1,'1899-12-31') else DATEADD(day,[HYD],'1899-12-31') end) = '2016-01-14'

Surpassing MySQL's TIME value limit of 838:59:59

The title might be a bit confusing so allow me to explain. I'm using a table to record my work logs. Every day I'll create an entry stating from what time to what time I have worked and I'll add a comment describing what I did.
I then use a query to compare the timestamps to figure out exactly how many hours and minutes I have worked that day. Additionally, I use a query to calculate the sum of hours and minutes I have worked the entire year. That's where I'm running into a problem. My query is as follows.
SELECT TIME_FORMAT(SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(entry_end_time, entry_start_time)))), '%H:%i')
AS total FROM entry
WHERE entry_date BETWEEN '2012-01-01' AND '2012-12-31' AND user_id = 3
By default, MySQL TIME fields allow a time range of '-838:59:59' to '838:59:59'. I have currently logged more than 900 hours of work this year though, and I want the result of my query to reflect this. Instead, the result is 838:59:59, which makes sense because that is the limit.
Is there any way around this so the result of the query can go beyond 839 hours, or would I have to use something like PHP to go over the entire table and add it all up? I kind of want to avoid that if possible.
I'd just retrieve the total number of seconds worked, and convert to hours/minutes as required in the presentation layer of my application (it is, after all, a simple case of division by 60):
<?
$dbh = new PDO("mysql:dbname=$dbname", $username, $password);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$qry = $dbh->prepare('
SELECT SUM(TIME_TO_SEC(entry_end_time)-TIME_TO_SEC(entry_start_time))
FROM entry
WHERE entry_date BETWEEN :start_date AND :end_date
AND user_id = :user_id
');
$qry->execute([
':start_date' => '2012-01-01',
':end_date' => '2012-12-31',
':user_id' => 3
]);
list ($totalMins, $remngSecs) = gmp_div_qr($qry->fetchColumn(), 60);
list ($totalHour, $remngMins) = gmp_div_qr($totalMins, 60);
echo "Worked a total of $totalHour:$remngMins:$remngSecs.";
?>
Have a look at timestampdiff which doesn't have the TIME limitation.
I.e. something like (untested):
SELECT CONCAT(
TIMESTAMPDIFF(HOURS, entry_end_time, entry_start_time),
":",
MOD(TIMESTAMPDIFF(MINUTES, entry_end_time, entry_start_time),60)
)
AS total FROM entry
WHERE entry_date BETWEEN '2012-01-01' AND '2012-12-31' AND user_id = 3
The concats not ideal, I'm sure there will be a more elegant solution.
Some simple math can do the trick,I hardcoded a random number of seconds(10000000)
SELECT CONCAT(FLOOR(10000000/3600),':',FLOOR((10000000%3600)/60),':',(10000000%3600)%60)
Fiddle
2777:46:40
Counting the days separately is enough.
Here's the concatenation I used.
I illustrated with a fully copy/pastable example to ease the understanding of the limit we hit (TIME format's max value)
A free unicorn is bundled to simplify comma management
SELECT 'pq7~' AS unicorn
#######################
## Expected result ##
#######################
## Total, formatted as days:hh:mm:ss ##
,CONCAT(
FLOOR(TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24') / 86400)
, ':'
, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24') % 86400)
) AS Real_expected_result
#########################
## Calculation details ##
#########################
## Extracted days from diff ##
,FLOOR(TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24') / 86400) AS Real_days
## Extracted Hours/minutes/seconds from diff ##
,SEC_TO_TIME(TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24') % 86400) AS Real_hours_minutes_seconds
###################################
## Demo of wrong values returned ##
###################################
,TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24') AS Real_seconds_diff
## WRONG value returned. 5.64M is truncated to 3.02 ! ##
,TIME_TO_SEC(SEC_TO_TIME(5644119)) AS WRONG_result
## Result is effectively limited to 838h59m59s ##
,SEC_TO_TIME(TIMESTAMPDIFF(SECOND, '2017-01-01 09:17:45', '2017-03-07 17:06:24')) AS Limit_hit
## Lights on said limit ##
,SEC_TO_TIME( 3020398) AS Limit_value_check1
,SEC_TO_TIME( 3020400) AS Limit_value_check2
Take a look at these functions. Credits:( https://gist.github.com/NaWer/8333736 )
DROP FUNCTION IF EXISTS BIG_SEC_TO_TIME;
DELIMITER $$
CREATE FUNCTION BIG_SEC_TO_TIME(SECS BIGINT)
RETURNS TEXT
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE HEURES TEXT;
DECLARE MINUTES CHAR(5);
DECLARE SECONDES CHAR(5);
IF (SECS IS NULL) THEN RETURN NULL; END IF;
SET HEURES = FLOOR(SECS / 3600);
SET MINUTES = FLOOR((SECS - (HEURES*3600)) / 60);
SET SECONDES = MOD(SECS, 60);
IF MINUTES < 10 THEN SET MINUTES = CONCAT( "0", MINUTES); END IF;
IF SECONDES < 10 THEN SET SECONDES = CONCAT( "0", SECONDES); END IF;
RETURN CONCAT(HEURES, ":", MINUTES, ":", SECONDES);
END;
$$
DELIMITER ;
DROP FUNCTION IF EXISTS BIG_TIME_TO_SEC;
DELIMITER $$
CREATE FUNCTION BIG_TIME_TO_SEC(TIME TEXT)
RETURNS BIGINT
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE HEURES TEXT;
DECLARE MINUTES CHAR(5);
DECLARE SECONDES CHAR(5);
IF (TIME IS NULL) THEN RETURN NULL; END IF;
SET HEURES = SUBSTRING_INDEX(TIME, ":", 1);
SET MINUTES = SUBSTRING(TIME FROM -5 FOR 2);
SET SECONDES = SUBSTRING(TIME FROM -2 FOR 2);
RETURN CAST(HEURES AS UNSIGNED INTEGER)*3600 + CAST(MINUTES AS UNSIGNED INTEGER)*60 + CAST(SECONDES AS UNSIGNED INTEGER);
END;
$$
DELIMITER ;
select concat(truncate(sum(time_to_sec(TIMEDIFF(hora_fim, hora_ini)))/3600,0), ':',
TIME_FORMAT(sec_to_time(sum(time_to_sec(TIMEDIFF(hora_fim, hora_ini))) - truncate(sum(time_to_sec(TIMEDIFF(hora_fim, hora_ini)))/3600,0)*3600), '%i:%s'))
as hms from tb_XXXXXX
First calculating the days difference then multiply it with 24*60*60 to convert it into seconds then add to it time_to_sec value result
DATEDIFF(start_date,end_date)*24*60*60 + TIME_TO_SEC(TIMEDIFF(TIME(start_date),TIME(end_date)))
AS sec_diff
For more details check codebucket- Surpassing time_to_sec() function maximum limit