2

I am calling some data from a simple SELECT query where the row contains three fields, start_time(DATETIME), end_time(DATETIME) & reopen_time(DATETIME).

I am trying to work out the time between both the start_time and end_time & also between the start_time & reopen_time if this is a case. The way I am going to determine is by another column called complete, which sets a value of either 1 or 0. It will calculate the start_time and end_time IF the complete value is 1 and start_time and reopen_time is value is 0.

Now, my PHP looks like the following so far; I am looking for somebody to help by casing and eye:

$id=$_GET['id']; //this grabs the id from the previous page depending on which task is clicked.
$sql = "SELECT * FROM to_do_list WHERE id=$id";
$result = mysqli_query($db, $sql);

if(mysqli_num_rows($result) > 0){
    while($row = mysqli_fetch_assoc($result)){    
        $date1 = $row["start_time"];        
        $date2 = $row["end_time"];            
        $diff = abs(strtotime($date2) - strtotime($date1));            
        $years = floor($diff / (365*60*60*24));
        $months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
        $days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));            
        printf("%d years, %d months, %d days\n", $years, $months, $days);            
    }
} else {
    echo "There are no tasks!";
}

Now, the result I get back is: 0 years, 0 months, 0 days but there isn't an error. I am presuming the format of the data from the database is maybe incorrect?

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
PhpDude
  • 1,542
  • 2
  • 18
  • 33

2 Answers2

1

I would take some time (see the pun?) an look at the PHP Class DateTime (http://php.net/manual/en/class.datetime.php).

This class already has a built in method for taking the difference between dates including cases like leap years, creating a DataInterval Object. Then using the DataInterval object you can format and pullout the pieces you want (Months, days, years, etc...)

while($row = mysqli_fetch_assoc($result)){    
    $date1 = $row["start_time"];        
    $date2 = $row["end_time"];    
    $datetime1 = new DateTime($date1);
    $datetime2 = new DateTime($date2);
    $interval = $datetime1->diff($datetime2);
    echo $interval->format('%y years, %m months, %d days');     
 }

Note: if the $date1 and $date2 fields are exactly same, you'll get and interval that reports 0 for months, years, etc... as there's no difference in time.

Also, be aware, because DateInterval takes into account variations like leap years, you'll need to remember not every 'month' is equal. If you need to know exact days difference, DateInterval provides a public property days:

 $date1 = new DateTime('2015-02-27');  // 2015 was a 'regular' year, Feb had 28 days
 $date2 = new DateTime('2015-03-27');
 $interval_1_2 = $date1->diff($date2);

 $date3 = new DateTime('2016-02-27'); // 2016 was a 'leap' year, Feb had 29 days
 $date4 = new DateTime('2016-03-27');
 $interval_3_4 = $date3->diff($date4);

 echo $interval_1_2->format('%m months, %d day '); // 1 month 0 days 
 echo "Total Days: ".$interval_1_2->days."\n"; // 28 days

 echo $interval_3_4->format('%m months, %d day');  // 1 month 0 days
 echo "Total Days: ".$interval_3_4->days."\n"; // 29 days

First rule of Dev-club: Don't re-invent the wheel unless:

  1. You don't care about functionality and are just experimenting with problems solved, many, many, times before for a some kind of learning experiment.
  2. and willing to probably end up with a quirky result that doesn't quite cover 100% use cases.
  3. Just having fun.
Ray
  • 40,256
  • 21
  • 101
  • 138
  • Hey Ray, thanks for taking the time to provide an answer, I am still a little confused though, is what I have done incorrect or is there a far better method? Or are you saying use the above? – PhpDude Apr 02 '16 at 20:20
  • @Dan, you trying to convert date strings to numbers to calculate the difference in days, months, years. A method already exists to get that stuff so use that instead of troubleshooting your own custom implementation. – Ray Apr 02 '16 at 20:22
  • @Dan I edited my example to `echo` out the exact string you're trying to `printf`; – Ray Apr 02 '16 at 20:28
  • Hey, I am still getting back `0 years, 0 months, 0 days` despite the values being different times stored in the db? – PhpDude Apr 02 '16 at 22:10
  • @Dan if you're seeing `2016-04-02 19:48:04 2016-04-02 19:55:20` for `$date1` and `$date2` it's the same day. There's less than 1 day difference, so of course you'll get `0 years, 0 months, 0 days` – Ray Apr 02 '16 at 22:27
  • @Dan You can go finer granularity with the `DateInterval`, as far as hours, minutes and seconds. Try `echo $interval->format('%y years, %m months, %d days, %h hours, %i minutes, %s seconds');` – Ray Apr 02 '16 at 22:30
  • @Dan the key is, use either the built in classes (or myql functions) as they account for months that change in length on leap year and such. – Ray Apr 02 '16 at 22:32
  • Is the suggested method (above) the best for the situation where it will account for leap years etc? – PhpDude Apr 02 '16 at 22:33
  • Yes, the PHP DateTime classes have all those little details worked out like leap years, the fact months sometimes have 28, 29, 30 or 31 days, etc... I'm sure in MySQL their functions do as well, I just like to keep my business logic out of the DB queries as much as possible--if you change backend say to SQLite for testing, the queries may not work if you use custom mysql functions. – Ray Apr 02 '16 at 22:34
  • I am really appreciative of all of your help and I have a much better understanding now of to handle this. Very kind of you to explain in depth. – PhpDude Apr 02 '16 at 22:35
1

You could actually do this directly in mysql using TIMESTAMPDIFF

$id= filter_input(INPUT_GET, 'id',FILTER_VALIDATE_INT); 
//crude sql injection filter, dont use in production

$sql = "
    SELECT *,
    TIMESTAMPDIFF(YEAR, start_time , end_time) as years,
    TIMESTAMPDIFF(MONTH, start_time , end_time) as months,
    TIMESTAMPDIFF(DAY, start_time , end_time) as days
    FROM to_do_list WHERE id=$id
";

$result = mysqli_query($db, $sql);

if(mysqli_num_rows($result) > 0){
    while($row = mysqli_fetch_assoc($result)){    
        $date1 = $row["start_time"];        
        $date2 = $row["end_time"];            
        $years = $row["years"];        
        $months = $row["months"];        
        $days = $row["days"];        

        printf("%d years, %d months, %d days\n", $years, $months, $days);            
    }
} else {
    echo "There are no tasks!";
}

see this answer for info on how to properly handle sql injection prevention

Community
  • 1
  • 1
andrew
  • 9,313
  • 7
  • 30
  • 61
  • This still gives me the same result of `0 years, 0 months, 0 days` – PhpDude Apr 02 '16 at 22:07
  • 2
    what does `echo "$date1 $date2";` give? – andrew Apr 02 '16 at 22:09
  • It gives: `2016-04-02 19:48:04 2016-04-02 19:55:20` – PhpDude Apr 02 '16 at 22:11
  • 1
    @andrew this should work, but I recommend keeping business logic out of queries (unless it's needed in the query itself for generating the result set). This i mainly because when you code it into a query, you look in the DB. If you wanted to test locally on a say a SQLite DB, your code would be brittle (meaning MysQL native function would break). – Ray Apr 02 '16 at 22:38
  • 1
    @Dan well those times are the same year month and day so there is no difference hence why you get 0 – andrew Apr 02 '16 at 23:17