How to format java.time.LocalDateTime and java.time.LocalDate with pattern?
Asked Answered
S

3

7

In the following snipped the property $F is of class java.time.LocalDateTime or java.time.LocalDate.

<textField pattern="EE. dd.MM.yyyy">
    <reportElement...>
    </reportElement>
    <textFieldExpression><![CDATA[$F{theLocalDateTime}]]></textFieldExpression>
</textField>

How can I format this property with textField pattern in jasper reports?

Stannic answered 8/8, 2016 at 13:38 Comment(3)
With a regular formatter like shown here https://mcmap.net/q/628809/-date-formatting-part-of-text-in-jasper-ireports, but for Java 8? Would be DateTimeFormatter.ofPattern(myPattern).format($F{thedatetime}).Sprinkle
@Sprinkle in this case the user like to use pattern that is also preferable when you export to different formats (hence correct format in excel etc), pattern can be used on java.util.Date objects, so the solution is to convert the field to such an class (or subclass) and wait for jasper-reports to update their library to also support the formatting with patterns of the LocalDateTime classCounts
You do not want a LocalDateTime. The java.sql.Timestamp class was replaced by java.time.Instant or OffsetDateTime (with its offset set to UTC). See this Question, What's the difference between Instant and LocalDateTime?.Barbados
C
11

To use the pattern attribute in current version of jasper-report for Date/Time object you need a java.util.Date class or one of it's subclasses.

The solution is to convert java.time.LocalDate and java.time.LocalDateTime

Converting to java.util.Date

from java.time.LocalDate

<textField pattern="EE. dd.MM.yyyy">
    <reportElement...>
    </reportElement>
    <textFieldExpression><![CDATA[java.util.Date.from($F{theLocalDate}.atStartOfDay(java.time.ZoneId.systemDefault()).toInstant())]]></textFieldExpression>
</textField>

from java.time.LocalDateTime

<textField pattern="EE. dd.MM.yyyy">
    <reportElement...>
    </reportElement>
    <textFieldExpression><![CDATA[java.util.Date.from($F{theLocalDateTime}.atZone(java.time.ZoneId.systemDefault()).toInstant())]]></textFieldExpression>
</textField>

Converting to java.sql.Timestamp

from java.time.LocalDate

<textField pattern="EE. dd.MM.yyyy">
    <reportElement...>
    </reportElement>
    <textFieldExpression><![CDATA[java.sql.Timestamp.valueOf($F{theLocalDate}.atStartOfDay())]]></textFieldExpression>
</textField>

from java.time.LocalDateTime

<textField pattern="EE. dd.MM.yyyy">
    <reportElement...>
    </reportElement>
    <textFieldExpression><![CDATA[java.sql.Timestamp.valueOf($F{theLocalDateTime})]]></textFieldExpression>
</textField>

Note: Applying pattern is always preferable solution, specially when exporting to excel since correct class will be passed to poi (hence excel will recognize column as a date and apply same formatting as in pattern)

Counts answered 8/8, 2016 at 14:11 Comment(1)
A Timestamp does not map to a LocalDateTime. Timestamp represents a moment, a specific point on the timeline. A LocalDateTime purposely lacks any concept of time zone or offset-from-UTC. As such it cannot represent a moment. It represents potential moments along a range of about 26-27 hours (the range of time zones around the globe). Timestamp is replaced by the Instant class, or an OffsetDateTime with its offset set to UTC. Hence the java.sql.Timestamp::toInstant method.Barbados
U
2

As java.time module is kind of complex and verbose, I usually create some variables which holds the compiled DateTimeFormatter for further use.

I want my reports to adapt to any Locale, so I don't use string literals for formatting.

I am using the report Locale, but you can choose your Locale with java.util.Locale.forLanguageTag("en-US"), for example.

You can also change the java.time.format.FormatStyle if needed

