Search code examples
javadatedatetime-formatdynamic-reportszoneddatetime

DynamicReports doesn't apply pattern to format ZonedDateTime


I have a model class that contains field of type ZonedDateTime. When generate report data is shown as:

2017-08-17T16:09:03+03:00[Europe/Chisinau]

To format that date I use method setPattern("dd.MM.yyyy") but nothing changes.

All stuff is generated via reflection, check source:

for (Field field : entityClass.getDeclaredFields()) {
    String fieldName = field.getName();
    if (usedFields.contains(fieldName)) {
        field.setAccessible(true);
        if(field.getType().isAssignableFrom(Date.class) || field.getType().isAssignableFrom(ZonedDateTime.class)){
            report.addColumn(Columns.column(fieldName, fieldName, field.getType()).setPattern("dd.MM.yyyy"));
        }else {
            report.addColumn(Columns.column(fieldName, fieldName, field.getType()));
        }
    }
}

Generate report where list is a generic list of elements.

report
.title(
        Components.text("Documents Report")
        .setStyle(getHeaderCenteredBoldStyle())
        .setHorizontalTextAlignment(HorizontalTextAlignment.CENTER))
        .pageFooter(Components.pageXofY())
.setColumnTitleStyle(getColumnTitleStyle())
.highlightDetailEvenRows()
.setDataSource(list);

Generated PDF

generated pdf Any ideas?


Solution

  • It seems that setPattern works fine for java.util.Date, but not for java.time.ZonedDateTime. I've created a simple entity:

    public class Entity {
    
        private ZonedDateTime zonedDateTime = ZonedDateTime.parse("2017-08-17T16:09:03+03:00[Europe/Chisinau]");
    
        private Date date = new Date();
    
        // getters and setters
    }
    

    Then I've used your code (for (Field field : entityClass.getDeclaredFields()) etc) to add the columns. For the data source list, I've created it manually, just to test:

    DRDataSource ds = new DRDataSource("zonedDateTime", "date");
    ds.add(entity.getZonedDateTime(), entity.getDate());
    

    In the resulting report, the ZonedDateTime wasn't formatted (it's shown as 2017-08-17T16:09:03+03:00[Europe/Chisinau]), but the Date was correctly displayed as 21.08.2017.

    One way to fix this is to set a value formatter and use a java.time.format.DateTimeFormatter to convert the ZonedDateTime to a String:

    if (field.getType().isAssignableFrom(ZonedDateTime.class)) {
        // ZonedDateTime, use a value formatter
        report.addColumn(Columns.column(fieldName, fieldName, ZonedDateTime.class)
            // set a custom value formatter
            .setValueFormatter(new AbstractValueFormatter<String, ZonedDateTime>() {
                @Override
                public String format(ZonedDateTime value, ReportParameters reportParameters) {
                    // convert ZonedDateTime to dd.MM.yyyy format
                    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd.MM.yyyy");
                    return value.format(fmt);
                }
            }));
    } else if (field.getType().isAssignableFrom(Date.class)) {
        // java.util.Date: setPattern works
        report.addColumn(Columns.column(fieldName, fieldName, field.getType()).setPattern("dd.MM.yyyy"));
    ...
    

    With this, the ZonedDateTime is displayed with the format defined in the DateTimeFormatter:

    17.08.2017

    In this example I'm creating the DateTimeFormatter inside the inner class, but it's better to create just one formatter outside of the if (it can be in a static final field, as it's immutable and thread-safe) and reuse it accross your application.


    Another alternative is to manually format the ZonedDateTime to a String, using the same java.time.format.DateTimeFormatter, but in the data source list. I also had to change the type of the column to String (inside the for):

    if (field.getType().isAssignableFrom(ZonedDateTime.class)) {
        // ZonedDateTime, manually convert to String
        report.addColumn(Columns.column(fieldName, fieldName, String.class));
    } else if (field.getType().isAssignableFrom(Date.class)) {
        // java.util.Date: setPattern works
        report.addColumn(Columns.column(fieldName, fieldName, field.getType()).setPattern("dd.MM.yyyy"));
    } else {
        report.addColumn(Columns.column(fieldName, fieldName, field.getType()));
    }
    

    Then, in the data source list, I had to use the DateTimeFormatter to format the ZonedDateTime to a String:

    // create formatter
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd.MM.yyyy");
    
    // format the ZonedDateTime
    ds.add(entity.getZonedDateTime().format(fmt), entity.getDate());
    

    With this, the ZonedDateTime is now shown as 17.08.2017.

    I'm not sure how you're creating the list of values, but you'll have to change it to format the ZonedDateTime in the way described above (or do it internally in the entity).