visitor (0 QPoints)
  • FR
  • EN
  • NL
  • DE
  • ES
315 experts, 1193 registered users, 1659 questions already answered
European Experts Exchange, the very best site for high-quality IT solutions

New Improved Search!

 


05/10/2011 1h30 : Steve Jobs is dead, the father of Apple ][ is gone, we are all orphaned.

Languages :: PHP :: performance problem with function to calculate time period (incl. workdays)


By: peter911 Germany  Date: 10/06/2003 00:00:00  English  Points: 500 Status: Answered
Quality : Excellent
sorry for multiposting - it's a php question but perhaps a non-php-programmer can help me too...


i have a function datesubtr to calculcate the time period of 2 dates - it reckognizes the workdays too.

the big problem now is while-loop - it takes too much time to execute (if you test it with a counter in the while-loop you'll see that the while-loop is being executed for 4970949709 times)

can anybody optimize the performance of this function or has an alternative idea?

many thnx, Peter

//parameteres $from and $until are "YYYY-MM-DD"
function datesubtr($from,$until){

$tsfrom=strtotime($from);
$tsuntil=strtotime($until);

$hrsfrom=date("H",$tsfrom);
$minfrom=date("i",$tsfrom);
$secfrom=date("s",$tsfrom);
$hrsuntil=date("H",$tsuntil);
$minuntil=date("i",$tsuntil);
$secuntil=date("s",$tsuntil);

$tsfrom=mktime($hrsfrom,$minfrom,$secfrom,date("m",$tsfrom),date("d",$tsfrom),date("Y",$tsfrom));
$tsuntil=mktime($hrsuntil,$minuntil,$secuntil,date("m",$tsuntil),date("d",$tsuntil),date("Y",$tsuntil));

$timeperiod=$tsuntil-$tsfrom;

if (date("D",$tsfrom)=="Sat" || date("D",$tsfrom)=="Sun"){
$timeperiod=$timeperiod-(((24-$hrsfrom)*3600)-($minfrom*60)-$secfrom);

}

$sdatum=date("Ymd",$tsfrom+86400);
$tdatum=$tsfrom+86400;

while ($sdatum!=date("Ymd",$tsuntil)){
$wtag=date("D",$tdatum);
$feiert=false;
if (in_array(date("Y-m-d",$tdatum),$FeiertageDaten["FEIERT_DATUM"])){
$feiert=true;
}

if ($wtag=="Sat" || $wtag=="Sun" || $feiert==true){
$timeperiod=$timeperiod-86400;
}
$sdatum=date("Ymd",$tdatum+86400);
$tdatum=$tdatum+86400;
}

if (date("D",$tsuntil)=="Sat" || date("D",$tsuntil)=="Sun"){
$timeperiod=$timeperiod-($hrsuntil*3600)-($minuntil*60)-$secuntil;
}

return $timeperiod/60/60;

}

By: VGR Date: 10/06/2003 00:26:00 English  Type : Comment
ok, this seems a rather strange way to do it.
Moreovern you get rid of non-workable days, but you count 24 hours par day , while people rarely are able to work that amount. Let's skip over this.

Then I suppose that the array $FeiertageDaten is declared GLOBAL in the function, huh ? For me, it's empty "as is"

Iterating so many times si the sure proof there is a conceptual problem.

Give me some time
By: ThG Date: 10/06/2003 00:38:00 English  Type : Comment
> while people rarely are able to work that amount. Let's skip over this.

seems something that doesn't apply to you :-)
By: peter911 Date: 10/06/2003 00:40:00 English  Type : Comment
plz don't notice the line with $FeiertageDaten and don't mind the if-operator "$feiert==true" - they shouldn't stand there.

i've changed the while-line to:

while ($sdatum!=date("Ymd",$tsbis) && date("Y",$sdatum)==date("Y")){

now it's fast enough but i don't know if it produces errors - especially when the years of the two dates are different...

thnx peter
By: ThG Date: 10/06/2003 00:53:00 English  Type : Comment
peter911: your code as-is kinda work, except when $until is before $from.

anyway testing in a loop with != or == is dangerous in this situation. If for any reason the matching point is passed, you get into an infinite loop. You should use <= or >=.

anyway there are too many things that doesn't make sense, like:
...
while ($sdatum!=date("Ymd",$tsuntil)){
...
$sdatum=date("Ymd",$tdatum+86400);
$tdatum=$tdatum+86400;
...

why don't you just test $tdatum with $tsuntil??

By: VGR Date: 10/06/2003 01:25:00 English  Type : Comment
huh huh huh
By: VGR Date: 10/06/2003 01:40:00 English  Type : Assist
ok, here's a result from your code :
1. 172800
2. 172800
3. 20030102 / 1041548400
4. 172800
retourné : 48
called with this : $toto=datesubtr('2003-01-01','2003-01-03');
(two days -> 48 hours, fine)
1st of Jan. is a Wed so you wonsider it workable, while it is almost wordly non-working.

What you do - given you don't (yet?) have a 'Ferien Daten' feature - is roughly :
-count the number of days being not Sun nor Sat
-return 24*this number

(yes yes yes, that's what you do)

So it can be done differently and more efficiently.

1) tell me if you agree so that I do something for you
2) I've an other suggestion, involving using MySql's perpetual calendar : would you consider accessing MySql for this ? It's a matter of one query :D
By: peter911 Date: 10/06/2003 02:14:00 English  Type : Comment
i've implemented the holiday-problem (from a mysql-table) yet - but this doesn't crash my performance... with the changed while-line it goes really as fast as i'm needing it...
thnx peter
By: ThG Date: 10/06/2003 02:34:00 English  Type : Comment

i keep thinking you should change your while condition to

while ($tdatum < $tsuntil)

By: sumotimor Date: 10/06/2003 03:32:00 English  Type : Answer
Fun problem!

This works for me. It uses the dateAdd() function to iterate, first using weeks (with each complete week = 5 work days), then iterating through each day in the last week to add the remainder. The days between is inclusive, so 2003-06-09 to 2003-06-10 would return 2. If you're after hours, just multiply the number of days * work hours in your day. It's reasonably fast, and should be smart about leap-years, daylight savings, etc.

If you need it to omit holidays, that gets somewhat messier.

<?php

function daysBetween($start, $stop) {
$days = 0;
$start = strtotime($start); // convert YYYY-MM-DD to timestamp
$stop = strtotime($stop); // convert YYYY-MM-DD to timestamp
echo ("Finding range between " . date ("D Y-m-d", $start) . " through " . date("D Y-m-d", $stop) . "<br />\n");
// add weeks
while (($test = dateAdd(7, 0, 0, $start)) <= $stop) {
$days += 5; // 5 workdays in a week
$start = $test;
}
// add days
//while (($test = dateAdd(1, 0, 0, $start)) <= $stop) {
for ($test=$start; $test <= $stop; $test = dateAdd(1, 0, 0, $start)) {
if (date('w', $test) != 0 && date('w', $test) != 6) {
$days++;
}
$start = $test;
}
return $days;
}

function dateAdd($days, $months=0, $years=0, $start=null) {
if (!$start) $start = time();
$now = getDate($start);
return mktime($now['hours'], $now['minutes'], $now['seconds'], $now['mon'] + $months, $now['mday'] + $days, $now['year'] + $years);
}

echo daysBetween("2003-06-03", "2003-06-14");

?>
By: peter911 Date: 10/06/2003 03:44:00 English  Type : Comment
thnx, i found my mistake - the while-loop was running endless!

Do register to be able to answer

EContact
browser fav
page generated in 315.909860 milliseconds

Why Google AdSense ads ?

compteur
 Ranking-Hits PageRank for this page