如何创建单个表达式,显示两个日期之间的时差,如年,月,日,小时,分钟,秒

我如何创建一个jasx报告JRExpression ,它可视化两个java.util.Date之间的差异,格式为yy年(s)月(s),hh小时(s),mm分(s),ss秒

 java.util.Date startDate java.util.Date endDate 

JRExpression是一行不允许变量声明的行,但是你可以使用语法boolean ? yes:no条件语句boolean ? yes:no boolean ? yes:no ,对于你不熟悉的人想象一下System.out.println();

所需输出的示例(如果你有一个很好的解决方案,当不存在时删除单位的描述并考虑单数/复数但如果它是一系列if语句则不是必需的):

2年,8个月,12天,2小时,53分钟,10秒

1小时1分钟

2月2日至3月4日和3月4日 – 4月6日都是“1个月,2天”,夏令时可以忽略 – 感谢@Affe

其他要求:

  • 只能使用jasper报告依赖项 (不包括joda)。
  • 优选的jdk 1.7或更低(如果这只是解决方案,则接受1.8)

没有必要将答案格式化为jasper报表,它可以是一个简单的System.out.println代码(我很乐意稍后编辑你的答案,也可以添加jasper报表表达式代码)。 例

 ((endDate.getTime()-startDate.getTime()) / (60 * 60 * 1000)) % 24 + " hour(s), " + ((endDate.getTime()-startDate.getTime()) / (60 * 1000)) % 60 + " minute(s)" 

我试过了什么:

我在SO的jasper报告部分回答了多个问题,这个问题在报告生成中很常见。 我更喜欢java部分的一个很好的答案,我可以链接而不是在这个问题上传递我的代码(我只知道部分解决为例)

这是jasper-report中的一个问题示例: 计算时间和日期差异

一些参考代码:

在java中计算日期/时间差异

如何在java中找到两个日期之间的差异持续时间?

对于jasper报告中熟悉的人,我不想使用变量声明,因为如果用户需要在参数上使用它,这将使解决方案无效。

Calendar API不能直接用于此问题:每个操作都需要多行,因为有趣的方法返回void并且无法链接。

这是一个非常大的延伸,但是,如JasperReports的依赖关系中所列,有org.codehaus.castor:castor-xml:1.3.3 ,它取决于commons-lang:commons-lang:2.6 。 因此,我们可以使用在commons-lang存在的DurationFormatUtils.formatPeriod(startMillis, endMillis, format)方法。 这里使用的格式字符串是

 "y' years 'M' months 'd' days 'H' hours 'm' minutes 's' seconds'" 

这将打印想要的字符串。 必须小心:这将包括0(如"0 months" ),并且也会有不正确的复数(如"1 months" )。

  • 我们可以使用正则表达式"(? 删除String的所有0。 此正则表达式匹配任何0, 不以数字开头 (例如,我们不想匹配10),后跟一个或多个单词字符,并可选地后跟空格。
  • 然后,我们可以使用正则表达式"(?匹配"1 ...s"每个出现,并将其替换为"1 ..."以使其正确多元化。 此正则表达式匹配任何1,不以数字开头,后跟一个以s结尾的一个或多个单词字符(在组中捕获); 它将被替换为"1 $1" ,即1后跟捕获的值。

例:

 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("(? 

所有这些都可以使用Java 7或更低版​​本完成。

在JasperReports中,这将是一个例子:

             <band height="43" splitType="Stretch"> <textfield> <reportelement x="0" y="0" width="560" height="30" uuid="cc03531c-2983-4f9a-9619-2826ed92760e"></reportelement> <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>   

输出为:

在此处输入图像描述


如果上面看起来太脆弱了(因为明显依赖于commons-lang而不是所有JasperReports版本都存在 ), 那么使用Java 8中引入的Java Time API 还有另一种可能的解决方案 。

这很糟糕 (我认为没有更简单的方法),但输出与上面完全相同,其中startend都是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("(?