{"id":17132832,"url":"https://github.com/lazee/freemarker-java-8","last_synced_at":"2025-04-05T07:03:37.678Z","repository":{"id":36676747,"uuid":"40983169","full_name":"lazee/freemarker-java-8","owner":"lazee","description":"Library that adds java.time support to FreeMarker templates.","archived":false,"fork":false,"pushed_at":"2025-03-17T04:59:47.000Z","size":201,"stargazers_count":55,"open_issues_count":1,"forks_count":15,"subscribers_count":62,"default_branch":"master","last_synced_at":"2025-03-29T06:03:47.966Z","etag":null,"topics":["freemarker","java","java-8","maven","open-source"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"chongshengB/Padavan-build","license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lazee.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-18T16:25:24.000Z","updated_at":"2025-02-22T19:43:17.000Z","dependencies_parsed_at":"2025-01-05T02:08:53.727Z","dependency_job_id":"16b54f45-d5ff-404b-8069-3a6fb54a9dca","html_url":"https://github.com/lazee/freemarker-java-8","commit_stats":{"total_commits":182,"total_committers":8,"mean_commits":22.75,"dds":0.6043956043956045,"last_synced_commit":"cedef433c128f2b7167d645ee1565bd8b100c78f"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazee%2Ffreemarker-java-8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazee%2Ffreemarker-java-8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazee%2Ffreemarker-java-8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lazee%2Ffreemarker-java-8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lazee","download_url":"https://codeload.github.com/lazee/freemarker-java-8/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299830,"owners_count":20916190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["freemarker","java","java-8","maven","open-source"],"created_at":"2024-10-14T19:28:31.229Z","updated_at":"2025-04-05T07:03:37.650Z","avatar_url":"https://github.com/lazee.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Extending FreeMarker with Java Date and Time API Support\n\n** NOTE: For some reason the lateste 3.0.2 release is not available in the Maven repositories. Not sure why. I will release 3.0.3 to see if that goes through.**\n\n![build status](https://github.com/lazee/freemarker-java-8/workflows/Build%20project/badge.svg)\n\n**freemarker-java8** is a Java library that extends FreeMarker by adding support for the `java.time` API.\nIt’s straightforward to integrate into your codebase and easy to use.\n\nDespite the name, the library is compatible with Java 8 and all newer versions.\n\nWhile the library is rarely updated, it’s still maintained and supported. If you have any questions or issues,\nplease don’t hesitate to open an issue. We also test on new versions and distributions of Java, regularly.\n\nThis library enables you to format and display values from `java.time` classes directly within FreeMarker\ntemplates. Additionally, it offers useful comparison and conversion methods to enhance your template logic.\n\nWhile it provides valuable functionality, it isn't a perfect solution due to FreeMarker's lack of support\nfor custom built-ins. Hopefully, future versions of FreeMarker will include native support, but according to\ntheir [contribution guidelines](http://freemarker.org/contribute.html), this seems unlikely.\n\n## Table of content\n\n- [Installation](#installation)\n- [Setup](#setup)\n- [Upgrade guides](#upgrade-guides)\n- [Usage](#usage)\n  - [Formatting](#formatting)\n    - [About pattern](#about-pattern)\n    - [About zone](#about-zone)\n    - [java.time.Clock](#user-content-ballot_box_with_check-javatimeclock)\n    - [java.time.Duration](#user-content-ballot_box_with_check-javatimeduration)\n    - [java.time.Instant](#user-content-ballot_box_with_check-javatimeinstant)\n    - [java.time.LocalDate](#user-content-ballot_box_with_check-javatimelocaldate)\n    - [java.time.LocalDateTime](#user-content-ballot_box_with_check-javatimelocaldatetime)\n    - [java.time.LocalTime](#user-content-ballot_box_with_check-javatimelocaltime)\n    - [java.time.MonthDay](#user-content-ballot_box_with_check-javatimemonthday)\n    - [java.time.OffsetDateTime](#user-content-ballot_box_with_check-javatimeoffsetdatetime)\n    - [java.time.OffsetTime](#user-content-ballot_box_with_check-javatimeoffsettime)\n    - [java.time.Period](#user-content-ballot_box_with_check-javatimeperiod)\n    - [java.time.Year](#user-content-ballot_box_with_check-javatimeyear)\n    - [java.time.YearMonth](#user-content-ballot_box_with_check-javatimeyearmonth)\n    - [java.time.ZonedDateTime](#user-content-ballot_box_with_check-javatimezoneddatetime)\n    - [java.time.ZonedId](#user-content-ballot_box_with_check-javatimezonedid)\n    - [java.time.ZonedOffset](#user-content-ballot_box_with_check-javatimezonedoffset)\n  - [Comparison](#comparison)\n    - [java.time.LocalDate](#user-content-ballot_box_with_check-javatimelocaldate-1)\n    - [java.time.LocalDateTime](#user-content-ballot_box_with_check-javatimelocaldatetime-1)\n    - [java.time.LocalTime](#user-content-ballot_box_with_check-javatimelocaltime-1)\n\n## Installation\n\nYou need Java 8 or higher. Tested on Freemarker 2.3.34, and should at least work\nfine for all 2.3.x versions.\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eno.api.freemarker\u003c/groupId\u003e\n    \u003cartifactId\u003efreemarker-java8\u003c/artifactId\u003e\n    \u003cversion\u003e3.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```gradle\nimplementation 'no.api.freemarker:freemarker-java8:3.0.2'\n```\n\n## Setup\n\n*freemarker-java8* extends the [DefaultObjectWrapper](https://freemarker.apache.org/docs/api/freemarker/template/DefaultObjectWrapper.html) \nto add support for the java.time classes. All you need to do is to replace\nthe default object wrapper with the *freemarker-java8* implementation in your FreeMarker Configuration object.\n\n```java\nthis.configuration = new Configuration(); // Or get the configuration from your framework like DropWizard or Spring Boot.\nthis.configuration.setObjectWrapper(new Java8ObjectWrapper(Configuration.VERSION_2_3_34));\n```\n\n### Spring setup\n\nThis is how you can add *freemarker-java8* to your FreeMarker configuration in Spring / Spring Boot.\n\n```java\npackage com.example.demo;\n\nimport no.api.freemarker.java8.Java8ObjectWrapper;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;\n\n@Configuration\npublic class FreemarkerConfig implements BeanPostProcessor {\n\n    @Override\n    public Object postProcessAfterInitialization(Object bean, String beanName)\n            throws BeansException {\n        if (bean instanceof FreeMarkerConfigurer) {\n            FreeMarkerConfigurer configurer = (FreeMarkerConfigurer) bean;\n            configurer.getConfiguration().setObjectWrapper(new Java8ObjectWrapper(freemarker.template.Configuration.getVersion()));\n        }\n        return bean;\n    }\n}\n```\n\nYou can also configure it via Spring boot properties like this:\n\n```java\nspring.freemarker.settings.object_wrapper=no.api.freemarker.java8.Java8ObjectWrapper(Configuration.VERSION_2_3_34)\n```\n\nThis takes advantage of Freemarker Configuration [object builder expressions](https://freemarker.apache.org/docs/api/freemarker/template/Configuration.html#fm_obe)\n\n## Upgrade guides\n\nUpgrade information has moved to [UPGRADE.md](UPGRADE.md). Also look at [CHANGELOG.md](CHANGELOG.md) for changes in new versions.\n\n## Usage\n\n### Formatting java.time classes\n\nAll format methods uses the\n[java.time.format.DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)\nfor formatting.\n\n#### About pattern\n\nInstead of customizing your own pattern in the format methods, you can use one of the predefined styles from:\n\n- [`java.text.format.DateFormat`](https://docs.oracle.com/javase/tutorial/i18n/format/dateFormat.html) (DEFAULT, SHORT, MEDIUM, LONG, FULL)\n- [`java.time.format.DateTimeFormatter`](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) (Eg: ISO_OFFSET_DATE_TIME, follow link for more)\n- Custom styles shipped with this library\n  - LONG_DATE\n  - LONG_DATETIME\n  - LONG_TIME\n  - MEDIUM_DATE\n  - MEDIUM_DATETIME\n  - MEDIUM_TIME\n  - SHORT_DATE\n  - SHORT_DATETIME\n  - SHORT_TIME\n\n##### Examples\n\n```freemarker\n${mydate.format('LONG_DATE')}\n${mydate.format('ISO_OFFSET_DATE_TIME')}\n${mydate.format('yyyy-MM-dd Z')}\n```\n\nWhen no pattern are given, each java.time class has a default pattern. This default can be overridden in the static\n`DefaultFormatters` class, where each class has its own setter method:\n\n```java\nno.api.freemarker.java8.time.DefaultFormatters.setClockFormatter(DateTimeFormatter.ofPattern('yyyy MM dd HH:mm:ss'))\n```\n\n#### About zone\n\nWhen a method takes `zone` as argument, it must be a valid [Java `ZoneId`](https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html), like `Europe/Oslo` or `-07:00`.\n\nThe `zone` argument is only supported for the `java.time` classes where it makes sense.\nWhen a zone is *not* explicitly given as argument, the formatter will by default use the zone found in the object itself, if it exists. If not, it will pick one based on the active zone strategy.\n\nTh default behaviour can be changed if you like. Sometimes you might want all dates and times\nto be converted into your local timezone.\n\n`Java8ObjectMapper`now a second argument where you can choose one of four strategies for the time zone used when formatting a ZonedDateTime:\n\n- **KeepingZoneStrategy** - (DEFAULT) Will use the zone from the objects themself. For objects without zone information, ZoneId.systemDefault() is used.\n- **EnviromentZoneStrategy** - Will use the same time zone as Freemarker itself.\n- **SystemZoneStrategy** - Will convert the time zone into ZoneId.systemDefault().\n- **StaticZoneStrategy** - Allows you to explicitly set a ZoneId.\n\n##### Examples\n\n```java\nthis.objectWrapper = new Java8ObjectWrapper(VERSION_2_3_34, new KeepingZoneStrategy());\nthis.objectWrapper = new Java8ObjectWrapper(VERSION_2_3_34, new StaticZoneStrategy(ZoneId.of(\"Europe/Oslo\")));\n```\n\n#### :ballot_box_with_check: java.time.Clock\n\nMethods for formatting `java.time.Clock`.\n\n##### Methods\n\n- format()\n- format(pattern)\n- format(pattern, zone)\n\nWhen no *pattern* is specified, `ISO_LOCAL_DATE_TIME` is used.\n\n##### Examples\n\n```freemarker\n${myclock.format()}\n${myclock.format('yyyy-MM-dd')}\n${myclock.format('yyyy-MM-dd Z', 'Asia/Seoul')}\n```\n\n#### java.time.Duration\n\nProvided access to a `java.time.Duration` object values.\n\n##### Methods\n\n- nano()\n- seconds()\n\n##### Examples\n\n```freemarker\n${myduration.seconds}\n${myduration.nano}\n```\n\n#### :ballot_box_with_check: java.time.Instant\n\nMethods for formatting `java.time.Instant`.\n\n##### Methods\n\n- format()\n- format(pattern)\n- format(pattern, zone)\n\nWhen no *pattern* is specified, `ISO_LOCAL_DATE_TIME` is used.\n\n##### Examples\n\n```freemarker\n${myinstant.format()}\n${myzoneddatetime.format('yyyy-MM-dd')}\n${myzoneddatetime.format('yyyy-MM-dd Z', 'Asia/Seoul')}\n```\n\n#### :ballot_box_with_check: java.time.LocalDate\n\nMethods for formatting `java.time.LocalDate`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${mylocaldate.format()}\n${mylocaldate.format('yyyy MM dd')}\n${mylocaldate.format('FULL_DATE')}\n```\n\n#### :ballot_box_with_check: java.time.LocalDateTime\n\nMethods for formatting `java.time.LocalDateTime`. We also have methods for converting\na `LocalDateTime` into a `ZoneDateTime`.\n\n##### Methods\n\n- format()\n- format(pattern)\n- format(pattern)\n- toZonedDateTime()\n- toZonedDateTime(zone)\n\n##### Examples\n\n```freemarker\n${mylocaldatetime.format()}\n${mylocaldatetime.format('yyyy-MM-dd HH : mm : ss')}\n${mylocaldatetime.format('MEDIUM_DATETIME')}\n${mylocaldatetime.toZonedDateTime()}\n${mylocaldatetime.toZonedDateTime('Asia/Seoul')}\n${mylocaldatetime.toZonedDateTime('Asia/Seoul')}.format('yyyy-MM-dd HH : mm : ss Z')}\n```\n\nWhen no *pattern* is specified, `ISO_LOCAL_DATE_TIME` is used.\n\nWhen no `zone` are given to `toZoneDateTime`, the ZoneStrategy are used to pick one for you.\n\n#### :ballot_box_with_check: java.time.LocalTime\n\nMethods for formatting `java.time.LocalTime`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${mylocaltime.format()}\n${mylocaltime.format('HH : mm : ss')}\n${mylocaltime.format('SHORT_TIME')}\n```\n\nWhen no *pattern* is specified, `ISO_LOCAL_TIME` is used.\n\n#### :ballot_box_with_check: java.time.MonthDay\n\nMethods for formatting `java.time.MonthDay`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${mymonthday.format()}\n${mymonthday.format('MM dd')}\n```\n\nWhen no *pattern* is specified, `MM:dd` is used.\n\n#### :ballot_box_with_check: java.time.OffsetDateTime\n\nMethods for formatting `java.time.OffsetDateTime`.\n\n##### Methods\n\n- format()\n- format(pattern)\n- format(pattern, zone)\n\nWhen no *pattern* is specified, `ISO_OFFSET_DATE_TIME` is used.\n\n*Uses the normalized ZoneId from the Offset `myOffsetDateTime.getOffset().normalized()`*\n\n##### Examples\n\n```freemarker\n${myoffsetdatetime.format()}\n${myoffsetdatetime.format('yyyy MM dd HH mm ss')}\n${myoffsetdatetime.format('FULL_DATETIME')}\n${myoffsetdatetime.format('yyyy MM dd HH mm ss Z', 'Asia/Seoul')}\n```\n\n#### :ballot_box_with_check: java.time.OffsetTime\n\nMethods for formatting `java.time.OffsetTime`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${myoffsettime.format()}\n${myoffsettime.format('HH mm ss')}\n${myoffsettime.format('MEDIUM_TIME')}\n```\n\nWhen no *pattern* is specified, `ISO_OFFSET_TIME` is used.\n\n#### :ballot_box_with_check: java.time.Period\n\nProvides access to the values of the a Period object within your template.\n\n##### Methods\n\n- days()\n- months()\n- years()\n\n##### Examples\n\n```freemarker\n${myperiod.days}\n${myperiod.months}\n${myperiod.years}\n```\n\n#### :ballot_box_with_check: java.time.Year\n\nMethods for formatting `java.time.Year`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${myyear.format()}\n${myyear.format('yyyy')}\n```\n\nWhen no *pattern* is specified, `yyyy` is used.\n\n#### :ballot_box_with_check: java.time.YearMonth\n\nMethods for formatting `java.time.YearMonth`.\n\n##### Methods\n\n- format()\n- format(pattern)\n\n##### Examples\n\n```freemarker\n${myyear.format()}\n${myyear.format('yyyy MM')}\n```\n\nWhen no *pattern* is specified, `yyyy-MM` is used.\n\n#### :ballot_box_with_check: java.time.ZonedDateTime\n\nMethods for formatting `java.time.ZonedDateTime`.\n\n##### Methods\n\n- format()\n- format(pattern)\n- format(pattern, zone)\n\n##### Examples\n\n```freemarker\n${myzoneddatetime.format()}\n${myzoneddatetime.format('yyyy-MM-dd HH mm s Z')}\n${myzoneddatetime.format('yyyy-MM-dd HH mm s Z', 'Asia/Seoul')}\n```\n\nExample:\n\n```java\nnew Java8ObjectWrapper(VERSION_2_3_34, new EnvironmentZoneStrategy());\n// or\nnew Java8ObjectWrapper(VERSION_2_3_34, new StaticZoneStrategy(ZoneId.of(\"Europe/Oslo\")));\n```\n\n#### :ballot_box_with_check: java.time.ZonedId\n\nMethods for formatting ZoneId display name.\n\nYou can override the textstyle with one of these values:\n\n- FULL\n- FULL_STANDALONE\n- SHORT\n- SHORT_STANDALONE\n- NARROW\n- NARROW_STANDALONE\n\nYou can also override the locale, but Java only seems to have locale support for a few languages.\n\n##### Methods\n\n- format()\n- format(textStyle)\n- format(textstyle, locale)\n\n#### Example\n\n```freemarker\n${myzoneid.format()}\n${myzoneid.format('short')}\n${myzoneid.format('short', 'no-NO')}\n```\n\n#### :ballot_box_with_check: java.time.ZonedOffset\n\nMethods for formatting the ZoneOffset display name. You can override the textstyle with one of these values:\n\n- FULL\n- FULL_STANDALONE\n- SHORT\n- SHORT_STANDALONE\n- NARROW\n- NARROW_STANDALONE\n \n You can also override the locale, but Java only seems to have locale support for a few languages.\n\n##### Methods\n\n- format()\n- format(textStyle)\n\n##### Examples\n\n```freemarker\n${myzoneoffset.format()}\n${myzoneoffset.format('short')}\n```\n\n### Comparison\n\n#### :ballot_box_with_check: java.time.LocalDate\n\nCan compare two LocalDate objects for equality.\n\n##### Methods\n\n- isEqual(localDate)\n- isAfter(localDate)\n- isBefore(localDate)\n\n##### Examples\n\n```freemarker\n${localDate.isEqual(anotherlocalDate)}\n${localDate.isAfter(anotherlocalDate)}\n${localDate.isBefore(anotherlocalDate)}\n```\n\n#### :ballot_box_with_check: java.time.LocalDateTime\n\nCan compare two LocalDateTime objects for equality.\n\n##### Methods\n\n- isEqual(localDateTime)\n- isAfter(localDateTime)\n- isBefore(localDateTime)\n\n##### Examples\n\n```freemarker\n${localDateTime.isEqual(anotherlocalDateTime)}\n${localDateTime.isAfter(anotherlocalDateTime)}\n${localDateTime.isBefore(anotherlocalDateTime)}\n```\n\n#### :ballot_box_with_check: java.time.LocalTime\n\nCan compare two LocalTime objects for equality.\n\n##### Methods\n\n- isEqual(localDateTime)\n- isAfter(localDateTime)\n- isBefore(localDateTime)\n\n##### Examples\n\n```freemarker\n${localTime.isEqual(anotherlocalTime)}\n${localTime.isAfter(anotherlocalTime)}\n${localTime.isBefore(anotherlocalTime)}\n```\n\n### Manipulating time\n\n#### :ballot_box_with_check: java.time.temporal.Temporal\n\nCan create a new Temporal object with specified time difference from the original object, supporting\n\n```freemarker\njava.time.Instant, \njava.time.LocalDate, \njava.time.LocalDateTime, \njava.time.LocalTime, \njava.time.OffsetDateTime, \njava.time.OffsetTime, \njava.time.Year, \njava.time.YearMonth, \njava.time.ZonedDateTime\n```\n\n##### Methods\n\n- plusSeconds(long)\n- plusMinutes(long)\n- plusDays(long)\n- plusWeeks(long)\n- plusMonths(long)\n- plusYears(lone)\n\n##### Examples\n\n```freemarker\n${localDateTime.plusMonths(1).plus.Hours(-2).plusMinutes(5).plusSeconds(30).format()}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flazee%2Ffreemarker-java-8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flazee%2Ffreemarker-java-8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flazee%2Ffreemarker-java-8/lists"}