How to get the format in hours when it exceeds 24?

Asked

Viewed 2,074 times

14

I’m developing a PHP system where I need to get the total time of an audio file at some point. This time is saved in the database in seconds and also in the format of hours.

The problem is that when you format these hours through the seconds saved in the bank, it works normally, but only if the file has the time less than 24 hours.

Example:

$date = new DateTime('@0'); // "zeramos" a data

$tempo_1 =  10 * 3600; // 10 horas

$tempo_2 = 18 * 3600; // 18 horas

$tempo_3 = 28 * 3600; // 28 horas


echo $date->setTime(0, 0, $tempo_1)->format('H:i:s'); // 10:00:00

echo $date->setTime(0, 0, $tempo_2)->format('H:i:s'); // 18:00:00

echo $date->setTime(0, 0, $tempo_3)->format('H:i:s'); // 04:00:00

In the last example, the desired result was 28:00:00. However, since it is a class that works with dates, it is returned 04:00:00, on behalf of 24 equal to 1 day.

How could I get that time formatted in hours, even if this exceeds 24 hours?

  • $tempo_1 is UNIX time?

  • 1

    I may not have understood or I may be talking nonsense but what you want doesn’t seem possible. The guy DateTime expects a valid time (I don’t know, PHP may have subverted this but in decent languages this is how q works), so 28:00:00 is not a possible time in this type.

  • 1

    I agree with you @bigown. Class is made to work with Dates, not with time itself. Is there any way (or a gambiarra) that can be done to solve my problem? I’m using a function with a sprintf, but I feel uncomfortable with her (besides, she’s "stealing" 1 second of the audio file)

  • @Guilhermenascimento, I made a small adjustment in the instance of DateTime. $tempo_1 in fact it would be the duration in seconds of the audio. In case, I want that if it has 100800 (28 hours), I convert it to the format 28:00:00. However, it displays 04:00:00, for he creates 1 dia instead of 24 horas existing within the 28 horas;

  • @Guilhermenascimento, my value isn’t exactly in unix time. But the class works with unix time (I hope I didn’t say anything stupid)

  • because date? if this is only about time wouldn’t it be better to work only with Time? Using only the team you wouldn’t have this problem of dates

Show 1 more comment

4 answers

12

The solutions presented above are very good.

However, even if it seems stupid, I discovered another solution to this problem using the function SEC_TO_TIME of Mysql.

Behold:

SELECT SEC_TO_TIME( 28 * 3600 )
--28:00:00

So, for the record, if anyone has a problem like this!

It seems at this point, the Mysql simplified things much more than PHP itself.

Updating

I’ve developed a library so we can work with the times more precisely. And I used some of the solutions applied here in the source code.

Care:

https://github.com/wallacemaxters/timer

  • 2

    Ours does not seem stupid to "discovery", it seems to me that you have simplified the lives of many people. Great answer! Just a detail I added the mysql tag, because otherwise the answer would not make much sense.

  • Thank you @Guilhermenascimento. The idea came to me after I had already asked the question and also given the answer. Thank you for the correction.

  • I noticed that in your repository you used the floor, then I believe you managed to find the problem of -1 second.

  • @Guilhermenascimento, I found out why. The seconds are saved as float!. That is to say, 24.4 One hour you’ll be able to quit!

  • So in my view the sum would have to be with the unixtime, I just think

  • I may have to use the ceil in my data float, and not within the scope of the?

  • Good morning, I got @Wallacemaxters , this problem is out of function, but it’s coming float from the database?

  • It’s out of function. Your code is right :)

  • Yeah, cool, just wanted to know how you got to the float (out of function).

Show 4 more comments

12

Note: In case the author solved using Mysql, then this answer may to chance data coming from another "source" where it does not have a resource similar to SEC_TO_TIME.

If the time format is in seconds you can calculate using PHP without the need for "advanced" classes for this, as the example of this reply in Soen

I just made an adjustment so that broken numbers are supported, using the function fmod()

The code went like this:

