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

How to create a single expression displaying time difference between two Date's as years, months, days, hours, minutes, seconds

我如何创建一个 single jasper 报告 JRExpression 以 yy year(s) month(s) 格式可视化两个 java.util.Date 之间的差异dd 天,hh 小时,mm 分钟,ss 秒

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

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

所需输出的示例(如果您有一个很好的解决方案,请在不存在时删除单元描述并考虑 singular/plural,但如果它是一系列 if 语句,则没有必要):

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

附加要求:

无需将答案格式化为 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 部分的一个很好的答案,我可以 link 而不是在这个问题上传递我的代码(我只知道部分解决作为示例)

这是:

中有问题的例子

部分参考代码:

Calculate date/time difference in java

How to find the duration of difference between two dates in java?

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

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

这是一个非常大的延伸,但是,正如在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)中列出的方法,它存在于commons-lang中。此处使用的格式字符串为

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

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

  • 我们可以使用正则表达式"(?<!\d)0 (\w+) ?" 来删除字符串的所有 0。此正则表达式匹配任何 0、not preceded by a digit(例如,我们不想匹配 10),后跟一个或多个单词字符,并且可选地后跟 space.
  • 然后,我们可以使用正则表达式 "(?<!\d)1 (\w+)s" 来匹配每个出现的 "1 ...s" 并将其替换为 "1 ..." 以获得适当的复数形式。此正则表达式匹配任何 1,前面没有数字,后跟一个或多个以 s 结尾的单词字符(捕获在一组中);它将替换为 "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("(?<!\d)0 (\w+) ?", "")
    .replaceAll("(?<!\d)1 (\w+)s", "1 ")
);

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

在 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 ")]]></textFieldExpression>
      </textField>
    </band>
  </title>
</jasperReport>

输出为:


如果以上内容看起来太脆弱(因为对 commons-lang 的显式依赖并非所有 JasperReports 版本都存在),there is another possible solution using the Java Time API 在 Java 8.

这是可怕的(我不认为有更简单的方法),但输出与上面完全相同,其中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("(?<!\d)0 (\w+) ?", "")
    .replaceAll("(?<!\d)1 (\w+)s", "1 ")
);