Java8 Date/Time Support For FreeMarker
This project is forked from the original but mostly "dead" project of amedia. See the original project here.
This is a tiny Java library that adds basic support for the new Java 8 date/time api to FreeMarker. It is not a perfect solution as FreeMarker doesn’t support adding custom built-ins. Hopefully FreeMarker will add native support in the future, but there are no implementation being worked on at the moment (http://freemarker.org/contribute.html).
The library has basic formatting support for all classes in the java.time api introduced in Java 8, using the new java.time.format.DateTimeFormatter. We have also introduced some comparison methods.
Take a look at our feature test spec too see how it all works : https://github.com/Munich-Airport/freemarker-java-8/blob/master/src/test/resources/no/api/freemarker/java8/time/
Requirements
Java 8 or higher. Tested on Freemarker 2.3.23, but should at least work fine for all 2.3.x versions. Please file an issue if you have problems with other versions.
Installation
freemarker-java-8 is deployed to the Maven Central Repository. You can include the package in your Maven POM like this :
<dependency>
<groupId>com.munich-airport.freemarker</groupId>
<artifactId>freemarker-java8</artifactId>
<version>2.0.0</version>
</dependency>
Make sure to replace the version with the current version found in the pom.
Usage
You need to configure FreeMarker with our package by adding the Java8ObjectWrapper
.
this.configuration = new Configuration(); // Or get the configuration from your framework like DropWizard or Spring Boot.
final Java8Configuration java8Configuration = Java8Configuration.defaultConfiguration();
// Configure the java8 module
// e.g.: java8Configuration.setTimezoneStrategy(SystemTimezoneStrategy.INSTANCE);
this.configuration.setObjectWrapper(new Java8ObjectWrapper(Configuration.VERSION_2_3_23, java8Configuration));
Usage within Spring
Within a Spring project you can add this configuration class to your project:
import no.api.freemarker.java8.Java8ObjectWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class FreemarkerConfig extends FreeMarkerAutoConfiguration.FreeMarkerWebConfiguration {
@Autowired
private freemarker.template.Configuration configuration;
@PostConstruct
public void postConstruct() {
final Java8Configuration java8Configuration = Java8Configuration.defaultConfiguration();
configuration.setObjectWrapper(
new Java8ObjectWrapper(freemarker.template.Configuration.getVersion(), java8Configuration)); // VERSION_2_3_26
}
}
java.time support
We had to cheat a little bit to add format methods to our date.time classes. This is why you will see that our syntax differs from the default FreeMarker built-ins.
All format methods uses the java.time.format.DateTimeFormatter for formatting. For sake of completion we also added a isEqual(…)
to LocalTime
instances. All bean methods of the java.time classes are still supported.
java.time class | methods | comment | example |
---|---|---|---|
Clock |
|
This is a simple implementation where format just prints the toString() value of the object. |
|
Instant |
|
This is a simple implementation where format just prints the toString() value of the object. |
|
LocalDate |
|
Allows you to print a LocalDate on a default pattern, or by providing a custom pattern or a builtin format style or the static field name of the |
|
LocalDateTime |
|
Allows you to print a LocalDateTime on a default pattern, or by providing a custom pattern or a builtin format style or the static field name of the |
|
LocalTime |
|
Allows you to print a LocalTime on a default pattern, or by providing a custom pattern or a builtin format style or the static field name of the |
|
MonthDay |
|
Allows you to print a MonthDay on a default pattern or by providing a custom pattern. |
|
OffsetDateTime |
|
Allows you to print a OffsetDateTime on a default pattern, or by providing a custom pattern or a builtin format style or the static field name of the |
|
OffsetTime |
|
Allows you to print a OffsetTime on a default pattern, or by providing a custom pattern or a builtin format style or the static field name of the |
|
Year |
|
Allows you to print a Year on a default pattern or by providing a custom pattern. |
|
YearMonth |
|
Allows you to print a YearMonth on a default pattern or by providing a custom pattern. |
|
ZonedDateTime |
|
Allows you to print a ZonedDateTime on a default pattern/timezone (see timezone strategies), or by providing a custom pattern or a builtin format style or the static field name of the |
|
ZoneId |
|
Prints the ZoneId display name. You can override the textstyle with one of these values [FULL, FULL_STANDALONE, SHORT, SHORT_STANDALONE, NARROW and NARROW_STANDALONE]. You can also override the locale, but Java only seems to have locale support for a few languages. |
|
ZoneOffset |
|
Prints the ZoneOffset display name. You can override the textstyle with one of these values [FULL, FULL_STANDALONE, SHORT, SHORT_STANDALONE, NARROW and NARROW_STANDALONE]. You can also override the locale, but Java only seems to have locale support for a few languages. |
|
java.time class | methods | comment | example |
---|---|---|---|
LocalTime |
|
Can compare two LocalTime objects for equality. |
|
pattern |
---|
LONG_DATE |
LONG_DATETIME |
LONG_TIME |
MEDIUM_DATE |
MEDIUM_DATETIME |
MEDIUM_TIME |
SHORT_DATE |
SHORT_DATETIME |
SHORT_TIME |
Timezone Strategies
Prior to version 2.0.0 all ZonedDateTime
instances formatted using format()
or format(pattern)
were translated into the ZoneId
retrieved by Environment.getCurrentEnvironment().getTimezone().toZoneId()
. This leads to unexpected behavior as the purpose of ZonedDateTime
is to have an instant in a specific timezone. Otherwise LocalDateTime
could be used. From version 2.0.0 onwards, this behavior can be changed by providing a Java8Configuration
with a specific TimezoneStrategy
when creating the Java8ObjectWrapper
. Due to backwards compability this behavior is still present when creating a Java8ObjectWrapper
without a Java8Configuration
. The Java8Configuration.defaultConfiguration()
however uses a different TimezoneStrategy
. There are currently 4 builtin strategies:
-
EnvironmentTimezoneStrategy
: Always uses theZoneId
provided by theEnvironment.getCurrentEnvironment()
. This is the default strategy for version < 2.0.0. This strategy is used when creating aJava8ObjectWrapper
without aJava8Configuration
-
KeepingTimezoneStrategy
: Does not change theZoneId
. This is the default strategy used when creating aJava8Configuration.defaultConfiguration()
. -
StaticTimezoneStrategy
: Always uses theZoneId
provided when constructing this strategy. -
SystemTimezoneStrategy
: Always uses theZoneId
provided byZoneId.systemDefault()