<?php
function getFullHour($seconds) {
    $negative = $seconds < 0; //Verifica se é um valor negativo

    if ($negative) {
        $seconds = -$seconds; //Converte o negativo para positivo para poder fazer os calculos
    }

    $hours = $seconds / 3600;

    $mins = ($seconds - ($hours * 3600)) / 60;

    //Pega o valor após o ponto flutuante
    $f = fmod($hours, 1);

    //Adiciona minutos se $seconds for quebrado
    if ($f > 0) $mins += 60 * $f;

    $secs = $seconds % 60;

    $sign = $negative ? '-' : ''; //Adiciona o sinal de negativo se necessário

    return $sign . sprintf('%02d:%02d:%02d', $hours, $mins, $secs);
}

echo getFullHour(3.002 * 60 * 60), PHP_EOL; //03:00:07
echo getFullHour(3.5 * 60 * 60), PHP_EOL; //03:30:00

echo getFullHour(3 * 60 * 60), PHP_EOL; //03:00:00

echo getFullHour(140801), PHP_EOL; //39:06:41
echo getFullHour(100800), PHP_EOL; //28:00:00

echo getFullHour(-140801), PHP_EOL; //-39:06:41
echo getFullHour(-100800), PHP_EOL; //-28:00:00

Online test on repl it.

  • What the str_pad does exactly in this concatenation? And another thing: why is it used floor, and not ceil? I had problems with "rounding down", because the audio was 1 second less.

  • 1

    Thank you very much for your reply, @Guilhermenascimento. I understood that you used str_pad to fill the numbers with zero, but there is no need to use them. The function sprintf does that. And I think it’s a good idea to return false if the argument value is not as expected. See Ideone the little refactoring I did. http://ideone.com/9bMIT4

  • Thanks for the tip from sprintf (I always forget and go for the hardest), in the question of returning false I preferred to use NULL. The floor returned -1 seconds? Could you provide an example?

  • Actually, @Guilhermenascimento, it’s like this: I use the library GETID3 to get audio time. Then, I save the time in seconds and the formatted time, which it generates. Then, when I have to add up these times, I have to use the seconds to add up the audio times and format. So if I have two audios with 3:00:02 and 3:00:00, instead of returning 6:00:2, he returns 6:00:1. It makes no difference to me, but it does to my client!

11


I don’t recommend it, but you can do a little snitching:

function dtLength($sec) {
    $t=new DateTime("@".$sec);
    $r=new DateTime("@0");
    $i=$t->diff($r);
    $h=intval($i->format("%a"))*24+intval($i->format("%H"));
    return $h.":".$i->format("%I:%S");
}

$date = new DateTime;
$tempo_1 =  10 * 3600; // 10 horas
$tempo_2 = 18 * 3600; // 18 horas
$tempo_3 = 28 * 3600; // 28 horas
echo $date->setTime(0, 0, $tempo_1)->format('H:i:s'); // 10:00:00
echo $date->setTime(0, 0, $tempo_2)->format('H:i:s'); // 18:00:00
echo $date->setTime(0, 0, $tempo_3)->format('H:i:s'); // 04:00:00
echo dtLength($tempo_1);
echo dtLength($tempo_2);
echo dtLength($tempo_3);

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Function withdrawn of that response in the OS.

Another possibility is to work with DateInterval. This type shows times, which is what you seem to be wanting to use, rather than hours. It’s probably more appropriate for what you want.

  • My beloved father! I didn’t want to have to read to give with this kind of thing. The project is in Laravel, and it’s so cute!

  • 8

    Wow, I don’t understand you both! The problem is using a class of dates to represent something that is not a date. Why the drama, why not "recommend" and why "my beloved father"?! If you’re not dealing with dates, don’t use the date class, or turn around with a scam! It’s simple, not everything can be always cute :)

  • 1

    I think I’ll take a look at DateInterval, You know, @bfavaretto?!

  • 1

    @Wallacemaxters Good idea, well capable that works!

  • Anything, I give a sprintf in the code, where it is concatenated, to feel at least a little bit of "sense of organization". ;) ;) ;)

  • @Wallacemaxters If you find another solution other than the gambiarra above, post later as answer.

  • I don’t know if I’ll find it. It’s the best I’ve found so far. Unless we make a class just for this and put it on Github :)

  • 2

    @Wallacemaxters probably this class already exists and it is called DateInterval.

  • 4

    Datetime is something that the more you take out of PHP code, the cleaner it gets.

