views:

1384

answers:

8

We're using MySQL to store some dates. I'm looking to show these as relative time periods in the user interface: 2 hours ago, 3 days ago etc. (Like Twitter does for the updates for example)

Is there a well-known way to accomplish this, or should I get creative with it?

Just to be clear, I want to transform: 07/26/2009 12:20 -> '2 days ago'

+1  A: 

I think the most common solution is to convert it to unix timestamps (or equivalent, milliseconds in Java normally), take the difference and start dividing away.

time = now - then;
time /= 1000; /* if milliseconds... */
seconds = time % 60; time /= 60;
minutes = time % 60; time /= 60;
hours   = time % 60; time /= 60;
days    = time % 24; time /= 24;
weeks   = time % 7; time /= 7;

or months (although, then it starts to get tricky...), or whatever you want to use.

Have fun.

roe
Even with days, it's tricky because due to daylight savings time, there are days that have 23 or 25 hours.
Michael Borgwardt
@Michael Borgwardt - factoring in leap seconds and the like causes the problem to become considerably more tricky.
Nick Holt
theoretically, you're all of course correct.. It's probably sufficient for most uses though, and it's not very error prone.
roe
No, I'd say that it's insufficient for many uses and very error prone - the kind of nasty error that sleeps for years before rearing its ugly head, when for the first time you have a calculation straddling the daylight savings switch, get a result that is 1 hour off, which gets rounded so that it's 1 day off, which results in a division by zero and the universe collapsing.
Michael Borgwardt
it is insufficient for some uses, definitely, but I'd wager a bet that 'most' uses for this kind of calculation are for non mission critical UI display, in which case one hour twice a year is not a huge deal (even if it turns 23 hours into a day, which will happen in an hour anyway, so it's just within that span). With not very error prone, I meant implementation bugs rather than conceptual bugs. For this kind of use, 30 days is usually good enough for 'months' as well. I know, i know, 'good enough' is a dirty expression around here... :)
roe
+1  A: 

My advice is run your server in UTC and then use JodaTime to do any date arithmetic or conversion between timezones.

There's considerably more to date arithmetic than meets the eye once you factor in things like DSL, leap-seconds, convention changes, etc and it's really the last thing you want to be doing yourself.

Nick Holt
+1  A: 

The question is rather vague.

In Java, using JodaTime Date API;

3 days ago:

DateTime date = new DateTime();
DateTime threeDA = date.plusDays(-3);
int daysBetween = Days.daysBetween(dbDate, threeDA).getDays();
int monthsBetween = Months.monthsBetween(dbDate, threeDA).getMonths();

or you could use the JodaTime Period/Duration objects.

In MySQL, use a built-in MySQL Date Function e.g.:

SELECT SUBTIME(SYSDATE(),'3'); -- untested, no MySQL to hand
SELECT SUBTIME('2007-12-31 23:59:59.999999','3 0:0:0.000000');

For date differences:

SELECT DATEDIFF(columnname, SYSDATE()); -- Days since
SELECT TIMEDIFF(columnname, SYSDATE()); -- Time since

In Java, using Gregorian Calendar:

GregorianCalendar threeDA = new GregorianCalendar();
threeDA.add(GregorianCalendar.DAY_OF_YEAR, -3);
JeeBee
How does this convert a time into a '3 days ago'? Turning 'three days ago' into a time is a different question, or did I misunderstand something here?
roe
The original question is hard to understand ("transform these dates in their relative equivalent" is meaningless). Does he actually want to know the difference between two dates, one of which is from the DB, and the other one is the "3 days ago" date?
JeeBee
I edited my original posting. I meant:I have an absolute date in the DB which I want to show as "4 days ago" , "2 hours ago" etc. in the user interface.
Dan
A: 

If you can use java for the conversion, have a look at the Joda library.

kgiannakakis
+2  A: 

I would take a look at the Joda library for performing this type of date-time arithmetic. For example, you could create a Joda Duration and then convert it to a Period, giving you access to numerous useful methods:

ResultSet rs = ...
Date dbDate = rs.getDate("Date"); // Get stored time in database.
long serverTime = System.currentTimeMillis(); // Get current server time.

// Compute absolute difference between two time-stamps.
Duration duration = new Duration(Math.abs(serverTime - dbDate.getTime()));

// Convert to period and make use of getHours(), getMinutes(), etc for display purposes.
Period period = duration.toPeriod();

System.err.println("Hours: " + period.getHours());
System.err.println("Minutes: " + period.getMinutes()); // etc.
Adamski
A: 

On the MySQL side:

CONVERT('date-time-value-here', DATETIME) - NOW()
caf
+1  A: 

The Java standard API method for date calculations is Calendar.add() (which also takes negative parameters).

Michael Borgwardt
Same question as I asked JeeBee, how do you use this to turn a time into '3 days ago'..?
roe
@roe: Calendar.add(Calendar.DAY_OF_MONTH, -3) should work. It is even used as an example in the javadocs (using -5 there though)
Fredrik
+3  A: 

As I understand your problem, the "Human Time" class is a solution.

check Date Formatting and Parsing for Humans in Java with HumanTime .

fg