0

I am struggling very hard to achieve this but the results are not what i am trying to get.The result values are wrong and the colons are not on their proper place when using random minute values(that should generate years : months : weeks : days : hours : minutes) from such as 1500, 2800 .etc

I want to convert Minutes into respective years : months : weeks : days : hours : minutes

What i have tried?

$minutesConverter = function($from,$to,$format='dynamic'){
    $year    = floor($from/525600)    ? floor($from/525600)     : '';
    $Rminutes= $from%525600;
    $month   = floor($Rminutes/43800.048)  ? floor($Rminutes/43800.048)  : '';
    $Rminutes= $from%43800.048;
    $week    = floor($Rminutes/10080)     ? floor($Rminutes/10080)     : '';
    $Rminutes= $from%10080;
    $day     = floor($Rminutes/1440)      ? floor($Rminutes/1440)      : '';
    $Rminutes= $from%1440;
    $hour    = floor($Rminutes/60)        ? floor($Rminutes/60)            : '';
    $minute  = $from%60               ? $from%60               : '';            

    if(!empty($year)  ){ if($year   > 1 ){ $year    = $year.' years';     }else{ $year   = $year.' year';     }  }
    if(!empty($month) ){ if($month  > 1 ){ $month   = $month.' months';   }else{ $month  = $month.' month';   }  }
    if(!empty($week)  ){ if($week   > 1 ){ $week    = $week.' weeks';     }else{ $week   = $week.' week';     }  }
    if(!empty($day)   ){ if($day    > 1 ){ $day     = $day.' days';       }else{ $day    = $day.' day';       }  }
    if(!empty($hour) ){ if($hour    > 1 ){ $hour    = $hour.' hours';     }else{ $hour   = $hour.' hour';     }  }
    if(!empty($minute)){ if($minute > 1 ){ $minute  = $minute.' minutes'; }else{ $minute = $minute.' minute'; }  }  

    if(!empty($year))  { $year   = $year;   }
    if(!empty($month)) { if(empty($year)){ $month  = $month; }else{ $month  = ' : '.$month; } }
    if(!empty($week)) { if(empty($month)){ $week  = $week; }else{ $week  = ' : '.$week; } }
    if(!empty($day)) { if(empty($week)){ $day  = $day; }else{ $day  = ' : '.$day; } }
    if(!empty($hour)) { if(empty($day)){ $hour  = $hour; }else{ $hour  = ' : '.$hour; } }
    if(!empty($minute)) { if(empty($hour)){ $minute  = $minute; }else{ $minute  = ' : '.$minute; } }
    
    return $year.$month.$week.$day.$hour.$minute;
};

Example:

For using minute value as 131400 i,e 3 Months(according to google calculator) But the output of this is:

2 months6 hours

Problems?

  1. No colons

  2. Wrong Readable time

Help Needed ! Kindly take a look.

Community
  • 1
  • 1
MR_AMDEV
  • 1,712
  • 2
  • 21
  • 38
  • @Nkoyan the issue with your proposed duplicate is that it gives a different result dependent on what day you run it on. If you run it on March 29, it gives 3 months 1 day. If you run it on Feb 28, it gives 2 months 29 days. – Nick Mar 30 '19 at 00:06

3 Answers3

1

The main issue with your code is that you should be subtracting the number of periods times the period length from $from on each pass, not taking the modulus of $from with the period length. This is because not all your periods are multiples of all the smaller ones (e.g. 1 month is not an exact multiple of weeks). The code can also be signficantly simplified by the use of an array of periods, for example:

$minutesConverter = function($from) {
    if (!$from) return '0 minutes';
    $periods = array('year' => 525600,
                     'month' => 43800,
                     'week' => 10080,
                     'day' => 1440,
                     'hour' => 60,
                     'minute' => 1);
    $output = array();
    foreach ($periods as $period_name => $period) {
        $num_periods = floor($from / $period);
        if ($num_periods > 1) {
            $output[] = "$num_periods {$period_name}s";
        }
        elseif ($num_periods > 0) {
            $output[] = "$num_periods {$period_name}";
        }
        $from -= $num_periods * $period;
    }
    return implode(' : ', $output);
};

