I was using simple function to calculate difference between two dates and timestamps until I noticed, it’s not working correctly in long intervals. It’s very easy to calculate difference between two timestamps in seconds, but it’s much more complicated print difference in human readable format. The Internet can be found in a wide range of ways to do this thing, but as a rule they use a fixed amount of seconds for the year and the month. So if we calculate year with using 365 or 365.25 days and month using 30 or 31 then the difference is not accurate, because of leap years, DST (Daylight Saving Time) and so on.
Because of this problem, I decided to make a function (at least in the short testing) to return the right kind of differences between the UNIX timestamps and dates in human readable format. This function uses PHP strtotime function to calculate real differences and can handle leap years and DST. This function can also return Twitter like about texts with precision parameter.
PHP dateDiff function for calculating real differences between dates and UNIX timestamps
<?php // Set timezone date_default_timezone_set("UTC"); // Time format is UNIX timestamp or // PHP strtotime compatible strings function dateDiff($time1, $time2, $precision = 6) { // If not numeric then convert texts to unix timestamps if (!is_int($time1)) { $time1 = strtotime($time1); } if (!is_int($time2)) { $time2 = strtotime($time2); } // If time1 is bigger than time2 // Then swap time1 and time2 if ($time1 > $time2) { $ttime = $time1; $time1 = $time2; $time2 = $ttime; } // Set up intervals and diffs arrays $intervals = array('year','month','day','hour','minute','second'); $diffs = array(); // Loop thru all intervals foreach ($intervals as $interval) { // Set default diff to 0 $diffs[$interval] = 0; // Create temp time from time1 and interval $ttime = strtotime("+1 " . $interval, $time1); // Loop until temp time is smaller than time2 while ($time2 >= $ttime) { $time1 = $ttime; $diffs[$interval]++; // Create new temp time from time1 and interval $ttime = strtotime("+1 " . $interval, $time1); } } $count = 0; $times = array(); // Loop thru all diffs foreach ($diffs as $interval => $value) { // Break if we have needed precission if ($count >= $precision) { break; } // Add value and interval // if value is bigger than 0 if ($value > 0) { // Add s if value is not 1 if ($value != 1) { $interval .= "s"; } // Add value and interval to times array $times[] = $value . " " . $interval; $count++; } } // Return string with times return implode(", ", $times); } ?>
dateDiff function example usage
strtotime examples:
echo dateDiff("2010-01-26", "2004-01-26") . "\n"; echo dateDiff("2006-04-12 12:30:00", "1987-04-12 12:30:01") . "\n"; echo dateDiff("now", "now +2 months") . "\n"; echo dateDiff("now", "now -6 year -2 months -10 days") . "\n"; echo dateDiff("2009-01-26", "2004-01-26 15:38:11") . "\n";
Output:
6 years 18 years, 11 months, 30 days, 23 hours, 59 minutes, 59 seconds 2 months 6 years, 2 months, 10 days 4 years, 11 months, 30 days, 8 hours, 21 minutes, 49 seconds
UNIX timestamp and precision examples
echo dateDiff(time(), time()-1000000, 1) . "\n"; echo dateDiff(time(), time()-1000000, 3) . "\n"; echo dateDiff(time(), time()-1000000, 6) . "\n";
Output:
11 days 11 days, 13 hours, 46 minutes 11 days, 13 hours, 46 minutes, 40 seconds
Converting text format back to UNIX timestamp example
$time1 = time(); $time2 = $time1-10000000; echo $diff = dateDiff($time1, $time2) . "\n"; echo $time1 . "\n"; echo strtotime(" +".$diff, $time2) . "\n";
Output:
3 months, 23 days, 17 hours, 46 minutes, 40 seconds 1264514564 1264514564
Related posts:
- PHP: Loop through dates (from date to date) with strtotime() function This is very easy way loop through dates (from date to date) with PHP strtotime() function. This example only echo...
- PHP mb_ucfirst Make a String’s First Character Uppercase-Multibyte (UTF-8) Function PHP’s ucfirst function is very usefull when you want to change words first letters to uppercase and other letters to...
- PHP __DIR__, __FILE__, __FUNCTION__, __CLASS__, __METHOD__, __LINE__, __NAMESPACE__ PHP has large number of predefined constants. This HOWTO will present the seven most important, most practical and most useful...
- Format bytes with PHP – B, KB, MB, GB, TB, PB, EB, ZB, YB converter Simple PHP function that formats the bytes to the desired form. Possible unit options are: Byte (B) Kilobyte (KB) Megabyte...
- PHP Class for Coloring PHP Command Line (CLI) Scripts Output – PHP Output Colorizing Using Bash Shell Colors PHP Command Line Interface (CLI) has not built-in coloring for script output, like example Perl language has (perldoc.perl.org/Term/ANSIColor.html). So I...
I really liked your blog! great
I was having the same accuracy issues caused by DST etc. This function has been a great help. Thanks!
Finally someone has done a “date diff” right!
Although I wish that under 24 hours it would have been displayed as “XX:YY:ZZ” instead.
Added from other comments:
BTW, IE6 users can’t use this comment form.
BTW, this comment form requires cookies without saying so on errors.
Hi LWC,
Nice to hear that you like my way to do real date diff. :)
Simple way to get “hours:minutes:seconds” format work for seconds if difference is less than 24 hours is following:
I think that comment form is working, but because of spam comments are waiting for moderation, if the user has never before written anything on this blog.
Cheer’s thanks for this function. It really helped.
Just a quick question though. How would I edit it to remove the seconds? I’m doing something like Last Post was made 2 hours, 5 minutes ago and don’t really want the seconds showing.
It’s working fine and I’m using something like
Again cheers for this. Even if you don’t reply it’s really useful and exactly what I was looking for.
Hi L3G3ND,
Nice to hear that this function helped you. :)
Complete removal of the seconds is easy, just remove second from the intervals array, like following:
And then change default precisions from 6 to 5, like following:
Finally take care about intervals which is less than minute, like following:
Is this what you need? :)
Ya JR,
That method to remove the seconds works perfectly. Thanks again for this function. It’s really useful.
L3G3ND
http://www.myu2sig.com/siteage.php
echo dateDiff(1267125840, “now”, 4) . “\n”;
That’s how I’m using it… but it doesn’t appear to be quite right. As of this posing it should be displaying 93 Days, 12 Hours but it’s an hour off. It’s showing 93 Days 11 Hours
http://www.epochconverter.com/
This is what the converter says since I’m EST / EDT: Your timezone: Thursday, February 25, 2010 2:24:00 PM
Anyway, like I said… it’s an hour behind and I dont know what do do.
For simplicity sake, when it hits 2:24pm on the nose today it should show 94 Days but it’s going to show 93 Days, 23 Hours
Thought I’d add that if I’m right it’s not counting for the DST +1 we just had a while ago or something… I don’t know.
Hi Andy,
Did you set right timezone when you use this function?
I check out this case more specifically later.
// Set timezone
date_default_timezone_set(“GMT”);
I used the dsame time zone that the epox converter works with which displays the right time for me.
My concern is keeping it as accurate as possible. I mean sure I can change the TZ or agg +3600 to get it accurate but regardless of what I do wont I have to change something every year when time changes or whatever ?
No need to really toss this comment in there but I want it to be like that Ron Popeil (sp?) thing, just set it and forget it lol
@Andy
Could you post exact timestamps what you use with dateDiff function and Epoch Converter?
And what tool you use on Epoch Converter site?
Timestamp & dateDiff: echo dateDiff(1267125840, time(), 4) . “\n”;
I’m using what turns the timestamp in to human time.
So in my case which I showed before, 1267125840 translates into :
GMT: Thu, 25 Feb 2010 19:24:00 UTC
Your (Me) timezone: Thursday, February 25, 2010 2:24:00 PM
Problem solved by entirely removing the following… I think it was somehow conflicting with the server settings.
// Set timezone
date_default_timezone_set(“UTC”);
I wanted to add that if I’m right I would suggest to remove it from the above “demo” since most people have the right TZ set o ntheir server or atleast adding a note about possible conflictions with server TZ settings.
@Andy
Nice to hear that you get problem solved, but thats why my first question was Did you set right timezone when you use this function? And you answered that, you are using following date_default_timezone_set(“GMT”);. So I thought that you understand the purpose of this function at the beginning of scripts.
Following is quoted from date_default_timezone_set PHP manual:
Note: Since PHP 5.1.0 (when the date/time functions were rewritten), every call to a date/time function will generate a E_NOTICE if the timezone isn’t valid, and/or a E_WARNING message if using the system settings or the TZ environment variable.
So date_default_timezone_set call is there because I just want to ensure that the function works without E_NOTICE and E_WARNING messages for all . Of course, I assume that every user to change the time zone correct or remove it completely if it is not needed.
Btw. It is important to first examine the code in more detail before you copy it in any proper use. All codes are similar examples, and of course it is very important to understand that what the code actually does. And modify the codes to suit own projects, if it does not correspond fully to what is needed.
it works……………
thanks ……………………………..
$start_working_hours = 08:00:00
$launc_time = 30 minutes
$end_working_hours = 16:00:00
$start_time_product_A = 27 July 2010 08:00:00
$end_time_product_A = 28 July 2010 10:00:00
I don’t really want the years, months, days, hours showing. How to calculate time product A = 570 minutes? thx.
Hi evang,
Your question is not very clear. There is nothing to indicate that when this “launc_time” (maybe lunch_time) is?
If I understand correctly, that what you want to calculate, then one simple possible solution is count all full days and calculate (7.5 x 60) minutes/per full day and then handle first and last day minutes in some way, that you want.
Your example logic goes like following:
If first day start time < = 08:00:00 then it's full day
If last day end time >= 16:00:00 then it’s full day
Else first day and last day need some other handling
27 July is full day 7.5 x 60 minutes = 450 minutes
28 July is not full day 2 x 60 minutes = 120 minutes
total is 570 minutes
So this is just logic for helping to write real code.
Hey JR, been a while… had to do a bit of yahooing to find this page again. In any case, if you check out my site you will see how I’m using your code ;-)
Anyway, as to why I’m here today… I was wondering if you could help me with something I’d like to achieve with your code. I think it might be a rather nice addition, for me anyway. I can pay your about $5 – $10 for this hack if you’d like…
So here’s the deal…
Actually, I want to side track just for a sec…
When the days reach triple digits it might look like 0000, well where would I use number_format() to get it to add the commas in the appropriate places when the days go triple etc. ? I looked but it’s possible I’m just missing it after al this work I’ve been doing heh.
Ok, now here’s the deal heh..
Every time a new year passes instead of showing something like ??? Days, ?? Hours, ??Minutes I would like it to show something like this…
Happy (1st, 2nd, 3rd, 4th etc.) Birthday myu2sig.com !
I know this can be achieved but have no idea what to do to do it and I would really love your help with this modification JR. The only problem I can see maybe causing any issues would be with telling it when exactly a year has passed because of leap years etc. Off the top of my head though I can see that as being the only obstacle.
I thought I would add that the “message” that should show every time a year passes should be shown all day and that when this day is over it would start showing the time again (??? Days, ?? Hours, ?? Minutes).
So since I deleted the default timezone line it’s going by my server time to determine my TZ which is EST / EDT it should know that a day begins at 12am and ends at 11:59pm that night.
So lets say today was my sites 1st birthday, at 12am this morning it should have started showing Happy 1st Birthday myu2sig.com ! instead of normal time stuff and it would show this all day. Now come 11:59pm that same day / 12am the next day it would start showing the normal time stuff again and every time a new year passes the message updates accordingly.
Happy (1st, 2nd, 3rd, 4th etc.) Birthday myu2sig.com !
Here’s my existing code incase you need it…
Hi again Andy,
It does not make sense to use this function for such simple calculation. You don’t need to know that how long time has passed, but you have to know the original birthday and current date.
So you need to extract dates, months and years from birthday date and current date. Then make simple comparison with months and dates. If they are the same, then simply calculate current year minus year birthday year and that’s all.
I wrote a quick simple PHP code to make this work:
Note: this needs of course styling, but this is logic to show birthday… :)
Thx for the reply, I’m not gonna lie… I know little to nothing about PHP. I know some minore things to help get me by with small fixes to my site which is PHP crazy but I had to hire a coder to build it for me. As for my idea My site rolls over 1 day at midnight so like right now my counter says 173 Days, 11 Hours, 16 Minutes but come midnight tonight / tomorrow morning it would read 174 days. Now sorry to sound like a broken record, just habbit but, lets say tomorrow was my sites 1yr birthday. Then staring tonight at moidnight / tomorrow morning it should read something like Happy 1st B-Day myu2sig.com ! and stay that way all day and then revert back to showing time the next day. Then come the sites 2nd birthday it would show Happy 2nd B-Day myu2sig.com ! so on and so forth for every 1yr therafter. If I come close to understanding what you wrote all it will do is show the number have years passed since the birthday and then show nothing if it’s not a birthday. http://www.myu2sig.com/birthday.php – Like today it shows 3 given the default dates but come tomorrow it should show nothing (a blank page).
Ok, so I made a goot bit of headway with this, all be it not much… Anyway, everything is exactly how I want it except for the english ordinal suffix. I don’t know what to do to get it to add the st, nd, rd and th for the appropriate years.
Ex. 1st, 2nd, 3rd, 4th etc. etc. etc.
Hi Andy,
Here is simple PHP function to add ordinal suffix. I tested this quickly, but looks like, it is working…
In your case, use this as follows:
1. Add function to inside < ?php ?> tags.
2. And do following change to your code:
I hope this helped you to achieve what you wanted? :)
Oh it did help heh, one thing though… like I was asking before… I made my time code 0 to use as an example but see: http://www.myu2sig.com/birthday.php
As of this posting the days read 14839 Days where would number_format() be used to get it tto read 14,839 Days ?
You must love me lol, anyway, I wanted to try and change Hour / Hours to Hr / Hrs and Minute / Minutes to Min / Mins but when I do it breaks the script. What needs to be done to get that working ?
I only just now thought of this because I’m trying to think of a way to save a few pxs in width… Thx JR ;-)
Hi Andy,
It is okey that you’re not a pro programmer and you want to use this example to help you, so therefore of course I will help you gladly :)
First number_format function usage is simple, like following:
Reason why changing Hour to Hr and Minute to Min break the script is strtotime function, which does not understand this shorter format. Simple solution to this is to give each value their own display name, which can be changed easily.
Add following line
after $intervals = array(‘Day’, ‘Hour’, ‘Minute’); line.
Then replace $times[] = $value . ” ” . $interval; line with following:strong
And you are done.
I do not tested this very well, so if this doesn’t work, please let me know.
I decided to do away with the number format idea because I wanted to add Years back in so people wouldnt have to do the math to figure out a more exact age. Anyway, if you look at: http://www.myu2sig.com/birthday.php
You will see the “names” aren’t showing up, I modified your code accordingly to work with years but even before that the names wouldnt show. Here’s my code with your changes regarding shortened intercal names.
Yes there was little mistake…try following:
Works like a charm, I now have this the way I want it ;-)
Thx JR !
You’re welcome Andy
Nice to hear that you got it working as you wanted… :D
I found another used for your below code that I wanted to use it for, however, I found something quirky with it. Unless an else statment is used with it it only appears to work one day a year. By that I mean I had it set for 1999-08-20 and 11 showed as years passed but soon as it rolled midnight 8/20 the year print dissapeard. Using the below code which is the same as the original provided minus the default TZ since I have that set in my ini file what needs to be done to make the year always show if that makes sense. Like right now assuming 8-20 stays then for the next 360 some odd days it should show the printed 11 and change to a 12 come 8-20 of next year.
I guess I could else the if statement but there’s got to be an easier way.
It seems I solved the problem by removin the if statement all together, let me know if that was the wrong decision due to an easier solution… either way if I’m right the printed years past should now always show regardless of the day and still +1 a year when the $birthday_date rolls aorund. So like right now with it being on 1999-08-20 it shows 11 and should continue to show it for the next 360 some odd days and change to a 12 come 8-20 of next year and continue to roller over a new year for each passing year.
When I said I removed the if statement I meant just the if ($birthday_month == $current_month && $birthday_day == $current_day) { }, I left the echo in. But like I said, everything appears to be working ok now (the years passed always show and not just on the bday like when the if statement was included). Oh, sorry for all the posts to but there’s no edit function here ;-/
Either way I really appreciate your code and the help given ;-)
Oh, dumb question but was what I did by removing the if statement and leaving the echos what you would have suggested ?
Hi Andy,
I’m not quite sure anymore what you want to do?
If you remove that if-statement, then you show that birthday message every day. On the other hand, if you want to show the elapsed time all the time, then remove just that else-statement.
I got what I wanted I believe, like you said if I remove the if statement the birthday message or in this specific case the years passed will always show. Thats what I wanted to do with the demod linked page. This way the persons age will always show and auto increment with each passing birthday.
http://www.myu2sig.com/interviews/arne_meyer_interview_0-0-0000.php
What I originally asked about with the birthday message is used on my main site myu2sig.com where Happy x B-Day myu2sig.com ! shows up once a year on the sites birthday. Like I said in one of my messages, this new idea which you linked on the demo page is something I came up with the other night.
HELLO!, This datediff() function is awesome! Great Job =D
I have a question, this is the code that i have from here but i modified just instead of giving years, motnhs, seconds.. it displays just the hours and minutes. My question is: How to output 355:14 instead of 355 hours, 14 minutes ?????
This is my code (yours but modified):
function dateDiff($time1, $time2, $precision = 6) {
// If not numeric then convert texts to unix timestamps
if (!is_int($time1)) {
$time1 = strtotime($time1);
}
if (!is_int($time2)) {
$time2 = strtotime($time2);
}
// If time1 is bigger than time2
// Then swap time1 and time2
if ($time1 > $time2) {
$ttime = $time1;
$time1 = $time2;
$time2 = $ttime;
}
// Set up intervals and diffs arrays
$intervals = array(‘hour’,'minute’);
$diffs = array();
// Loop thru all intervals
foreach ($intervals as $interval) {
// Set default diff to 0
$diffs[$interval] = 0;
// Create temp time from time1 and interval
$ttime = strtotime(“+1 ” . $interval, $time1);
// Loop until temp time is smaller than time2
while ($time2 >= $ttime) {
$time1 = $ttime;
$diffs[$interval]++;
// Create new temp time from time1 and interval
$ttime = strtotime(“+1 ” . $interval, $time1);
}
}
$count = 0;
$times = array();
// Loop thru all diffs
foreach ($diffs as $interval => $value) {
// Break if we have needed precission
if ($count >= $precision) {
break;
}
// Add value and interval
// if value is bigger than 0
if ($value > 0) {
// Add s if value is not 1
if ($value != 1) {
$interval .= “s”;
}
// Add value and interval to times array
$times[] = $value . ” ” . $interval;
$count++;
}
}
// Return string with times
return implode(“, “, $times);
}
THANKS IN ADVANCE =D
Hi Daniel,
Nice to hear that you like this function. :)
Replace the code at the end with this:
Let me know if you do not get this to work?
It works THANKS, just 1 thing, when i get just minutes is it possible to get
Example:
00:02 = 2 minutes
or if its hours to get:
02:05
Thanks in advance.. =D
i didn’t explained very well in my last question..
i don’t want to get: “00:02 = 2 minutes”
i want to get: “00:02″
or if its hours to get: “02:05″
Thanks in advance, you are awesome, u solved my first question very fast.
@Daniel
Following should work:
@JR
i have 3 words for you:
YOU ARE AWESOME!
i wish i could be as good as you, i hope some day =D
Many thanks to you.
Thanks for the compliments! :D