Show 4 more comments

3

First we need to understand two important concepts. Consider the two sentences below:

  • the film will be shown at two o'clock in the morning
  • the film is two hours long

In the first case, "two hours" refers to timetable (a specific moment of the day). It’s okay that he needs "morning" not to be ambiguous (it could be 2 in the afternoon), but the fact is that he is referring to a specific moment.

In the second case, "two hours" refers to duration (an amount of time). It is not said what time the movie starts, nor will it pass or already has passed. It’s just the amount of time, unrelated to schedules.

These two concepts are not the same thing. Dates and times refer to points in the timeline: a date (day, month and year) refers to a specific point in a calendar, and a time (time, minute, second, fractions of a second) refers to a specific time of a day. Durations are just amounts of time, which exist by themselves, without being tied to a specific date and time.

What confuses is the fact that both (both times and durations) use exactly the same words (days, hours, minutes, etc.), and are often written in the same way (a clock shows "02:00:00" when it is two in the morning, a stopwatch shows "02:00:00" when the time count reaches two hours).

Another point that can - erroneously - make us think that times and durations are the same thing is that, although they are different concepts, they may be related. If I calculate the difference between two dates, the result is a duration (knowing the date/time that started and ended, I can calculate how long it lasted), and if I add a date/time with a duration, the result is another date/time (knowing the date/time that started and how long it lasted, I can calculate the date/time that ended).


That said, a DateTime to work with dates and times (points in the timeline), but not with durations.

To work with durations, you can use a DateInterval. With it you can create an instance that corresponds to a certain amount of time, and format it through the method format. Ex:

$tempo_1 = new DateInterval('PT10H'); // duração de 10 horas
$tempo_2 = new DateInterval('PT18H'); // duração de 18 horas
$tempo_3 = new DateInterval('PT28H'); // duração de 28 horas

$formato = '%H:%I:%S';
echo $tempo_1->format($formato); // 10:00:00
echo $tempo_2->format($formato); // 18:00:00
echo $tempo_3->format($formato); // 28:00:00

Note that the format used is different from the one used in method format of a DateTime. To a DateInterval, besides the letters need to have the % before, some are different (for example, to format the DateTime, use the i minuscule for the minutes and the s tiny for seconds, but for a DateInterval one should use I and S uppercase so they have zero left for values less than 10).

Another detail is that the builder of DateInterval receives a duration in ISO 8601 format. Basically, the P at the beginning is mandatory (comes from the English "Period", another name for "amount of time"). The T indicates that the following fields refer to hours, minutes and seconds (as before the T we can have fields for days, months and years - for example, P2Y3MT10H5M is a duration of 2 years, 3 months, 10 hours and 5 minutes).


In case you only have the total amount of seconds, it wouldn’t do any good new DateInterval("PT{$segundos}S"), since this class does not transform the total amount of seconds into hours, minutes and seconds (PT120S - 120 seconds - is formatted as 00:00:120, and not as 00:02:00). In this case, you would have to manually calculate the hours, minutes and seconds (as suggested in reply from Guilherme Nascimento) and then create the DateInterval. Ex:

$seconds = 100800;
// calcular manualmente as horas, minutos e segundos (versão simplificada da resposta do Guilherme Nascimento)
$hours = floor($seconds / 3600);
$mins = floor(($seconds - ($hours * 3600)) / 60);
$secs = floor($seconds % 60);

$tempo_1 = new DateInterval("PT{$hours}H{$mins}M{$secs}S");
echo $tempo_1->format('%H:%I:%S'); // 28:00:00

Although in this case, it seems an exaggeration to use DateInterval, because you have already calculated each field separately and just print them.

The documentation also suggests other alternatives to turn 100800 seconds in 28 hours, such as this (which uses a logic similar to maniero’s response, to use a DateTime to get another DateInterval), or that one (which makes the calculations similar to reply from Guilherme Nascimento). Unfortunately, DateInterval does not make this conversion natively.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.