echo $minutesConverter(131390, 0);

Output:

2 months : 4 weeks : 2 days : 9 hours : 50 minutes

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
  • I have just improved the code to meet the need of `minutes` also.Thanks for the support. – MR_AMDEV Mar 30 '19 at 10:27
  • @MR_AMDEV sorry about omitting the minutes. The fix for minutes is simpler than the change you made, I've updated the code (and demo) with that. I've also corrected the 1440 for days. – Nick Mar 30 '19 at 12:16
  • @MR_AMDEV I wouldn't use 43800.048 for months as this code is really designed for operation with integer values and may not produce the correct results if you introduce floating point numbers owing to floating point precision issues. – Nick Mar 30 '19 at 12:18
  • Thanks man this works ! – MR_AMDEV Apr 01 '19 at 17:16
  • Its not working if the minutes are `0` ; returns empty.Its should return 0 – MR_AMDEV Apr 29 '19 at 17:38
  • @MR_AMDEV do you mean just when you call `$minutesConverter(0)` or any time which has a 0 minute value e.g. `$minutesConverter(120)`? – Nick Apr 29 '19 at 23:18
  • Only when doing something like `$minutesConverter(0)` . – MR_AMDEV Apr 30 '19 at 17:00
  • 1
    @MR_AMDEV the easiest way to deal with that is just to check for it at the start of the function e.g. `if (!$from) return '0 minutes';`. See my edit. – Nick Apr 30 '19 at 22:47
0

Not a direct answer to your question, but maybe an idea and a template.

I had a similar problem years ago. I wanted to display a duration (time stamp into the past) as relative time. So I create the function below and had the following prerequisites:

  • Short format.
  • Fixed field width (6+strlen(separator)).
  • No month, because the length of month varies too much.
  • Units are the abbreviations of seconds, minutes, hours, days, weeks and years.
  • Support of german and english abbreviations.
  • Limit the output to 2 units. More doesn't help a reader.
  • Option separator between 2 units.

And here is my function:

function GetAge6 ( $sec, $sep = '' )
{
    if ( $sec < 60 )
        return sprintf('   %s%2us',$sep,$sec);

    $min = floor($sec/60);
    if ( $min < 60 )
        return sprintf('%2um%s%02us',$min,$sep,$sec%60);

    $hour = floor($min/60);
    if ( $hour < 48 )
        return sprintf('%2uh%s%02um',$hour,$sep,$min%60);

    $day = floor($hour/24);
    if ( $day < 28 )
        return sprintf('%2ud%s%02uh',$day,$sep,$hour%24);

    $week = floor($day/7);
    if ( $week < 52 )
        return sprintf('%2uw%s%2ud',$week,$sep,$day%7);

    $year = floor($day/365);
    if ( $year < 100 )
    {
        $week = floor( ( $day - $year*365 ) / 7 );
        return sprintf('%2u%s%s%02uw',
                $year, DCLIB_LANGUAGE=='de' ? 'j' : 'y', $sep, $week );
    }

    if ( $year < 1000000 )
        return sprintf('%s%6u%s',
                str_repeat(' ',strlen($sep)),
                $year, DCLIB_LANGUAGE=='de' ? 'j' : 'y' );

    return sprintf('***%s***',$sep);
}

And here is an example: https://wiimmfi.de/stats/login/ext#hour See column average online duration and the hover texts.

Wiimm
  • 2,971
  • 1
  • 15
  • 25
0

If you use composer (https://getcomposer.org) I recommend using the Carbon package for dates and such. Maybe you and others will find this interesting:

https://carbon.nesbot.com/docs/#api-difference

It also has difference for humans, if you would like something like: "1 hour ago".

https://carbon.nesbot.com/docs/#api-humandiff

This is also the package used by the Laravel framework and it is awesome. But if you're trying to learn new stuff, I would definitely convert all into seconds before calculating and start with the highest amount and so on.

Ulrik McArdle
  • 601
  • 4
  • 9