【问题标题】:How to create a single expression displaying time difference between two Date's as years, months, days, hours, minutes, seconds如何创建一个表达式,将两个日期之间的时差显示为年、月、日、小时、分钟、秒
【发布时间】:2016-01-07 21:00:38
【问题描述】:

如何创建 单个 jasper 报告 JRExpression,以 yy 年 月 dd 天,hh 小时( s), mm 分, ss 秒

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

JRExpression 是不允许变量声明的单行,但是您可以使用语法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 报告 dependencies(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 部分的一个很好的答案,我可以链接而不是传递我的代码来解决这个问题(我只知道部分解决作为示例)

这是中的一个问题示例:Calculating Time and Date difference

一些参考代码:

Calculate date/time difference in java

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

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

【问题讨论】:

  • 切线地,您如何定义那里的一个月? 2 月 2 日至 3 月 4 日和 3 月 4 日至 4 月 6 日都是“一个月两天”吗?
  • 不错的评论,我更喜欢这样的解决方案,似乎是正确的,这是客户/用户所期望的
  • 我看不出如何在一行中实现这一点,仅仅是因为所需 API 的 JDK 版本不使用允许在一行中使用它们的“Fluent”样式时尚。不能直接把代码放在静态方法里,然后把class文件放到jasper服务器的/lib文件夹下吗?
  • @Affe 否,如果用户(so 上的海报)需要在 ireport 中安装其他库,jasper studio ecc 这将启动一个永无止境的评论线程......但我觉得你可以......注意你可以做新的 GregorianCalendar ecc 你喜欢多少次,我有一个模糊的希望有人知道公共库已经包含,这些里面没有方法??
  • 注意,@Affe 评论后我已经降低了要求 1.8 是允许的,如果这只是解决方案。

标签: jasper-report java date jasper-reports


【解决方案1】:

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

这是一个很大的延伸,但是,正如the dependencies of JasperReports 中列出的那样,org.codehaus.castor:castor-xml:1.3.3 依赖于 commons-lang:commons-lang:2.6。因此,我们可以使用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),后跟一个或多个单词字符,并且可以选择后跟一个空格。
  • 然后,我们可以使用正则表达式"(?<!\\d)1 (\\w+)s"来匹配"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("(?<!\\d)0 (\\w+) ?", "")
    .replaceAll("(?<!\\d)1 (\\w+)s", "1 $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 $1")]]></textFieldExpression>
      </textField>
    </band>
  </title>
</jasperReport>

输出为:


如果上面看起来太脆弱(因为对 commons-lang 的显式依赖,并非所有 JasperReports 版本都存在),there is another possible solution 使用 Java 8 中引入的 Java Time API。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-19
    • 2016-03-22
    • 1970-01-01
    • 2012-05-19
    相关资源
    最近更新 更多