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)
如何创建一个jasper report JRExpression,该JRExpression是两个java.util之间的差异。日期格式yy年(s)月日(s), hh小时(s), mm分钟(s), s秒(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();
JRExpression是哪一行不允许变量声明,但是您可以使用语法布尔的条件语句?是的,不熟悉的同学可以想象一下系统的一行。输出。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):
想要输出的例子(如果你有一个很好的解决方案,在不存在的情况下,删除单位的描述,并考虑单数/复数形式,但如果它是一种if语句,这是不必要的):
2 years, 8 months, 12 days, 2 hours, 53 minutes, 10 s
2年,8个月,12天,2小时,53分钟,10秒。
1 hour, 1 minute
1小时1分钟
both Feb 2- Mar 4 and Mar 4 - April 6 are "1 month, 2 days", daylight savings however can be ignored - thanks @Affe
2月2日- 3月4日和4月4日- 4月6日都是“1个月,2天”,但是可以忽略日光节约——谢谢@Affe
Additional requirements:
附加要求:
- Only jasper report dependencies may be used (joda is not included).
- 只能使用jasper报表依赖项(不包括joda)。
- Preferable jdk 1.7 or less (1.8 is accepted if this is only solution)
- 更好的jdk 1.7或更少(如果这是唯一的解决方案,1.8将被接受)
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
不需要将答案格式化为jasper report expression它可以是一个简单的System.out。println代码(我很高兴稍后编辑您的答案,并添加jasper报表代码)。例子
((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)
我在SO的jasper report部分回答了多个问题,这个问题在报告生成中很常见。我希望java部分给出一个好的答案,我可以链接它,而不是在这个问题上传递我的代码(作为示例,我只知道要解决部分问题)
This is an example on question in jasper-report: Calculating Time and Date difference
这是jasper-report中的一个问题示例:计算时间和日期差异
Some reference code:
参考代码:
Calculate date/time difference in java
计算java中的日期/时间差异。
How to find the duration of difference between two dates in java?
如何在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.
对于您熟悉的jasper报告,我不想使用变量声明,因为如果用户需要在参数上使用它,将会使解决方案无效。
1 个解决方案
#1
2
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.
不能直接使用Calendar API来解决这个问题:每个操作都需要多行,因为有趣的方法是无效返回的,不能链接。
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 on commons-lang:commons-lang:2.6
. We therefore can make use of the DurationFormatUtils.formatPeriod(startMillis, endMillis, format)
method, which is present in commons-lang
. The format String to use here would be
这是一个很大的扩展,但是,正如在JasperReports的依赖项中所列,有org.code .castor:castor-xml:1.3.3,它依赖于common -lang:common -lang:2.6。因此,我们可以利用DurationFormatUtils。formatPeriod(startMillis, endMillis, format)方法,它在common -lang中出现。这里要使用的格式字符串是
"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'"
which 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"
).
打印需要的字符串。仍然要小心:这将包括0(如“0个月”),并且会有错误的多元化(如“1个月”)。
- We can use the regular expression
"(?<!\\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. - 我们可以使用正则表达式“(?
- Then, we can use the regular expression
"(?<!\\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:
例子:
System.out.println(
org.apache.commons.lang.time.DurationFormatUtils.formatPeriod(
startDate.getTime(),
endDate.getTime(),
"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'"
)
.replaceAll("(?<!\\d)0 (\\w+) ?", "")
.replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")
);
All this can be done with Java 7 or lower.
所有这些都可以用Java 7或更低的方式完成。
In a JasperReports, this would be an example:
在JasperReports中,这是一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.2.1.final using JasperReports Library version 6.2.1 -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports jasperreports.sourceforge.net/…" name="Blank_A4" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f067f2c4-395f-4669-9fda-4fe81cc59227">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
<parameter name="dateStart" class="java.util.Date" isForPrompting="false">
<defaultValueExpression><![CDATA[new java.util.Date(1)]]></defaultValueExpression>
</parameter>
<parameter name="dateEnd" class="java.util.Date" isForPrompting="false">
<defaultValueExpression><![CDATA[new java.util.Date()]]></defaultValueExpression>
</parameter>
<queryString><![CDATA[]]></queryString>
<title>
<band height="43" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="560" height="30" uuid="cc03531c-2983-4f9a-9619-2826ed92760e"/>
<textFieldExpression><![CDATA[org.apache.commons.lang.time.DurationFormatUtils.formatPeriod($P{dateStart}.getTime(),$P{dateEnd}.getTime(),"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'").replaceAll("(?<!\\d)0 (\\w+) ?", "").replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")]]></textFieldExpression>
</textField>
</band>
</title>
</jasperReport>
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.
如果上面的内容看起来太脆弱(由于对common -lang的显式依赖,不能用于所有JasperReports版本),那么使用Java 8中引入的Java Time API还有另一种可能的解决方案。
This is horrible (I don't think there is a simpler way), but the output is exactly the same as above, where start
and end
are both LocalDateTime
objects:
这很可怕(我不认为有更简单的方法),但是输出结果与上面完全相同,其中start和end都是LocalDateTime对象:
System.out.println((
ChronoUnit.YEARS.between(start, end) + " years " +
ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end) + " months " +
ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end) + " days " +
ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end) + " hours " +
ChronoUnit.MINUTES.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)), end) + " minutes " +
ChronoUnit.SECONDS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)).plusMinutes(ChronoUnit.MINUTES.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)), end)), end) + " seconds"
)
.replaceAll("(?<!\\d)0 (\\w+) ?", "")
.replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")
);
#1
2
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.
不能直接使用Calendar API来解决这个问题:每个操作都需要多行,因为有趣的方法是无效返回的,不能链接。
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 on commons-lang:commons-lang:2.6
. We therefore can make use of the DurationFormatUtils.formatPeriod(startMillis, endMillis, format)
method, which is present in commons-lang
. The format String to use here would be
这是一个很大的扩展,但是,正如在JasperReports的依赖项中所列,有org.code .castor:castor-xml:1.3.3,它依赖于common -lang:common -lang:2.6。因此,我们可以利用DurationFormatUtils。formatPeriod(startMillis, endMillis, format)方法,它在common -lang中出现。这里要使用的格式字符串是
"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'"
which 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"
).
打印需要的字符串。仍然要小心:这将包括0(如“0个月”),并且会有错误的多元化(如“1个月”)。
- We can use the regular expression
"(?<!\\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. - 我们可以使用正则表达式“(?
- Then, we can use the regular expression
"(?<!\\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:
例子:
System.out.println(
org.apache.commons.lang.time.DurationFormatUtils.formatPeriod(
startDate.getTime(),
endDate.getTime(),
"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'"
)
.replaceAll("(?<!\\d)0 (\\w+) ?", "")
.replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")
);
All this can be done with Java 7 or lower.
所有这些都可以用Java 7或更低的方式完成。
In a JasperReports, this would be an example:
在JasperReports中,这是一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.2.1.final using JasperReports Library version 6.2.1 -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports jasperreports.sourceforge.net/…" name="Blank_A4" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="f067f2c4-395f-4669-9fda-4fe81cc59227">
<property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
<parameter name="dateStart" class="java.util.Date" isForPrompting="false">
<defaultValueExpression><![CDATA[new java.util.Date(1)]]></defaultValueExpression>
</parameter>
<parameter name="dateEnd" class="java.util.Date" isForPrompting="false">
<defaultValueExpression><![CDATA[new java.util.Date()]]></defaultValueExpression>
</parameter>
<queryString><![CDATA[]]></queryString>
<title>
<band height="43" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="560" height="30" uuid="cc03531c-2983-4f9a-9619-2826ed92760e"/>
<textFieldExpression><![CDATA[org.apache.commons.lang.time.DurationFormatUtils.formatPeriod($P{dateStart}.getTime(),$P{dateEnd}.getTime(),"y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'").replaceAll("(?<!\\d)0 (\\w+) ?", "").replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")]]></textFieldExpression>
</textField>
</band>
</title>
</jasperReport>
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.
如果上面的内容看起来太脆弱(由于对common -lang的显式依赖,不能用于所有JasperReports版本),那么使用Java 8中引入的Java Time API还有另一种可能的解决方案。
This is horrible (I don't think there is a simpler way), but the output is exactly the same as above, where start
and end
are both LocalDateTime
objects:
这很可怕(我不认为有更简单的方法),但是输出结果与上面完全相同,其中start和end都是LocalDateTime对象:
System.out.println((
ChronoUnit.YEARS.between(start, end) + " years " +
ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end) + " months " +
ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end) + " days " +
ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end) + " hours " +
ChronoUnit.MINUTES.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)), end) + " minutes " +
ChronoUnit.SECONDS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)).plusMinutes(ChronoUnit.MINUTES.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)).plusHours(ChronoUnit.HOURS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)).plusDays(ChronoUnit.DAYS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)).plusMonths(ChronoUnit.MONTHS.between(start.plusYears(ChronoUnit.YEARS.between(start, end)), end)), end)), end)), end)), end) + " seconds"
)
.replaceAll("(?<!\\d)0 (\\w+) ?", "")
.replaceAll("(?<!\\d)1 (\\w+)s", "1 $1")
);