LocalDate formatter:

    <variable name="dateFormatter" class="java.time.format.DateTimeFormatter">
        <variableExpression><![CDATA[java.time.format.DateTimeFormatter
  .ofLocalizedDate(java.time.format.FormatStyle.SHORT)
  .withLocale($P{REPORT_LOCALE})
  .withChronology(java.time.chrono.Chronology.ofLocale($P{REPORT_LOCALE}))]]></variableExpression>
    </variable>

LocalDateTime formatter:

    <variable name="dateTimeFormatter" class="java.time.format.DateTimeFormatter">
        <variableExpression><![CDATA[java.time.format.DateTimeFormatter
  .ofLocalizedDateTime(java.time.format.FormatStyle.SHORT)
  .withLocale($P{REPORT_LOCALE})
  .withChronology(java.time.chrono.Chronology.ofLocale($P{REPORT_LOCALE}))]]></variableExpression>
    </variable>

Now I can format a LocalDate field this way:

$F{localDateField}.format($V{dateFormatter})

And a LocalDateTime field this way:

$F{localDateTimeField}.format($V{dateTimeFormatter})
Ungodly answered 28/7, 2021 at 10:52 Comment(1)
Thanks - that saved my day! I prefer this solution, because this allows string literals in text fields together with the formatted date, like "formatted date is " + $F{localDateField}.format($V{dateFormatter}).Chide
C
2

I've stumpled over the same problem and I solved it by introducing a custom function to JasperReports. So at the end it can be used as following on LocalDate, LocalTime, LocalDateTime, just anything implementing TemporalAccessor:

FORMAT_DATETIME($F{someLocalDate}, "dd.MM.yyyy")
FORMAT_DATETIME($F{someLocalDateTime}, "dd.MM.yyyy HH:mm")
FORMAT_DATETIME($F{someLocalTime}, "HH:mm")

To achieve that create following files:

Function Category. This is needed so the Expression Editor of the jasper report designer shows the function, under the set category

package org.example.jrfunctions;

import net.sf.jasperreports.functions.annotations.FunctionCategory;

@FunctionCategory()
public class LocalDateTime {
}

Class with the function(s) (more functions could be added)

package org.example.jrfunctions;

import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;

import net.sf.jasperreports.functions.annotations.Function;
import net.sf.jasperreports.functions.annotations.FunctionCategories;
import net.sf.jasperreports.functions.annotations.FunctionParameter;
import net.sf.jasperreports.functions.annotations.FunctionParameters;

@FunctionCategories({
        org.example.jrfunctions.LocalDateTime.class })
public class JRDateTimeFunctions {

    @Function("FORMAT_DATETIME")
    @FunctionParameters({
            @FunctionParameter("temporalObject"),
            @FunctionParameter("format")
            })
    public static String FORMAT_DATETIME(TemporalAccessor temporalObject, String format) {
        if (temporalObject == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return formatter.format(temporalObject);
    }

}

The following file needs to be in the same directory as the above two, and all the properties for every parameter needs to be set. Otherwise the jasper report designer will not show the custom functions in the expression editor:

jasperreports_messages.properties

org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.description = Formats the Temporal Object according to format
org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.name = Format Temporal Accessor (Object)
org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.temporalObject.name = Temporal Accessor
org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.temporalObject.description = Object to format
org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.format.name = Format
org.example.jrfunctions.JRDateTimeFunctions.FORMAT_DATETIME.format.description = Fromat
org.example.jrfunctions.LocalDateTime.name=Local Date & Time
org.example.jrfunctions.LocalDateTime.description=

The following file needs to be in the classpath. I put it into src/main/resources

jasperreports_extension.properties

net.sf.jasperreports.extension.registry.factory.functions=net.sf.jasperreports.functions.FunctionsRegistryFactory
net.sf.jasperreports.extension.functions.jrdatetimefunctions=org.example.jrfunctions.JRDateTimeFunctions

Now the custom function should be shown in the expression editor, and compile without any problems.

More infos:

Candycecandystriped answered 20/4, 2022 at 15:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.