如何创建一个单独的表达式来显示两个日期之间的时间差异,比如年、月、天、小时、分钟、秒

时间:2022-01-27 16:26:52

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 : 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 an s; 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 an s; 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")
);