How can I create a single jasper report JRExpression that visualize the difference between two java.util.Date
in format yy year(s) month(s) dd day(s), hh hour(s), mm minute(s), ss second(s)
java.util.Date startDate
java.util.Date endDate
A JRExpression is a single line where variable declaration is not allowed, however you may use conditional statements using syntax boolean ? yes:no
, for you who are not familiar imagine one line of System.out.println();
Example of desired output (if you have a nice solution remove description of unit when not present and consider singular/plural but this is not necessary if it's a serie of if statements):
2 years, 8 months, 12 days, 2 hours, 53 minutes, 10 s
1 hour, 1 minute
both Feb 2- Mar 4 and Mar 4 - April 6 are "1 month, 2 days", daylight savings however can be ignored - thanks @Affe
Additional requirements:
- Only jasper report dependencies may be used (joda is not included).
- Preferable jdk 1.7 or less (1.8 is accepted if this is only solution)
There is no need to format the answer as jasper report expression it can be a simple System.out.println
code (I'm happy to edit your answer later to also add the jasper report expression code). Example
((endDate.getTime()-startDate.getTime()) / (60 * 60 * 1000)) % 24 + " hour(s), " +
((endDate.getTime()-startDate.getTime()) / (60 * 1000)) % 60 + " minute(s)"
What have I tried:
I answer multiple question in the jasper report section of SO, and this question is common in report generation. I would prefer a good answer from the java section that I can link rather then passing my code on this issue (that I would only know to solve partially as example)
This is an example on question in jasper-report: Calculating Time and Date difference
Some reference code:
Calculate date/time difference in java
How to find the duration of difference between two dates in java?
For you that are familiar to jasper report I don't want to use the variable declaration since this would invalidate the solution if user need to use it on parameters.
The Calendar API cannot be directly be used for this problem: every operation would require multiple lines since the interesting methods are
void
returning and can't be chained.This is a very big stretch, but, as listed in the dependencies of JasperReports, there is
org.codehaus.castor:castor-xml:1.3.3
which depends itself oncommons-lang:commons-lang:2.6
. We therefore can make use of theDurationFormatUtils.formatPeriod(startMillis, endMillis, format)
method, which is present incommons-lang
. The format String to use here would bewhich would print the wanted String. Care must still be taken: this will include 0s (like
"0 months"
) and will also have incorrect pluralization (like"1 months"
)."(?<!\\d)0 (\\w+) ?"
to remove all the 0s for the String. This regex matches any 0, not preceded by a digit (we don't want to match 10 for example), followed by one or more word characters and optionally followed by a space."(?<!\\d)1 (\\w+)s"
to match every occurence of"1 ...s"
and replace it with"1 ..."
to have proper pluralization. This regular expression matches any 1, not preceded by a digit, followed by one or more word characters (captured in a group) ending with ans
; it would be replaced with"1 $1"
, i.e. 1 followed by the value captured.Example:
All this can be done with Java 7 or lower.
In a JasperReports, this would be an example:
With the output being:
If the above looks too fragile (because of the explicit dependency towards
commons-lang
that could not be there for all JasperReports version), there is another possible solution using the Java Time API introduced in Java 8.This is horrible (I don't think there is a simpler way), but the output is exactly the same as above, where
start
andend
are bothLocalDateTime
objects: