├── .gitattributes ├── .gitignore ├── BUILD.txt ├── CONTRIBUTING.markdown ├── ChangeLog.txt ├── LICENSE.txt ├── NOTICE.txt ├── README.markdown ├── pom.xml └── src ├── assembly └── dist.xml ├── main ├── java │ └── org │ │ └── thymeleaf │ │ └── extras │ │ └── java8time │ │ ├── dialect │ │ ├── Java8TimeDialect.java │ │ └── Java8TimeExpressionFactory.java │ │ ├── expression │ │ └── Temporals.java │ │ └── util │ │ ├── TemporalArrayUtils.java │ │ ├── TemporalCreationUtils.java │ │ ├── TemporalFormattingUtils.java │ │ ├── TemporalListUtils.java │ │ ├── TemporalObjects.java │ │ └── TemporalSetUtils.java └── resources │ ├── org │ └── thymeleaf │ │ └── extras │ │ └── java8time │ │ └── Java8Time.xml │ └── temporal.png └── test ├── java └── org │ └── thymeleaf │ └── extras │ └── java8time │ └── expression │ ├── TemporalsArrayTest.java │ ├── TemporalsClassesFormattingTest.java │ ├── TemporalsCreationTest.java │ ├── TemporalsFormattingTest.java │ ├── TemporalsListTest.java │ └── TemporalsSetTest.java └── resources ├── simplelogger.properties └── test1.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | *.java text diff=java 5 | *.properties text 6 | *.js text 7 | *.css text 8 | *.less text 9 | *.html text diff=html 10 | *.jsp text diff=html 11 | *.jspx text diff=html 12 | *.tag text diff=html 13 | *.tagx text diff=html 14 | *.tld text 15 | *.xml text 16 | *.gradle text 17 | 18 | *.sql text 19 | 20 | *.xsd text 21 | *.dtd text 22 | *.mod text 23 | *.ent text 24 | 25 | *.txt text 26 | *.md text 27 | *.markdown text 28 | 29 | *.thtest text 30 | *.thindex text 31 | *.common text 32 | 33 | *.odt binary 34 | *.pdf binary 35 | 36 | *.sh text eol=lf 37 | *.bat text eol=crlf 38 | 39 | *.ico binary 40 | *.png binary 41 | *.svg binary 42 | *.woff binary 43 | 44 | *.rar binary 45 | *.zargo binary 46 | *.zip binary 47 | 48 | CNAME text 49 | *.MF text 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | target/ 4 | bin/ 5 | .settings/ 6 | .idea/ 7 | *.iml 8 | 9 | -------------------------------------------------------------------------------- /BUILD.txt: -------------------------------------------------------------------------------- 1 | 2 | Building thymeleaf-extras-java8time 3 | ----------------------------------- 4 | 5 | To build thymeleaf-extras-java8time you will need Maven 3. You can 6 | get it at: 7 | 8 | http://maven.apache.org 9 | 10 | Build and install the project executing, from the project's root folder: 11 | 12 | mvn clean:clean install 13 | 14 | And you will get a fresh target/thymeleaf-extras-java8time-{version}.jar 15 | file. You will also get it installed in your local repository at: 16 | 17 | $M2_REPO/org/thymeleaf/extras/thymeleaf-extras-java8time/{version}/thymeleaf-extras-java8time-{version}.jar 18 | 19 | 20 | 21 | Generating Javadoc for thymeleaf-extras-java8time 22 | ------------------------------------------------- 23 | 24 | If you wish to generate the javadoc for thymeleaf-extras-java8time, 25 | execute this from the project root folder: 26 | 27 | mvn javadoc:javadoc 28 | 29 | This will generate the javadoc documentation in HTML format in: 30 | 31 | target/site/apidocs 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.markdown: -------------------------------------------------------------------------------- 1 | # Contributing to Thymeleaf 2 | 3 | Thymeleaf is released under the Apache 2.0 license. If you would like to 4 | contribute something, or want to hack on the code this document should help you 5 | get started. 6 | 7 | 8 | ## Code of Conduct 9 | 10 | This project adheres to the Contributor Covenant 11 | [code of conduct][code-of-coduct]. 12 | By participating, you are expected to uphold this code. Please report 13 | unacceptable behavior to [the project leads][thymeleaf-team]. 14 | 15 | 16 | ## Using GitHub Issues 17 | 18 | We use GitHub issues to track bugs and enhancements. 19 | If you have a general usage question please ask on 20 | [Stack Overflow][stackoverflow]. 21 | The Thymeleaf team and the broader community monitor the 22 | [`thymeleaf`][stackoverflow-thymeleaf] tag. 23 | 24 | If you are reporting a bug, please help to speed up problem diagnosis by 25 | providing as much information as possible. 26 | Ideally, that would include a small sample project that reproduces the problem. 27 | 28 | 29 | ## Before submitting a Contribution 30 | 31 | Before submitting a contribution that is not an obvious or trivial fix, 32 | get in contact with the [the project leads][thymeleaf-team] about your 33 | ideas (an email should do). Let us discuss the possibilities with you so that 34 | we make sure your contribution goes in the right direction and aligns with the 35 | project's standards, intentions and roadmap. 36 | 37 | Please understand that *not all contributions will be accepted and merged into 38 | the project's repositories*. Talking about your planned contributions with the 39 | project maintainers before creating pull requests can maximize the possibility 40 | of your contributions being accepted. 41 | 42 | 43 | 44 | ## Signing the Contributor License Agreement 45 | 46 | Before we accept a non-trivial patch or pull request we will need you to 47 | sign a **Contributor License Agreement**. 48 | 49 | There are two versions of the CLA: 50 | 51 | * **Individual CLA**: For individuals acting on their own behalf, i.e. not 52 | being backed by any company or government, and not making their 53 | contributions potentially under the effect of any contracts, agreements or 54 | laws that could cause their employeer (or any other entities) claim 55 | any rights on their contribution. 56 | * **Corporate CLA**: For corporate entities allowing some of their employees 57 | to contribute to Thymeleaf on the entity's behalf. 58 | 59 | For more information on the CLA and the (very easy) process involving this 60 | step, please have a look at the [Thymeleaf CLA repository][cla]. 61 | 62 | 63 | 64 | ## Conventions and Housekeeping 65 | 66 | ### General Guidelines: 67 | 68 | - Obviously, **your code must both compile and work correctly**. 69 | - All your code should be easy to read and understand by a human. The same 70 | requirement applies to documentation. 71 | - Unless for specific artifacts such as documentation translations, all 72 | code, comments, documentation, names of classes and variables, 73 | log messages, etc. must be **in English**. 74 | - All contribured files must include the standard Thymeleaf copyright header. 75 | - Maximum recommended line length is 120 characters. This is not strictly 76 | enforced. 77 | - Indentation should be made with 4 spaces, not tabs. Line feeds should be 78 | UNIX-like (`\n`). 79 | - All source files should be pure ASCII, except `.properties` files which 80 | should be ISO-8859-1. 81 | - You shall add yourself as _author_ (e.g. Javadoc `@author`) to any files 82 | that you create or modify substantially (more than cosmetic changes). 83 | 84 | ### Specific Java Code Gudelines: 85 | 86 | - All your code should compile and run in the current minimum Java version 87 | of the project. 88 | - All your code should follow the Java Code Conventions regarding 89 | variable/method/class naming. 90 | - Number autoboxing and/or autounboxing is forbidden. 91 | - Every class should define a constructor, even if it is the no-argument 92 | constructor, and include a call to `super()`. 93 | - All method parameters should be declared as `final` so that they cannot be 94 | changed or reassigned in the method. 95 | - All non-nullable parameters in public methods should be first validated for 96 | non-nullity inside the code. 97 | - Existing Javadoc must be maintained along with performed changes. Addition 98 | of new Javadoc for public methods or code comments for any non-trivial 99 | algorithms is always welcome. 100 | - Writing unit tests for new, existing and modified code is always welcome 101 | too. For any new algorithms or functionality contributed, or substantial 102 | modifications made to existing ones, the team might consider these a 103 | requirement. 104 | 105 | 106 | 107 | 108 | [cla]: https://github.com/thymeleaf/thymeleaf-org/blob/CLA_CURRENT/CLA/ 109 | [code-of-coduct]: https://github.com/thymeleaf/thymeleaf-org/blob/CoC_CURRENT/CoC/THYMELEAF_CODE_OF_CONDUCT.markdown 110 | [thymeleaf-team]: https://www.thymeleaf.org/team.html 111 | [stackoverflow]: https://stackoverflow.com 112 | [stackoverflow-thymeleaf]: https://stackoverflow.com/tags/thymeleaf 113 | -------------------------------------------------------------------------------- /ChangeLog.txt: -------------------------------------------------------------------------------- 1 | 3.0.5 2 | ===== 3 | 4 | - Added method #temportals.format(target, pattern, zoneId) to allow formatting datetimes using different time zones 5 | - Using Thymeleaf 3.0.15.RELEASE 6 | - Updated SLF4J dependency to 1.7.36 7 | 8 | 3.0.4 9 | ===== 10 | - When the patterns defined at java.time.format.FormatStyle (SHORT, MEDIUM, LONG, FULL) were used, Locale was ignored. 11 | - Added support for pattern formatting for some classes without timezone. 12 | - Fixed automatic module name error 13 | 14 | 3.0.3 15 | ===== 16 | - Fixed problem formatting LocalTime with custom pattern https://github.com/thymeleaf/thymeleaf-extras-java8time/issues/20 17 | 18 | 3.0.2 19 | ===== 20 | - Updated SLF4j dependency to 1.7.25. 21 | - Added "Automatic-Module-Name: thymeleaf-extras-java8time" to MANIFEST.MF for Java 9+ JPMS. 22 | - Adapted expected test results to formatting rules of Java 11 LTS. 23 | - Fixed issue 24 | https://github.com/thymeleaf/thymeleaf-extras-java8time/issues/17 regarding 25 | formatting with custom patterns 26 | - Allowing SHORT, MEDIUM, LONG and FULL as pattern values to use the java.time.format.FormatStyle patterns 27 | - Localised formatters for OffsetDateTime and YearMonth classes 28 | 29 | 3.0.1 30 | ===== 31 | - Normalisation of null-safety: make #temporals return null for null input, instead of throwing exception. 32 | 33 | 3.0.0 34 | ===== 35 | - Adapted extension to the new APIs in Thymeleaf 3 36 | 37 | 2.1.1 38 | ===== 39 | - Support for specific formatting of LocalDate, LocalDateTime, ZonedDateTime, Instant, LocalTime, 40 | OffsetTime, OffsetDateTime, Year and YearMonth. 41 | 42 | 2.1.0 43 | ===== 44 | - First release of the Java 8 Dialect 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | 2 | Thymeleaf - Module for Java 8 Time API compatibility 3 | ==================================================== 4 | 5 | ------------------------------------------------------------------------------ 6 | 7 | Status 8 | ------ 9 | 10 | This is a *thymeleaf extras* module, not a part of the Thymeleaf core (and as 11 | such following its own versioning schema), but fully supported by the 12 | Thymeleaf team. 13 | 14 | Current versions: 15 | 16 | * **Version 3.1.0.RELEASE** - for Thymeleaf 3.0 (requires Thymeleaf 3.0.0+) 17 | * **Version 2.1.0.RELEASE** - for Thymeleaf 2.1 (requires Thymeleaf 2.1.3+) 18 | 19 | 20 | License 21 | ------- 22 | 23 | This software is licensed under the [Apache License 2.0] 24 | (http://www.apache.org/licenses/LICENSE-2.0.html). 25 | 26 | 27 | Requirements (3.0.x) 28 | -------------------- 29 | 30 | * Java **8** 31 | * Thymeleaf **3.0.0+** 32 | 33 | 34 | Maven info 35 | ---------- 36 | 37 | * groupId: `org.thymeleaf.extras` 38 | * artifactId: `thymeleaf-extras-java8time` 39 | 40 | 41 | Distribution packages 42 | --------------------- 43 | 44 | Distribution packages (binaries + sources + javadoc) can be downloaded from [SourceForge](http://sourceforge.net/projects/thymeleaf/files/thymeleaf-extras-java8time/). 45 | 46 | 47 | Installation 48 | ------------ 49 | 50 | Just add the `org.thymeleaf.extras.java8time.dialect.Java8TimeDialect` 51 | class to the list of dialects in your TemplateEngine implementation, and you will 52 | have the `#temporals` object available to be used in your templates. 53 | 54 | ```java 55 | TemplateEngine templateEngine = new TemplateEngine(); 56 | templateEngine.addDialect(new Java8TimeDialect()); 57 | ... 58 | ``` 59 | 60 | Features 61 | -------- 62 | 63 | This module adds a `#temporals` object similar to the `#dates` or `#calendars` ones in 64 | the Standard Dialect, allowing the formatting and creation of temporal objects from 65 | Thymeleaf templates. 66 | 67 | ![Java 8 Time API](/src/main/resources/temporal.png "Java 8 Time API") 68 | 69 | 70 | Usage 71 | ----- 72 | 73 | ```java 74 | /* 75 | * ============================================================================= 76 | * See javadoc API for class org.thymeleaf.extras.java8time.expression.Temporals 77 | * ============================================================================= 78 | */ 79 | 80 | /* 81 | * Format date with the standard locale format 82 | * Also works with arrays, lists or sets 83 | */ 84 | ${#temporals.format(temporal)} 85 | ${#temporals.arrayFormat(temporalsArray)} 86 | ${#temporals.listFormat(temporalsList)} 87 | ${#temporals.setFormat(temporalsSet)} 88 | 89 | /* 90 | * Format date with the standard format for the provided locale 91 | * Also works with arrays, lists or sets 92 | */ 93 | ${#temporals.format(temporal, locale)} 94 | ${#temporals.arrayFormat(temporalsArray, locale)} 95 | ${#temporals.listFormat(temporalsList, locale)} 96 | ${#temporals.setFormat(temporalsSet, locale)} 97 | 98 | /* 99 | * Format date with the specified pattern 100 | * SHORT, MEDIUM, LONG and FULL can also be specified to used the default java.time.format.FormatStyle patterns 101 | * Also works with arrays, lists or sets 102 | */ 103 | ${#temporals.format(temporal, 'dd/MMM/yyyy HH:mm')} 104 | ${#temporals.format(temporal, 'dd/MMM/yyyy HH:mm', 'Europe/Paris')} 105 | ${#temporals.arrayFormat(temporalsArray, 'dd/MMM/yyyy HH:mm')} 106 | ${#temporals.listFormat(temporalsList, 'dd/MMM/yyyy HH:mm')} 107 | ${#temporals.setFormat(temporalsSet, 'dd/MMM/yyyy HH:mm')} 108 | 109 | /* 110 | * Format date with the specified pattern and locale 111 | * Also works with arrays, lists or sets 112 | */ 113 | ${#temporals.format(temporal, 'dd/MMM/yyyy HH:mm', locale)} 114 | ${#temporals.arrayFormat(temporalsArray, 'dd/MMM/yyyy HH:mm', locale)} 115 | ${#temporals.listFormat(temporalsList, 'dd/MMM/yyyy HH:mm', locale)} 116 | ${#temporals.setFormat(temporalsSet, 'dd/MMM/yyyy HH:mm', locale)} 117 | 118 | /* 119 | * Format date with ISO-8601 format 120 | * Also works with arrays, lists or sets 121 | */ 122 | ${#temporals.formatISO(temporal)} 123 | ${#temporals.arrayFormatISO(temporalsArray)} 124 | ${#temporals.listFormatISO(temporalsList)} 125 | ${#temporals.setFormatISO(temporalsSet)} 126 | 127 | /* 128 | * Obtain date properties 129 | * Also works with arrays, lists or sets 130 | */ 131 | ${#temporals.day(temporal)} // also arrayDay(...), listDay(...), etc. 132 | ${#temporals.month(temporal)} // also arrayMonth(...), listMonth(...), etc. 133 | ${#temporals.monthName(temporal)} // also arrayMonthName(...), listMonthName(...), etc. 134 | ${#temporals.monthNameShort(temporal)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc. 135 | ${#temporals.year(temporal)} // also arrayYear(...), listYear(...), etc. 136 | ${#temporals.dayOfWeek(temporal)} // also arrayDayOfWeek(...), listDayOfWeek(...), etc. 137 | ${#temporals.dayOfWeekName(temporal)} // also arrayDayOfWeekName(...), listDayOfWeekName(...), etc. 138 | ${#temporals.dayOfWeekNameShort(temporal)} // also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc. 139 | ${#temporals.hour(temporal)} // also arrayHour(...), listHour(...), etc. 140 | ${#temporals.minute(temporal)} // also arrayMinute(...), listMinute(...), etc. 141 | ${#temporals.second(temporal)} // also arraySecond(...), listSecond(...), etc. 142 | ${#temporals.nanosecond(temporal)} // also arrayNanosecond(...), listNanosecond(...), etc. 143 | 144 | /* 145 | * Create temporal (java.time.Temporal) objects from its components 146 | */ 147 | ${#temporals.create(year,month,day)} // return a instance of java.time.LocalDate 148 | ${#temporals.create(year,month,day,hour,minute)} // return a instance of java.time.LocalDateTime 149 | ${#temporals.create(year,month,day,hour,minute,second)} // return a instance of java.time.LocalDateTime 150 | ${#temporals.create(year,month,day,hour,minute,second,nanosecond)} // return a instance of java.time.LocalDateTime 151 | 152 | /* 153 | * Create a temporal (java.time.Temporal) object for the current date and time 154 | */ 155 | ${#temporals.createNow()} // return a instance of java.time.LocalDateTime 156 | ${#temporals.createNowForTimeZone(zoneId)} // return a instance of java.time.ZonedDateTime 157 | ${#temporals.createToday()} // return a instance of java.time.LocalDate 158 | ${#temporals.createTodayForTimeZone(zoneId)} // return a instance of java.time.LocalDate 159 | 160 | /* 161 | * Create a temporal (java.time.Temporal) object for the provided date 162 | */ 163 | ${#temporals.createDate(isoDate)} // return a instance of java.time.LocalDate 164 | ${#temporals.createDateTime(isoDate)} // return a instance of java.time.LocalDateTime 165 | ${#temporals.createDate(isoDate, pattern)} // return a instance of java.time.LocalDate 166 | ${#temporals.createDateTime(isoDate, pattern)} // return a instance of java.time.LocalDateTime 167 | 168 | ``` 169 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 24 | 25 | 4.0.0 26 | org.thymeleaf.extras 27 | thymeleaf-extras-java8time 28 | jar 29 | 3.1.0-SNAPSHOT 30 | thymeleaf-extras-java8time 31 | http://www.thymeleaf.org 32 | 33 | Modern server-side Java template engine for both web and standalone environments 34 | 35 | 36 | 37 | The Apache Software License, Version 2.0 38 | http://www.apache.org/licenses/LICENSE-2.0.txt 39 | repo 40 | 41 | 42 | 43 | 44 | The THYMELEAF team 45 | http://www.thymeleaf.org 46 | 47 | 48 | 49 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-java8time.git 50 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-java8time.git 51 | scm:git:git@github.com:thymeleaf/thymeleaf-extras-java8time.git 52 | 53 | 54 | 55 | 56 | danielfernandez 57 | Daniel Fernandez 58 | daniel.fernandez AT 11thlabs DOT org 59 | 60 | Project Admin 61 | Lead Developer 62 | 63 | 64 | 65 | jmiguelsamper 66 | Jose Miguel Samper 67 | jmiguelsamper AT users DOT sourceforge DOT net 68 | 69 | Developer 70 | 71 | 72 | 73 | ultraq 74 | Emanuel Rabina 75 | emanuelrabina AT gmail DOT com 76 | 77 | Developer 78 | 79 | 80 | 81 | 82 | 83 | 84 | sonatype-nexus-snapshots 85 | Sonatype Nexus Snapshots 86 | https://oss.sonatype.org/content/repositories/snapshots 87 | 88 | 89 | sonatype-nexus-snapshots 90 | Sonatype Nexus Snapshots 91 | https://oss.sonatype.org/service/local/staging/deploy/maven2 92 | 93 | 94 | 95 | 96 | 97 | sonatype-nexus-snapshots 98 | Sonatype Nexus Snapshots 99 | https://oss.sonatype.org/content/repositories/snapshots 100 | 101 | true 102 | 103 | 104 | 105 | 106 | 107 | 108 | 8 109 | ${java.version} 110 | ${java.version} 111 | ${java.version} 112 | US-ASCII 113 | ISO-8859-1 114 | 115 | 116 | 117 | thymeleaf.extras.java8time 118 | 3.0.15.RELEASE 119 | 1.7.36 120 | 121 | 4.13.2 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | src/main/resources 131 | 132 | 133 | 134 | . 135 | META-INF 136 | 137 | LICENSE.txt 138 | NOTICE.txt 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | src/test/resources 147 | 148 | 149 | 150 | 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-compiler-plugin 155 | 3.10.1 156 | 157 | 158 | 159 | org.apache.maven.plugins 160 | maven-resources-plugin 161 | 3.2.0 162 | 163 | ${project.build.sourceEncoding} 164 | ${project.build.propertiesEncoding} 165 | 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-jar-plugin 171 | 3.2.2 172 | 173 | 174 | false 175 | 176 | true 177 | true 178 | 179 | 180 | 181 | 182 | 183 | ${maven.compiler.source} 184 | ${maven.compiler.target} 185 | 186 | thymeleaf 187 | 188 | 189 | ${module.name} 190 | 191 | 192 | 193 | 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-javadoc-plugin 198 | 3.4.0 199 | 200 | protected 201 | java.lang 202 | ${maven.compiler.source} 203 | ${maven.compiler.release} 204 | ${basedir}/src/main/javadoc/overview.html 205 | ${basedir}/src/main/javadoc 206 | ${project.build.directory}/apidocs 207 | 208 | --no-module-directories 209 | 210 | 211 | 212 | package 213 | 214 | jar 215 | 216 | 217 | 218 | 219 | 220 | 221 | org.apache.maven.plugins 222 | maven-source-plugin 223 | 3.2.1 224 | 225 | 226 | package 227 | 228 | jar 229 | 230 | 231 | 232 | 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-assembly-plugin 237 | 3.3.0 238 | 239 | 240 | make-assembly-dist 241 | package 242 | 243 | single 244 | 245 | 246 | 247 | ${basedir}/src/assembly/dist.xml 248 | 249 | true 250 | thymeleaf-extras-java8time-${project.version} 251 | 252 | 253 | 254 | 255 | 256 | 257 | org.apache.maven.plugins 258 | maven-gpg-plugin 259 | 3.0.1 260 | 261 | 262 | sign-artifacts 263 | verify 264 | 265 | sign 266 | 267 | 268 | 269 | 270 | 271 | 272 | org.apache.maven.plugins 273 | maven-deploy-plugin 274 | 2.8.2 275 | 276 | 277 | 278 | org.apache.maven.plugins 279 | maven-release-plugin 280 | 2.5.3 281 | 282 | 283 | 284 | 285 | org.apache.maven.plugins 286 | maven-antrun-plugin 287 | 3.1.0 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | copy-javadoc-element-list-to-package-list 298 | package 299 | 300 | run 301 | 302 | 303 | 304 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | org.thymeleaf 325 | thymeleaf 326 | ${thymeleaf.version} 327 | compile 328 | 329 | 330 | 331 | org.slf4j 332 | slf4j-api 333 | ${slf4j.version} 334 | compile 335 | 336 | 337 | 338 | junit 339 | junit 340 | ${junit.version} 341 | test 342 | 343 | 344 | 345 | org.slf4j 346 | slf4j-simple 347 | ${slf4j.version} 348 | test 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | -------------------------------------------------------------------------------- /src/assembly/dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | dist 7 | 8 | 9 | zip 10 | 11 | 12 | 13 | 14 | ${basedir}/target/apidocs/ 15 | /apidocs/thymeleaf-extras-java8time/ 16 | 17 | **/* 18 | 19 | 20 | 21 | ${basedir}/target/ 22 | /dist 23 | 24 | thymeleaf-extras-java8time-${project.version}.jar 25 | thymeleaf-extras-java8time-${project.version}-javadoc.jar 26 | thymeleaf-extras-java8time-${project.version}-sources.jar 27 | 28 | 29 | 30 | ${basedir}/ 31 | / 32 | 33 | LICENSE.txt 34 | README.markdown 35 | NOTICE.txt 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | /lib 44 | false 45 | runtime 46 | 47 | org.thymeleaf.extras:thymeleaf-extras-java8time 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/dialect/Java8TimeDialect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.dialect; 21 | 22 | import org.thymeleaf.dialect.AbstractDialect; 23 | import org.thymeleaf.dialect.IExpressionObjectDialect; 24 | import org.thymeleaf.expression.IExpressionObjectFactory; 25 | 26 | /** 27 | * 28 | * Thymeleaf Dialect to format and create Java 8 Time objects. 29 | * 30 | * @author José Miguel Samper 31 | * @author Ivan Martinez-Ortiz 32 | * 33 | * @since 2.1.0 34 | */ 35 | public class Java8TimeDialect extends AbstractDialect implements IExpressionObjectDialect { 36 | 37 | private final IExpressionObjectFactory JAVA8_TIME_EXPRESSION_OBJECTS_FACTORY = new Java8TimeExpressionFactory(); 38 | 39 | public Java8TimeDialect() { 40 | super("java8time"); 41 | } 42 | 43 | @Override 44 | public IExpressionObjectFactory getExpressionObjectFactory() { 45 | return JAVA8_TIME_EXPRESSION_OBJECTS_FACTORY; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/dialect/Java8TimeExpressionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.dialect; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | import org.thymeleaf.context.IExpressionContext; 23 | import org.thymeleaf.expression.IExpressionObjectFactory; 24 | import org.thymeleaf.extras.java8time.expression.Temporals; 25 | 26 | /** 27 | * @author jmsamper 28 | * @author Ivan Martinez-Ortiz 29 | * 30 | * @since 3.0.0 31 | */ 32 | public class Java8TimeExpressionFactory implements IExpressionObjectFactory { 33 | 34 | private static final String TEMPORAL_EVALUATION_VARIABLE_NAME = "temporals"; 35 | 36 | private static final Set ALL_EXPRESSION_OBJECT_NAMES = Collections.unmodifiableSet( 37 | new HashSet<>(Arrays.asList(TEMPORAL_EVALUATION_VARIABLE_NAME))); 38 | 39 | @Override 40 | public Set getAllExpressionObjectNames() { 41 | return ALL_EXPRESSION_OBJECT_NAMES; 42 | } 43 | 44 | @Override 45 | public Object buildObject(IExpressionContext context, String expressionObjectName) { 46 | if (TEMPORAL_EVALUATION_VARIABLE_NAME.equals(expressionObjectName)) { 47 | return new Temporals(context.getLocale()); 48 | } 49 | return null; 50 | } 51 | 52 | @Override 53 | public boolean isCacheable(String expressionObjectName) { 54 | return expressionObjectName != null && TEMPORAL_EVALUATION_VARIABLE_NAME.equals(expressionObjectName); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/expression/Temporals.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.expression; 21 | 22 | import java.time.ZoneId; 23 | import java.time.temporal.Temporal; 24 | import java.util.List; 25 | import java.util.Locale; 26 | import java.util.Set; 27 | 28 | import org.thymeleaf.extras.java8time.util.TemporalArrayUtils; 29 | import org.thymeleaf.extras.java8time.util.TemporalCreationUtils; 30 | import org.thymeleaf.extras.java8time.util.TemporalFormattingUtils; 31 | import org.thymeleaf.extras.java8time.util.TemporalListUtils; 32 | import org.thymeleaf.extras.java8time.util.TemporalSetUtils; 33 | import org.thymeleaf.util.Validate; 34 | 35 | /** 36 | *

37 | * Utility class to performJava 8 dates ({@link Temporal}) operations. 38 | *

39 | *

40 | * An object of this class is usually available in variable evaluation expressions with the name 41 | * {@code #temporals}. 42 | *

43 | * 44 | * @author José Miguel Samper 45 | * 46 | * @since 2.1.0 47 | */ 48 | public final class Temporals { 49 | 50 | private final TemporalCreationUtils temporalCreationUtils; 51 | private final TemporalFormattingUtils temporalFormattingUtils; 52 | private final TemporalArrayUtils temporalArrayUtils; 53 | private final TemporalListUtils temporalListUtils; 54 | private final TemporalSetUtils temporalSetUtils; 55 | 56 | public Temporals(final Locale locale) { 57 | this(locale, ZoneId.systemDefault()); 58 | } 59 | 60 | public Temporals(final Locale locale, final ZoneId defaultZoneId) { 61 | super(); 62 | Validate.notNull(locale, "Locale cannot be null"); 63 | this.temporalCreationUtils = new TemporalCreationUtils(); 64 | this.temporalFormattingUtils = new TemporalFormattingUtils(locale, defaultZoneId); 65 | this.temporalArrayUtils = new TemporalArrayUtils(locale, defaultZoneId); 66 | this.temporalListUtils = new TemporalListUtils(locale, defaultZoneId); 67 | this.temporalSetUtils = new TemporalSetUtils(locale, defaultZoneId); 68 | } 69 | 70 | /** 71 | * 72 | * @return a instance of java.time.LocalDate 73 | * @since 2.1.0 74 | */ 75 | public Temporal create(final Object year, final Object month, final Object day) { 76 | return temporalCreationUtils.create(year, month, day); 77 | } 78 | 79 | /** 80 | * 81 | * @return a instance of java.time.LocalDateTime 82 | * @since 2.1.0 83 | */ 84 | public Temporal create(final Object year, final Object month, final Object day, 85 | final Object hour, final Object minute) { 86 | return temporalCreationUtils.create(year, month, day, hour, minute); 87 | } 88 | 89 | /** 90 | * 91 | * @return a instance of java.time.LocalDateTime 92 | * @since 2.1.0 93 | */ 94 | public Temporal create(final Object year, final Object month, final Object day, 95 | final Object hour, final Object minute, final Object second) { 96 | return temporalCreationUtils.create(year, month, day, hour, minute, second); 97 | } 98 | 99 | /** 100 | * 101 | * @return a instance of java.time.LocalDateTime 102 | * @since 2.1.0 103 | */ 104 | public Temporal create(final Object year, final Object month, final Object day, 105 | final Object hour, final Object minute, final Object second, final Object nanosecond) { 106 | return temporalCreationUtils.create(year, month, day, hour, minute, second, nanosecond); 107 | } 108 | 109 | /** 110 | * 111 | * @return a instance of java.time.LocalDate 112 | * @since 2.1.0 113 | */ 114 | public Temporal createDate(final String isoDate) { 115 | return temporalCreationUtils.createDate(isoDate); 116 | } 117 | 118 | /** 119 | * 120 | * @return a instance of java.time.LocalDateTime 121 | * @since 2.1.0 122 | */ 123 | public Temporal createDateTime(final String isoDate) { 124 | return temporalCreationUtils.createDateTime(isoDate); 125 | } 126 | 127 | /** 128 | * 129 | * @return a instance of java.time.LocalDate 130 | * @since 2.1.0 131 | */ 132 | public Temporal createDate(final String isoDate, final String pattern) { 133 | return temporalCreationUtils.createDate(isoDate, pattern); 134 | } 135 | 136 | /** 137 | * 138 | * @return a instance of java.time.LocalDateTime 139 | * @since 2.1.0 140 | */ 141 | public Temporal createDateTime(final String isoDate, final String pattern) { 142 | return temporalCreationUtils.createDateTime(isoDate, pattern); 143 | } 144 | 145 | /** 146 | * 147 | * @return a instance of java.time.LocalDateTime 148 | * @since 2.1.0 149 | */ 150 | public Temporal createNow() { 151 | return temporalCreationUtils.createNow(); 152 | } 153 | 154 | /** 155 | * 156 | * @return a instance of java.time.ZonedDateTime 157 | * @since 2.1.0 158 | */ 159 | public Temporal createNowForTimeZone(final Object zoneId) { 160 | return temporalCreationUtils.createNowForTimeZone(zoneId); 161 | } 162 | 163 | /** 164 | * 165 | * @return a instance of java.time.LocalDate 166 | * @since 2.1.0 167 | */ 168 | public Temporal createToday() { 169 | return temporalCreationUtils.createToday(); 170 | } 171 | 172 | /** 173 | * 174 | * @return a instance of java.time.ZonedDateTime with 00:00:00.000 for the time part 175 | * @since 2.1.0 176 | */ 177 | public Temporal createTodayForTimeZone(final Object zoneId) { 178 | return temporalCreationUtils.createTodayForTimeZone(zoneId); 179 | } 180 | 181 | /** 182 | * 183 | * @since 2.1.0 184 | */ 185 | public String format(final Temporal target) { 186 | return temporalFormattingUtils.format(target); 187 | } 188 | 189 | /** 190 | * 191 | * @since 2.1.0 192 | */ 193 | public String[] arrayFormat(final Object[] target) { 194 | return temporalArrayUtils.arrayFormat(target); 195 | } 196 | 197 | /** 198 | * 199 | * @since 2.1.0 200 | */ 201 | public List listFormat(final List target) { 202 | return temporalListUtils.listFormat(target); 203 | } 204 | 205 | /** 206 | * 207 | * @since 2.1.0 208 | */ 209 | public Set setFormat(final Set target) { 210 | return temporalSetUtils.setFormat(target); 211 | } 212 | 213 | /** 214 | * 215 | * @since 2.1.1 216 | */ 217 | public String format(final Temporal target, final Locale locale) { 218 | return temporalFormattingUtils.format(target, locale); 219 | } 220 | 221 | /** 222 | * 223 | * @since 2.1.1 224 | */ 225 | public String[] arrayFormat(final Object[] target, final Locale locale) { 226 | return temporalArrayUtils.arrayFormat(target, locale); 227 | } 228 | 229 | /** 230 | * 231 | * @since 2.1.1 232 | */ 233 | public List listFormat(final List target, final Locale locale) { 234 | return temporalListUtils.listFormat(target, locale); 235 | } 236 | 237 | /** 238 | * 239 | * @since 2.1.1 240 | */ 241 | public Set setFormat(final Set target, final Locale locale) { 242 | return temporalSetUtils.setFormat(target, locale); 243 | } 244 | 245 | /** 246 | * 247 | * @since 2.1.0 248 | */ 249 | public String format(final Temporal target, final String pattern) { 250 | return temporalFormattingUtils.format(target, pattern, null); 251 | } 252 | 253 | /** 254 | * 255 | * @since 3.1.0 256 | */ 257 | public String format(final Temporal target, final String pattern, final String zoneId) { 258 | return temporalFormattingUtils.format(target, pattern, ZoneId.of(zoneId)); 259 | } 260 | 261 | /** 262 | * 263 | * @since 2.1.0 264 | */ 265 | public String[] arrayFormat(final Object[] target, final String pattern) { 266 | return temporalArrayUtils.arrayFormat(target, pattern); 267 | } 268 | 269 | /** 270 | * 271 | * @since 2.1.0 272 | */ 273 | public List listFormat(final List target, final String pattern) { 274 | return temporalListUtils.listFormat(target, pattern); 275 | } 276 | 277 | /** 278 | * 279 | * @since 2.1.0 280 | */ 281 | public Set setFormat(final Set target, final String pattern) { 282 | return temporalSetUtils.setFormat(target, pattern); 283 | } 284 | 285 | /** 286 | * 287 | * @since 2.1.1 288 | */ 289 | public String format(final Temporal target, final String pattern, final Locale locale) { 290 | return temporalFormattingUtils.format(target, pattern, locale, null); 291 | } 292 | 293 | /** 294 | * 295 | * @since 2.1.1 296 | */ 297 | public String[] arrayFormat(final Object[] target, final String pattern, final Locale locale) { 298 | return temporalArrayUtils.arrayFormat(target, pattern, locale); 299 | } 300 | 301 | /** 302 | * 303 | * @since 2.1.1 304 | */ 305 | public List listFormat(final List target, final String pattern, final Locale locale) { 306 | return temporalListUtils.listFormat(target, pattern, locale); 307 | } 308 | 309 | /** 310 | * 311 | * @since 2.1.1 312 | */ 313 | public Set setFormat(final Set target, final String pattern, final Locale locale) { 314 | return temporalSetUtils.setFormat(target, pattern, locale); 315 | } 316 | 317 | /** 318 | * 319 | * @since 2.1.0 320 | */ 321 | public Integer day(final Temporal target) { 322 | return temporalFormattingUtils.day(target); 323 | } 324 | 325 | /** 326 | * 327 | * @since 2.1.0 328 | */ 329 | public Integer[] arrayDay(final Object[] target) { 330 | return temporalArrayUtils.arrayDay(target); 331 | } 332 | 333 | /** 334 | * 335 | * @since 2.1.0 336 | */ 337 | public List listDay(final List target) { 338 | return temporalListUtils.listDay(target); 339 | } 340 | 341 | /** 342 | * 343 | * @since 2.1.0 344 | */ 345 | public Set setDay(final Set target) { 346 | return temporalSetUtils.setDay(target); 347 | } 348 | 349 | /** 350 | * 351 | * @since 2.1.0 352 | */ 353 | public Integer month(final Temporal target) { 354 | return temporalFormattingUtils.month(target); 355 | } 356 | 357 | /** 358 | * 359 | * @since 2.1.0 360 | */ 361 | public Integer[] arrayMonth(final Object[] target) { 362 | return temporalArrayUtils.arrayMonth(target); 363 | } 364 | 365 | /** 366 | * 367 | * @since 2.1.0 368 | */ 369 | public List listMonth(final List target) { 370 | return temporalListUtils.listMonth(target); 371 | } 372 | 373 | /** 374 | * 375 | * @since 2.1.0 376 | */ 377 | public Set setMonth(final Set target) { 378 | return temporalSetUtils.setMonth(target); 379 | } 380 | 381 | /** 382 | * 383 | * @since 2.1.0 384 | */ 385 | public String monthName(final Temporal target) { 386 | return temporalFormattingUtils.monthName(target); 387 | } 388 | 389 | /** 390 | * 391 | * @since 2.1.0 392 | */ 393 | public String[] arrayMonthName(final Object[] target) { 394 | return temporalArrayUtils.arrayMonthName(target); 395 | } 396 | 397 | /** 398 | * 399 | * @since 2.1.0 400 | */ 401 | public List listMonthName(final List target) { 402 | return temporalListUtils.listMonthName(target); 403 | } 404 | 405 | /** 406 | * 407 | * @since 2.1.0 408 | */ 409 | public Set setMonthName(final Set target) { 410 | return temporalSetUtils.setMonthName(target); 411 | } 412 | 413 | /** 414 | * 415 | * @since 2.1.0 416 | */ 417 | public String monthNameShort(final Temporal target) { 418 | return temporalFormattingUtils.monthNameShort(target); 419 | } 420 | 421 | /** 422 | * 423 | * @since 2.1.0 424 | */ 425 | public String[] arrayMonthNameShort(final Object[] target) { 426 | return temporalArrayUtils.arrayMonthNameShort(target); 427 | } 428 | 429 | /** 430 | * 431 | * @since 2.1.0 432 | */ 433 | public List listMonthNameShort(final List target) { 434 | return temporalListUtils.listMonthNameShort(target); 435 | } 436 | 437 | /** 438 | * 439 | * @since 2.1.0 440 | */ 441 | public Set setMonthNameShort(final Set target) { 442 | return temporalSetUtils.setMonthNameShort(target); 443 | } 444 | 445 | /** 446 | * 447 | * @since 2.1.0 448 | */ 449 | public Integer year(final Temporal target) { 450 | return temporalFormattingUtils.year(target); 451 | } 452 | 453 | /** 454 | * 455 | * @since 2.1.0 456 | */ 457 | public Integer[] arrayYear(final Object[] target) { 458 | return temporalArrayUtils.arrayYear(target); 459 | } 460 | 461 | /** 462 | * 463 | * @since 2.1.0 464 | */ 465 | public List listYear(final List target) { 466 | return temporalListUtils.listYear(target); 467 | } 468 | 469 | /** 470 | * 471 | * @since 2.1.0 472 | */ 473 | public Set setYear(final Set target) { 474 | return temporalSetUtils.setYear(target); 475 | } 476 | 477 | /** 478 | * 479 | * @since 2.1.0 480 | */ 481 | public Integer dayOfWeek(final Temporal target) { 482 | return temporalFormattingUtils.dayOfWeek(target); 483 | } 484 | 485 | /** 486 | * 487 | * @since 2.1.0 488 | */ 489 | public Integer[] arrayDayOfWeek(final Object[] target) { 490 | return temporalArrayUtils.arrayDayOfWeek(target); 491 | } 492 | 493 | /** 494 | * 495 | * @since 2.1.0 496 | */ 497 | public List listDayOfWeek(final List target) { 498 | return temporalListUtils.listDayOfWeek(target); 499 | } 500 | 501 | /** 502 | * 503 | * @since 2.1.0 504 | */ 505 | public Set setDayOfWeek(final Set target) { 506 | return temporalSetUtils.setDayOfWeek(target); 507 | } 508 | 509 | /** 510 | * 511 | * @since 2.1.0 512 | */ 513 | public String dayOfWeekName(final Temporal target) { 514 | return temporalFormattingUtils.dayOfWeekName(target); 515 | } 516 | 517 | /** 518 | * 519 | * @since 2.1.0 520 | */ 521 | public String[] arrayDayOfWeekName(final Object[] target) { 522 | return temporalArrayUtils.arrayDayOfWeekName(target); 523 | } 524 | 525 | /** 526 | * 527 | * @since 2.1.0 528 | */ 529 | public List listDayOfWeekName(final List target) { 530 | return temporalListUtils.listDayOfWeekName(target); 531 | } 532 | 533 | /** 534 | * 535 | * @since 2.1.0 536 | */ 537 | public Set setDayOfWeekName(final Set target) { 538 | return temporalSetUtils.setDayOfWeekName(target); 539 | } 540 | 541 | /** 542 | * 543 | * @since 2.1.0 544 | */ 545 | public String dayOfWeekNameShort(final Temporal target) { 546 | return temporalFormattingUtils.dayOfWeekNameShort(target); 547 | } 548 | 549 | /** 550 | * 551 | * @since 2.1.0 552 | */ 553 | public String[] arrayDayOfWeekNameShort(final Object[] target) { 554 | return temporalArrayUtils.arrayDayOfWeekNameShort(target); 555 | } 556 | 557 | /** 558 | * 559 | * @since 2.1.0 560 | */ 561 | public List listDayOfWeekNameShort(final List target) { 562 | return temporalListUtils.listDayOfWeekNameShort(target); 563 | } 564 | 565 | /** 566 | * 567 | * @since 2.1.0 568 | */ 569 | public Set setDayOfWeekNameShort(final Set target) { 570 | return temporalSetUtils.setDayOfWeekNameShort(target); 571 | } 572 | 573 | /** 574 | * 575 | * @since 2.1.0 576 | */ 577 | public Integer hour(final Temporal target) { 578 | return temporalFormattingUtils.hour(target); 579 | } 580 | 581 | /** 582 | * 583 | * @since 2.1.0 584 | */ 585 | public Integer[] arrayHour(final Object[] target) { 586 | return temporalArrayUtils.arrayHour(target); 587 | } 588 | 589 | /** 590 | * 591 | * @since 2.1.0 592 | */ 593 | public List listHour(final List target) { 594 | return temporalListUtils.listHour(target); 595 | } 596 | 597 | /** 598 | * 599 | * @since 2.1.0 600 | */ 601 | public Set setHour(final Set target) { 602 | return temporalSetUtils.setHour(target); 603 | } 604 | 605 | /** 606 | * 607 | * @since 2.1.0 608 | */ 609 | public Integer minute(final Temporal target) { 610 | return temporalFormattingUtils.minute(target); 611 | } 612 | 613 | /** 614 | * 615 | * @since 2.1.0 616 | */ 617 | public Integer[] arrayMinute(final Object[] target) { 618 | return temporalArrayUtils.arrayMinute(target); 619 | } 620 | 621 | /** 622 | * 623 | * @since 2.1.0 624 | */ 625 | public List listMinute(final List target) { 626 | return temporalListUtils.listMinute(target); 627 | } 628 | 629 | /** 630 | * 631 | * @since 2.1.0 632 | */ 633 | public Set setMinute(final Set target) { 634 | return temporalSetUtils.setMinute(target); 635 | } 636 | 637 | /** 638 | * 639 | * @since 2.1.0 640 | */ 641 | public Integer second(final Temporal target) { 642 | return temporalFormattingUtils.second(target); 643 | } 644 | 645 | /** 646 | * 647 | * @since 2.1.0 648 | */ 649 | public Integer[] arraySecond(final Object[] target) { 650 | return temporalArrayUtils.arraySecond(target); 651 | } 652 | 653 | /** 654 | * 655 | * @since 2.1.0 656 | */ 657 | public List listSecond(final List target) { 658 | return temporalListUtils.listSecond(target); 659 | } 660 | 661 | /** 662 | * 663 | * @since 2.1.0 664 | */ 665 | public Set setSecond(final Set target) { 666 | return temporalSetUtils.setSecond(target); 667 | } 668 | 669 | /** 670 | * 671 | * @since 2.1.0 672 | */ 673 | public Integer nanosecond(final Temporal target) { 674 | return temporalFormattingUtils.nanosecond(target); 675 | } 676 | 677 | /** 678 | * 679 | * @since 2.1.0 680 | */ 681 | public Integer[] arrayNanosecond(final Object[] target) { 682 | return temporalArrayUtils.arrayNanosecond(target); 683 | } 684 | 685 | /** 686 | * 687 | * @since 2.1.0 688 | */ 689 | public List listNanosecond(final List target) { 690 | return temporalListUtils.listNanosecond(target); 691 | } 692 | 693 | /** 694 | * 695 | * @since 2.1.0 696 | */ 697 | public Set setNanosecond(final Set target) { 698 | return temporalSetUtils.setNanosecond(target); 699 | } 700 | 701 | /** 702 | * 703 | * @since 2.1.0 704 | */ 705 | public String formatISO(final Temporal target) { 706 | return temporalFormattingUtils.formatISO(target); 707 | } 708 | 709 | /** 710 | * 711 | * @since 2.1.0 712 | */ 713 | public String[] arrayFormatISO(final Object[] target) { 714 | return temporalArrayUtils.arrayFormatISO(target); 715 | } 716 | 717 | /** 718 | * 719 | * @since 2.1.0 720 | */ 721 | public List listFormatISO(final List target) { 722 | return temporalListUtils.listFormatISO(target); 723 | } 724 | 725 | /** 726 | * 727 | * @since 2.1.0 728 | */ 729 | public Set setFormatISO(final Set target) { 730 | return temporalSetUtils.setFormatISO(target); 731 | } 732 | 733 | } 734 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalArrayUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.lang.reflect.Array; 23 | import java.time.ZoneId; 24 | import java.util.Locale; 25 | import java.util.function.Function; 26 | import java.util.stream.Stream; 27 | import org.thymeleaf.util.Validate; 28 | 29 | 30 | /** 31 | * Formatting utilities for Java 8 Time object arrays. 32 | * 33 | * @author José Miguel Samper 34 | * 35 | * @since 2.1.0 36 | */ 37 | public final class TemporalArrayUtils { 38 | 39 | private final TemporalFormattingUtils temporalFormattingUtils; 40 | 41 | public TemporalArrayUtils(final Locale locale, final ZoneId defaultZoneId) { 42 | super(); 43 | Validate.notNull(locale, "Locale cannot be null"); 44 | Validate.notNull(defaultZoneId, "ZoneId cannot be null"); 45 | temporalFormattingUtils = new TemporalFormattingUtils(locale, defaultZoneId); 46 | } 47 | 48 | public String[] arrayFormat(final Object[] target) { 49 | return arrayFormat(target, temporalFormattingUtils::format, String.class); 50 | } 51 | 52 | public String[] arrayFormat(final Object[] target, final Locale locale) { 53 | return arrayFormat(target, time -> temporalFormattingUtils.format(time, locale), String.class); 54 | } 55 | 56 | public String[] arrayFormat(final Object[] target, final String pattern) { 57 | return arrayFormat(target, time -> temporalFormattingUtils.format(time, pattern, null), String.class); 58 | } 59 | 60 | public String[] arrayFormat(final Object[] target, final String pattern, final Locale locale) { 61 | return arrayFormat(target, time -> temporalFormattingUtils.format(time, pattern, locale, null), String.class); 62 | } 63 | 64 | public Integer[] arrayDay(final Object[] target) { 65 | return arrayFormat(target, temporalFormattingUtils::day, Integer.class); 66 | } 67 | 68 | public Integer[] arrayMonth(final Object[] target) { 69 | return arrayFormat(target, temporalFormattingUtils::month, Integer.class); 70 | } 71 | 72 | public String[] arrayMonthName(final Object[] target) { 73 | return arrayFormat(target, temporalFormattingUtils::monthName, String.class); 74 | } 75 | 76 | public String[] arrayMonthNameShort(final Object[] target) { 77 | return arrayFormat(target, temporalFormattingUtils::monthNameShort, String.class); 78 | } 79 | 80 | public Integer[] arrayYear(final Object[] target) { 81 | return arrayFormat(target, temporalFormattingUtils::year, Integer.class); 82 | } 83 | 84 | public Integer[] arrayDayOfWeek(final Object[] target) { 85 | return arrayFormat(target, temporalFormattingUtils::dayOfWeek, Integer.class); 86 | } 87 | 88 | public String[] arrayDayOfWeekName(final Object[] target) { 89 | return arrayFormat(target, temporalFormattingUtils::dayOfWeekName, String.class); 90 | } 91 | 92 | public String[] arrayDayOfWeekNameShort(final Object[] target) { 93 | return arrayFormat(target, temporalFormattingUtils::dayOfWeekNameShort, String.class); 94 | } 95 | 96 | public Integer[] arrayHour(final Object[] target) { 97 | return arrayFormat(target, temporalFormattingUtils::hour, Integer.class); 98 | } 99 | 100 | public Integer[] arrayMinute(final Object[] target) { 101 | return arrayFormat(target, temporalFormattingUtils::minute, Integer.class); 102 | } 103 | 104 | public Integer[] arraySecond(final Object[] target) { 105 | return arrayFormat(target, temporalFormattingUtils::second, Integer.class); 106 | } 107 | 108 | public Integer[] arrayNanosecond(final Object[] target) { 109 | return arrayFormat(target, temporalFormattingUtils::nanosecond, Integer.class); 110 | } 111 | 112 | public String[] arrayFormatISO(final Object[] target) { 113 | return arrayFormat(target, temporalFormattingUtils::formatISO, String.class); 114 | } 115 | 116 | private R[] arrayFormat( 117 | final Object[] target, final Function mapFunction, final Class returnType) { 118 | Validate.notNull(target, "Target cannot be null"); 119 | return Stream.of(target) 120 | .map(time -> mapFunction.apply(time)) 121 | .toArray(length -> (R[]) Array.newInstance(returnType, length)); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalCreationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.time.LocalDate; 23 | import java.time.LocalDateTime; 24 | import java.time.ZoneId; 25 | import java.time.ZonedDateTime; 26 | import java.time.format.DateTimeFormatter; 27 | import java.time.temporal.Temporal; 28 | import java.util.TimeZone; 29 | import org.thymeleaf.util.EvaluationUtils; 30 | import org.thymeleaf.util.Validate; 31 | 32 | /** 33 | * This class instances implementations of Temporal ({@link Temporal}) according to different sets 34 | * of parameters. 35 | * 36 | * @author José Miguel Samper 37 | * 38 | * @since 2.1.0 39 | */ 40 | public final class TemporalCreationUtils { 41 | 42 | public TemporalCreationUtils() { 43 | super(); 44 | } 45 | 46 | /** 47 | * 48 | * @return a instance of java.time.LocalDate 49 | * @since 2.1.0 50 | */ 51 | public Temporal create(final Object year, final Object month, final Object day) { 52 | return LocalDate.of(integer(year), integer(month), integer(day)); 53 | } 54 | 55 | /** 56 | * 57 | * @return a instance of java.time.LocalDateTime 58 | * @since 2.1.0 59 | */ 60 | public Temporal create(final Object year, final Object month, final Object day, 61 | final Object hour, final Object minute) { 62 | return LocalDateTime.of(integer(year), integer(month), integer(day), integer(hour), integer(minute)); 63 | } 64 | 65 | /** 66 | * 67 | * @return a instance of java.time.LocalDateTime 68 | * @since 2.1.0 69 | */ 70 | public Temporal create(final Object year, final Object month, final Object day, 71 | final Object hour, final Object minute, final Object second) { 72 | return LocalDateTime.of(integer(year), integer(month), integer(day), 73 | integer(hour), integer(minute), integer(second)); 74 | } 75 | 76 | /** 77 | * 78 | * @return a instance of java.time.LocalDateTime 79 | * @since 2.1.0 80 | */ 81 | public Temporal create(final Object year, final Object month, final Object day, 82 | final Object hour, final Object minute, final Object second, final Object nanosecond) { 83 | return LocalDateTime.of(integer(year), integer(month), integer(day), 84 | integer(hour), integer(minute), integer(second), integer(nanosecond)); 85 | } 86 | 87 | /** 88 | * 89 | * @return a instance of java.time.LocalDateTime 90 | * @since 2.1.0 91 | */ 92 | public Temporal createNow() { 93 | return LocalDateTime.now(); 94 | } 95 | 96 | /** 97 | * 98 | * @return a instance of java.time.ZonedDateTime 99 | * @since 2.1.0 100 | */ 101 | public Temporal createNowForTimeZone(final Object zoneId) { 102 | return ZonedDateTime.now(zoneId(zoneId)); 103 | } 104 | 105 | /** 106 | * 107 | * @return a instance of java.time.LocalDate 108 | * @since 2.1.0 109 | */ 110 | public Temporal createToday() { 111 | return LocalDate.now(); 112 | } 113 | 114 | /** 115 | * 116 | * @return a instance of java.time.ZonedDateTime with 00:00:00.000 for the time part 117 | * @since 2.1.0 118 | */ 119 | public Temporal createTodayForTimeZone(final Object zoneId) { 120 | return ZonedDateTime.now(zoneId(zoneId)) 121 | .withHour(0).withMinute(0).withSecond(0).withNano(0); 122 | } 123 | 124 | /** 125 | * 126 | * @return a instance of java.time.LocalDate 127 | * @since 2.1.0 128 | */ 129 | public Temporal createDate(String isoDate) { 130 | return LocalDate.parse(isoDate); 131 | } 132 | 133 | /** 134 | * 135 | * @return a instance of java.time.LocalDateTime 136 | * @since 2.1.0 137 | */ 138 | public Temporal createDateTime(String isoDate) { 139 | return LocalDateTime.parse(isoDate); 140 | } 141 | 142 | /** 143 | * 144 | * @return a instance of java.time.LocalDate 145 | * @since 2.1.0 146 | */ 147 | public Temporal createDate(String isoDate, String pattern) { 148 | return LocalDate.parse(isoDate, DateTimeFormatter.ofPattern(pattern)); 149 | } 150 | 151 | /** 152 | * 153 | * @return a instance of java.time.LocalDateTime 154 | * @since 2.1.0 155 | */ 156 | public Temporal createDateTime(String isoDate, String pattern) { 157 | return LocalDateTime.parse(isoDate, DateTimeFormatter.ofPattern(pattern)); 158 | } 159 | 160 | private int integer(final Object number) { 161 | Validate.notNull(number, "Argument cannot be null"); 162 | return EvaluationUtils.evaluateAsNumber(number).intValue(); 163 | } 164 | 165 | private ZoneId zoneId(final Object zoneId) { 166 | Validate.notNull(zoneId, "ZoneId cannot be null"); 167 | if (zoneId instanceof ZoneId) { 168 | return (ZoneId) zoneId; 169 | } else if (zoneId instanceof TimeZone) { 170 | TimeZone timeZone = (TimeZone) zoneId; 171 | return timeZone.toZoneId(); 172 | } else { 173 | return ZoneId.of(zoneId.toString()); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalFormattingUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.time.ZoneId; 23 | import java.time.chrono.ChronoZonedDateTime; 24 | import java.time.format.DateTimeFormatter; 25 | import java.time.format.FormatStyle; 26 | import java.time.temporal.ChronoField; 27 | import java.time.temporal.TemporalAccessor; 28 | import java.util.Locale; 29 | import org.thymeleaf.exceptions.TemplateProcessingException; 30 | import static org.thymeleaf.extras.java8time.util.TemporalObjects.temporal; 31 | import static org.thymeleaf.extras.java8time.util.TemporalObjects.zonedTime; 32 | import org.thymeleaf.util.StringUtils; 33 | import org.thymeleaf.util.Validate; 34 | 35 | /** 36 | * Formatting utilities for Java 8 Time objects. 37 | * 38 | * @author José Miguel Samper 39 | * 40 | * @since 2.1.0 41 | */ 42 | public final class TemporalFormattingUtils { 43 | 44 | // Even though Java comes with several patterns for ISO8601, we use the same pattern of Thymeleaf #dates utility. 45 | private static final DateTimeFormatter ISO8601_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"); 46 | 47 | private final Locale locale; 48 | private final ZoneId defaultZoneId; 49 | 50 | public TemporalFormattingUtils(final Locale locale, final ZoneId defaultZoneId) { 51 | super(); 52 | Validate.notNull(locale, "Locale cannot be null"); 53 | Validate.notNull(defaultZoneId, "ZoneId cannot be null"); 54 | this.locale = locale; 55 | this.defaultZoneId = defaultZoneId; 56 | } 57 | 58 | public String format(final Object target) { 59 | return formatDate(target); 60 | } 61 | 62 | public String format(final Object target, final Locale locale) { 63 | Validate.notNull(locale, "Locale cannot be null"); 64 | return formatDate(target, null, locale, null); 65 | } 66 | 67 | public String format(final Object target, final String pattern, final ZoneId zoneId) { 68 | return format(target, pattern, null, zoneId); 69 | } 70 | 71 | public String format(final Object target, final String pattern, final Locale locale, final ZoneId zoneId) { 72 | Validate.notEmpty(pattern, "Pattern cannot be null or empty"); 73 | return formatDate(target, pattern, locale, zoneId); 74 | } 75 | 76 | public Integer day(final Object target) { 77 | if (target == null) { 78 | return null; 79 | } 80 | final TemporalAccessor time = temporal(target); 81 | return time.get(ChronoField.DAY_OF_MONTH); 82 | } 83 | 84 | public Integer month(final Object target) { 85 | if (target == null) { 86 | return null; 87 | } 88 | final TemporalAccessor time = temporal(target); 89 | return time.get(ChronoField.MONTH_OF_YEAR); 90 | } 91 | 92 | public String monthName(final Object target) { 93 | return format(target, "MMMM", null); 94 | } 95 | 96 | public String monthNameShort(final Object target) { 97 | return format(target, "MMM", null); 98 | } 99 | 100 | public Integer year(final Object target) { 101 | if (target == null) { 102 | return null; 103 | } 104 | final TemporalAccessor time = temporal(target); 105 | return time.get(ChronoField.YEAR); 106 | } 107 | 108 | public Integer dayOfWeek(final Object target) { 109 | if (target == null) { 110 | return null; 111 | } 112 | final TemporalAccessor time = temporal(target); 113 | return time.get(ChronoField.DAY_OF_WEEK); 114 | } 115 | 116 | public String dayOfWeekName(final Object target) { 117 | return format(target, "EEEE", null); 118 | } 119 | 120 | public String dayOfWeekNameShort(final Object target) { 121 | return format(target, "EEE", null); 122 | } 123 | 124 | public Integer hour(final Object target) { 125 | if (target == null) { 126 | return null; 127 | } 128 | final TemporalAccessor time = temporal(target); 129 | return time.get(ChronoField.HOUR_OF_DAY); 130 | } 131 | 132 | public Integer minute(final Object target) { 133 | if (target == null) { 134 | return null; 135 | } 136 | final TemporalAccessor time = temporal(target); 137 | return time.get(ChronoField.MINUTE_OF_HOUR); 138 | } 139 | 140 | public Integer second(final Object target) { 141 | if (target == null) { 142 | return null; 143 | } 144 | final TemporalAccessor time = temporal(target); 145 | return time.get(ChronoField.SECOND_OF_MINUTE); 146 | } 147 | 148 | public Integer nanosecond(final Object target) { 149 | if (target == null) { 150 | return null; 151 | } 152 | final TemporalAccessor time = temporal(target); 153 | return time.get(ChronoField.NANO_OF_SECOND); 154 | } 155 | 156 | public String formatISO(final Object target) { 157 | if (target == null) { 158 | return null; 159 | } else if (target instanceof TemporalAccessor) { 160 | ChronoZonedDateTime time = zonedTime(target, defaultZoneId); 161 | return ISO8601_DATE_TIME_FORMATTER.withLocale(locale).format(time); 162 | } else { 163 | throw new IllegalArgumentException( 164 | "Cannot format object of class \"" + target.getClass().getName() + "\" as a date"); 165 | } 166 | } 167 | 168 | private String formatDate(final Object target) { 169 | return formatDate(target, null, null, null); 170 | } 171 | 172 | private String formatDate(final Object target, final String pattern, final Locale localeOverride, final ZoneId zoneId) { 173 | if (target == null) { 174 | return null; 175 | } 176 | Locale formattingLocale = localeOverride != null ? localeOverride : locale; 177 | try { 178 | DateTimeFormatter formatter; 179 | if (StringUtils.isEmptyOrWhitespace(pattern)) { 180 | formatter = TemporalObjects.formatterFor(target, formattingLocale); 181 | return formatter.format(temporal(target)); 182 | } else { 183 | formatter = defaultOrPatternFormatted(pattern, formattingLocale, zoneId); 184 | return formatter.format(zonedTime(target, defaultZoneId)); 185 | } 186 | } catch (final Exception e) { 187 | throw new TemplateProcessingException( 188 | "Error formatting date for locale " + formattingLocale, e); 189 | } 190 | } 191 | 192 | private DateTimeFormatter defaultOrPatternFormatted(final String pattern, final Locale locale, final ZoneId zoneId) { 193 | switch (pattern) { 194 | case "SHORT" : return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withZone(zoneId).withLocale(locale); 195 | case "MEDIUM" : return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withZone(zoneId).withLocale(locale); 196 | case "LONG" : return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withZone(zoneId).withLocale(locale); 197 | case "FULL" : return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withZone(zoneId).withLocale(locale); 198 | default : return DateTimeFormatter.ofPattern(pattern, locale).withZone(zoneId); 199 | } 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalListUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.time.ZoneId; 23 | import java.time.temporal.Temporal; 24 | import java.util.List; 25 | import java.util.Locale; 26 | import java.util.function.Function; 27 | import static java.util.stream.Collectors.toList; 28 | import org.thymeleaf.util.Validate; 29 | 30 | 31 | /** 32 | * Formatting utilities for Java 8 Time object lists. 33 | * 34 | * @author José Miguel Samper 35 | * 36 | * @since 2.1.0 37 | */ 38 | public final class TemporalListUtils { 39 | 40 | private final TemporalFormattingUtils temporalFormattingUtils; 41 | 42 | public TemporalListUtils(final Locale locale, final ZoneId defaultZoneId) { 43 | super(); 44 | Validate.notNull(locale, "Locale cannot be null"); 45 | Validate.notNull(defaultZoneId, "ZoneId cannot be null"); 46 | temporalFormattingUtils = new TemporalFormattingUtils(locale, defaultZoneId); 47 | } 48 | 49 | public List listFormat(final List target) { 50 | return listFormat(target, temporalFormattingUtils::format); 51 | } 52 | 53 | public List listFormat(final List target, final Locale locale) { 54 | return listFormat(target, time -> temporalFormattingUtils.format(time, locale)); 55 | } 56 | 57 | public List listFormat(final List target, final String pattern) { 58 | return listFormat(target, time -> temporalFormattingUtils.format(time, pattern, null)); 59 | } 60 | 61 | public List listFormat(final List target, final String pattern, final Locale locale) { 62 | return listFormat(target, time -> temporalFormattingUtils.format(time, pattern, locale, null)); 63 | } 64 | 65 | public List listDay(final List target) { 66 | return listFormat(target, temporalFormattingUtils::day); 67 | } 68 | 69 | public List listMonth(final List target) { 70 | return listFormat(target, temporalFormattingUtils::month); 71 | } 72 | 73 | public List listMonthName(final List target) { 74 | return listFormat(target, temporalFormattingUtils::monthName); 75 | } 76 | 77 | public List listMonthNameShort(final List target) { 78 | return listFormat(target, temporalFormattingUtils::monthNameShort); 79 | } 80 | 81 | public List listYear(final List target) { 82 | return listFormat(target, temporalFormattingUtils::year); 83 | } 84 | 85 | public List listDayOfWeek(final List target) { 86 | return listFormat(target, temporalFormattingUtils::dayOfWeek); 87 | } 88 | 89 | public List listDayOfWeekName(final List target) { 90 | return listFormat(target, temporalFormattingUtils::dayOfWeekName); 91 | } 92 | 93 | public List listDayOfWeekNameShort(final List target) { 94 | return listFormat(target, temporalFormattingUtils::dayOfWeekNameShort); 95 | } 96 | 97 | public List listHour(final List target) { 98 | return listFormat(target, temporalFormattingUtils::hour); 99 | } 100 | 101 | public List listMinute(final List target) { 102 | return listFormat(target, temporalFormattingUtils::minute); 103 | } 104 | 105 | public List listSecond(final List target) { 106 | return listFormat(target, temporalFormattingUtils::second); 107 | } 108 | 109 | public List listNanosecond(final List target) { 110 | return listFormat(target, temporalFormattingUtils::nanosecond); 111 | } 112 | 113 | public List listFormatISO(final List target) { 114 | return listFormat(target, temporalFormattingUtils::formatISO); 115 | } 116 | 117 | private List listFormat( 118 | final List target, final Function mapFunction) { 119 | Validate.notNull(target, "Target cannot be null"); 120 | return target.stream() 121 | .map(time -> mapFunction.apply(time)) 122 | .collect(toList()); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalObjects.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.time.Instant; 23 | import java.time.LocalDate; 24 | import java.time.LocalDateTime; 25 | import java.time.LocalTime; 26 | import java.time.OffsetDateTime; 27 | import java.time.OffsetTime; 28 | import java.time.Year; 29 | import java.time.YearMonth; 30 | import java.time.ZoneId; 31 | import java.time.ZonedDateTime; 32 | import java.time.chrono.ChronoZonedDateTime; 33 | import java.time.format.*; 34 | import java.time.temporal.ChronoField; 35 | import java.time.temporal.TemporalAccessor; 36 | import java.util.Locale; 37 | import org.thymeleaf.util.Validate; 38 | 39 | /** 40 | * Utilities for the creation of Java 8 Time objects. 41 | * 42 | * @author José Miguel Samper 43 | * 44 | * @since 2.1.1 45 | */ 46 | public final class TemporalObjects { 47 | 48 | public TemporalObjects() { 49 | super(); 50 | } 51 | 52 | public static DateTimeFormatter formatterFor(final Object target, final Locale locale) { 53 | Validate.notNull(target, "Target cannot be null"); 54 | Validate.notNull(locale, "Locale cannot be null"); 55 | if (target instanceof Instant) { 56 | return new DateTimeFormatterBuilder().appendInstant().toFormatter(); 57 | } else if (target instanceof LocalDate) { 58 | return DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale); 59 | } else if (target instanceof LocalDateTime) { 60 | return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM).withLocale(locale); 61 | } else if (target instanceof LocalTime) { 62 | return DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).withLocale(locale); 63 | } else if (target instanceof OffsetDateTime) { 64 | return new DateTimeFormatterBuilder() 65 | .appendLocalized(FormatStyle.LONG, FormatStyle.MEDIUM) 66 | .appendLocalizedOffset(TextStyle.FULL) 67 | .toFormatter() 68 | .withLocale(locale); 69 | } else if (target instanceof OffsetTime) { 70 | return new DateTimeFormatterBuilder() 71 | .appendValue(ChronoField.HOUR_OF_DAY) 72 | .appendLiteral(':') 73 | .appendValue(ChronoField.MINUTE_OF_HOUR) 74 | .appendLiteral(':') 75 | .appendValue(ChronoField.SECOND_OF_MINUTE) 76 | .appendLocalizedOffset(TextStyle.FULL) 77 | .toFormatter() 78 | .withLocale(locale); 79 | } else if (target instanceof Year) { 80 | return new DateTimeFormatterBuilder() 81 | .appendValue(ChronoField.YEAR) 82 | .toFormatter(); 83 | } else if (target instanceof YearMonth) { 84 | return yearMonthFormatter(locale); 85 | } else if (target instanceof ZonedDateTime) { 86 | return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withLocale(locale); 87 | } else { 88 | throw new IllegalArgumentException( 89 | "Cannot format object of class \"" + target.getClass().getName() + "\" as a date"); 90 | } 91 | } 92 | 93 | /** 94 | * Creates a Temporal object filling the missing fields of the provided time with default values. 95 | * @param target the temporal object to be converted 96 | * @param defaultZoneId the default value for ZoneId 97 | * @return a Temporal object 98 | */ 99 | public static ChronoZonedDateTime zonedTime(final Object target, final ZoneId defaultZoneId) { 100 | Validate.notNull(target, "Target cannot be null"); 101 | Validate.notNull(defaultZoneId, "ZoneId cannot be null"); 102 | if (target instanceof Instant) { 103 | return ZonedDateTime.ofInstant((Instant) target, defaultZoneId); 104 | } else if (target instanceof LocalDate) { 105 | return ZonedDateTime.of((LocalDate) target, LocalTime.MIDNIGHT, defaultZoneId); 106 | } else if (target instanceof LocalDateTime) { 107 | return ZonedDateTime.of((LocalDateTime) target, defaultZoneId); 108 | } else if (target instanceof LocalTime) { 109 | return ZonedDateTime.of(LocalDate.now(), (LocalTime) target, defaultZoneId); 110 | } else if (target instanceof OffsetDateTime) { 111 | return ((OffsetDateTime) target).toZonedDateTime(); 112 | } else if (target instanceof OffsetTime) { 113 | LocalTime localTime = ((OffsetTime) target).toLocalTime(); 114 | return ZonedDateTime.of(LocalDate.now(), localTime, defaultZoneId); 115 | } else if (target instanceof Year) { 116 | LocalDate localDate = ((Year) target).atDay(1); 117 | return ZonedDateTime.of(localDate, LocalTime.MIDNIGHT, defaultZoneId); 118 | } else if (target instanceof YearMonth) { 119 | LocalDate localDate = ((YearMonth) target).atDay(1); 120 | return ZonedDateTime.of(localDate, LocalTime.MIDNIGHT, defaultZoneId); 121 | } else if (target instanceof ZonedDateTime) { 122 | return (ChronoZonedDateTime) target; 123 | } else { 124 | throw new IllegalArgumentException( 125 | "Cannot format object of class \"" + target.getClass().getName() + "\" as a date"); 126 | } 127 | } 128 | 129 | public static TemporalAccessor temporal(final Object target) { 130 | Validate.notNull(target, "Target cannot be null"); 131 | if (target instanceof TemporalAccessor) { 132 | return (TemporalAccessor) target; 133 | } else { 134 | throw new IllegalArgumentException( 135 | "Cannot normalize class \"" + target.getClass().getName() + "\" as a date"); 136 | } 137 | } 138 | 139 | private static DateTimeFormatter yearMonthFormatter(final Locale locale) { 140 | if (shouldDisplayYearBeforeMonth(locale)) { 141 | return new DateTimeFormatterBuilder() 142 | .appendValue(ChronoField.YEAR) 143 | .appendLiteral(' ') 144 | .appendText(ChronoField.MONTH_OF_YEAR) 145 | .toFormatter() 146 | .withLocale(locale); 147 | } else { 148 | return new DateTimeFormatterBuilder() 149 | .appendText(ChronoField.MONTH_OF_YEAR) 150 | .appendLiteral(' ') 151 | .appendValue(ChronoField.YEAR) 152 | .toFormatter() 153 | .withLocale(locale); 154 | } 155 | } 156 | 157 | private static boolean shouldDisplayYearBeforeMonth(final Locale locale) { 158 | // We use "Month Year" or "Year Month" depending on the locale according to https://en.wikipedia.org/wiki/Date_format_by_country 159 | String country = locale.getCountry(); 160 | switch (country) { 161 | case "BT" : 162 | case "CA" : 163 | case "CN" : 164 | case "KP" : 165 | case "KR" : 166 | case "TW" : 167 | case "HU" : 168 | case "IR" : 169 | case "JP" : 170 | case "LT" : 171 | case "MN" : 172 | return true; 173 | default: 174 | return false; 175 | } 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/org/thymeleaf/extras/java8time/util/TemporalSetUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * 4 | * Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org) 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * ============================================================================= 19 | */ 20 | package org.thymeleaf.extras.java8time.util; 21 | 22 | import java.time.ZoneId; 23 | import java.time.temporal.Temporal; 24 | import java.util.Locale; 25 | import java.util.Set; 26 | import java.util.function.Function; 27 | import static java.util.stream.Collectors.toSet; 28 | import org.thymeleaf.util.Validate; 29 | 30 | 31 | /** 32 | * Formatting utilities for Java 8 Time object sets. 33 | * 34 | * @author José Miguel Samper 35 | * 36 | * @since 2.1.0 37 | */ 38 | public final class TemporalSetUtils { 39 | 40 | private final TemporalFormattingUtils temporalFormattingUtils; 41 | 42 | public TemporalSetUtils(final Locale locale, final ZoneId defaultZoneId) { 43 | super(); 44 | Validate.notNull(locale, "Locale cannot be null"); 45 | Validate.notNull(defaultZoneId, "ZoneId cannot be null"); 46 | temporalFormattingUtils = new TemporalFormattingUtils(locale, defaultZoneId); 47 | } 48 | 49 | public Set setFormat(final Set target) { 50 | return setFormat(target, temporalFormattingUtils::format); 51 | } 52 | 53 | public Set setFormat(final Set target, final Locale locale) { 54 | return setFormat(target, time -> temporalFormattingUtils.format(time, locale)); 55 | } 56 | 57 | public Set setFormat(final Set target, final String pattern) { 58 | return setFormat(target, time -> temporalFormattingUtils.format(time, pattern, null)); 59 | } 60 | 61 | public Set setFormat(final Set target, final String pattern, final Locale locale) { 62 | return setFormat(target, time -> temporalFormattingUtils.format(time, pattern, locale, null)); 63 | } 64 | 65 | public Set setDay(final Set target) { 66 | return setFormat(target, temporalFormattingUtils::day); 67 | } 68 | 69 | public Set setMonth(final Set target) { 70 | return setFormat(target, temporalFormattingUtils::month); 71 | } 72 | 73 | public Set setMonthName(final Set target) { 74 | return setFormat(target, temporalFormattingUtils::monthName); 75 | } 76 | 77 | public Set setMonthNameShort(final Set target) { 78 | return setFormat(target, temporalFormattingUtils::monthNameShort); 79 | } 80 | 81 | public Set setYear(final Set target) { 82 | return setFormat(target, temporalFormattingUtils::year); 83 | } 84 | 85 | public Set setDayOfWeek(final Set target) { 86 | return setFormat(target, temporalFormattingUtils::dayOfWeek); 87 | } 88 | 89 | public Set setDayOfWeekName(final Set target) { 90 | return setFormat(target, temporalFormattingUtils::dayOfWeekName); 91 | } 92 | 93 | public Set setDayOfWeekNameShort(final Set target) { 94 | return setFormat(target, temporalFormattingUtils::dayOfWeekNameShort); 95 | } 96 | 97 | public Set setHour(final Set target) { 98 | return setFormat(target, temporalFormattingUtils::hour); 99 | } 100 | 101 | public Set setMinute(final Set target) { 102 | return setFormat(target, temporalFormattingUtils::minute); 103 | } 104 | 105 | public Set setSecond(final Set target) { 106 | return setFormat(target, temporalFormattingUtils::second); 107 | } 108 | 109 | public Set setNanosecond(final Set target) { 110 | return setFormat(target, temporalFormattingUtils::nanosecond); 111 | } 112 | 113 | public Set setFormatISO(final Set target) { 114 | return setFormat(target, temporalFormattingUtils::formatISO); 115 | } 116 | 117 | private Set setFormat( 118 | final Set target, final Function mapFunction) { 119 | Validate.notNull(target, "Target cannot be null"); 120 | return target.stream() 121 | .map(time -> mapFunction.apply(time)) 122 | .collect(toSet()); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/resources/org/thymeleaf/extras/java8time/Java8Time.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 27 | 28 | 29 | https://github.com/thymeleaf/thymeleaf-extras-java8time 31 | ]]> 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/resources/temporal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thymeleaf/thymeleaf-extras-java8time/6c270d0804f167d7ba13dabd49429a1b81c9d3f5/src/main/resources/temporal.png -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsArrayTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import org.junit.Test; 19 | 20 | import java.time.LocalDate; 21 | import java.time.LocalDateTime; 22 | import java.time.ZoneOffset; 23 | import java.time.temporal.Temporal; 24 | import java.util.Locale; 25 | 26 | import static org.junit.Assert.assertArrayEquals; 27 | 28 | /** 29 | * Tests regarding formatting of arrays of temporal objects. 30 | */ 31 | public class TemporalsArrayTest { 32 | 33 | private final Temporals temporals = new Temporals(Locale.US, ZoneOffset.UTC); 34 | 35 | @Test 36 | public void testArrayFormat() { 37 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 38 | String[] expected = {"January 1, 2015", "December 31, 2015"}; 39 | assertArrayEquals(expected, temporals.arrayFormat(array)); 40 | } 41 | 42 | @Test 43 | public void testArrayFormatWithLocale() { 44 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 45 | String[] expected = {"1. Januar 2015", "31. Dezember 2015"}; 46 | assertArrayEquals(expected, temporals.arrayFormat(array, Locale.GERMANY)); 47 | } 48 | 49 | @Test 50 | public void testArrayFormatWithPattern() { 51 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 52 | String pattern = "yyyy-MM-dd"; 53 | String[] expected = {"2015-01-01", "2015-12-31"}; 54 | assertArrayEquals(expected, temporals.arrayFormat(array, pattern)); 55 | } 56 | 57 | @Test 58 | public void testArrayFormatWithPatternAndLocale() { 59 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 60 | String pattern = "EEEE, d MMMM, yyyy"; 61 | String[] expected = {"Donnerstag, 1 Januar, 2015", "Donnerstag, 31 Dezember, 2015"}; 62 | assertArrayEquals(expected, temporals.arrayFormat(array, pattern, Locale.GERMANY)); 63 | } 64 | 65 | @Test 66 | public void testArrayDay() { 67 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 68 | Integer[] expected = {1, 31}; 69 | assertArrayEquals(expected, temporals.arrayDay(array)); 70 | } 71 | 72 | @Test 73 | public void testArrayMonth() { 74 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 75 | Integer[] expected = {1, 12}; 76 | assertArrayEquals(expected, temporals.arrayMonth(array)); 77 | } 78 | 79 | @Test 80 | public void testArrayMonthName() { 81 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 82 | String[] expected = {"January", "December"}; 83 | assertArrayEquals(expected, temporals.arrayMonthName(array)); 84 | } 85 | 86 | @Test 87 | public void testArrayMonthNameShort() { 88 | Temporal[] array = {LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)}; 89 | String[] expected = {"Jan", "Dec"}; 90 | assertArrayEquals(expected, temporals.arrayMonthNameShort(array)); 91 | } 92 | 93 | @Test 94 | public void testArrayYear() { 95 | Temporal[] array = {LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)}; 96 | Integer[] expected = {2014, 2015}; 97 | assertArrayEquals(expected, temporals.arrayYear(array)); 98 | } 99 | 100 | @Test 101 | public void testArrayDayOfWeek() { 102 | Temporal[] array = {LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)}; 103 | Integer[] expected = {3, 4}; 104 | assertArrayEquals(expected, temporals.arrayDayOfWeek(array)); 105 | } 106 | 107 | @Test 108 | public void testArrayDayOfWeekName() { 109 | Temporal[] array = {LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)}; 110 | String[] expected = {"Wednesday", "Thursday"}; 111 | assertArrayEquals(expected, temporals.arrayDayOfWeekName(array)); 112 | } 113 | 114 | @Test 115 | public void testArrayDayOfWeekNameShort() { 116 | Temporal[] array = {LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)}; 117 | String[] expected = {"Wed", "Thu"}; 118 | assertArrayEquals(expected, temporals.arrayDayOfWeekNameShort(array)); 119 | } 120 | 121 | @Test 122 | public void testArrayHour() { 123 | Temporal[] array = {LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)}; 124 | Integer[] expected = {1, 23}; 125 | assertArrayEquals(expected, temporals.arrayHour(array)); 126 | } 127 | 128 | @Test 129 | public void testArrayMinute() { 130 | Temporal[] array = {LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)}; 131 | Integer[] expected = {1, 59}; 132 | assertArrayEquals(expected, temporals.arrayMinute(array)); 133 | } 134 | 135 | @Test 136 | public void testArraySecond() { 137 | Temporal[] array = {LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)}; 138 | Integer[] expected = {1, 10}; 139 | assertArrayEquals(expected, temporals.arraySecond(array)); 140 | } 141 | 142 | @Test 143 | public void testArrayNanosecond() { 144 | Temporal[] array = {LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)}; 145 | Integer[] expected = {1, 9}; 146 | assertArrayEquals(expected, temporals.arrayNanosecond(array)); 147 | } 148 | 149 | @Test 150 | public void testFormatISO() { 151 | Temporal[] array = { 152 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)}; 153 | String[] expected = {"2015-01-01T01:01:01.000+0000", "2015-12-31T23:59:10.000+0000"}; 154 | assertArrayEquals(expected, temporals.arrayFormatISO(array)); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsClassesFormattingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import java.time.Instant; 19 | import java.time.LocalDate; 20 | import java.time.LocalDateTime; 21 | import java.time.LocalTime; 22 | import java.time.OffsetDateTime; 23 | import java.time.OffsetTime; 24 | import java.time.Year; 25 | import java.time.YearMonth; 26 | import java.time.ZoneOffset; 27 | import java.time.ZonedDateTime; 28 | import java.time.temporal.Temporal; 29 | import java.util.Locale; 30 | import org.junit.Test; 31 | import static org.junit.Assert.*; 32 | 33 | /** 34 | * Tests regarding formatting of all temporal classes. 35 | */ 36 | public class TemporalsClassesFormattingTest { 37 | 38 | private final Temporals temporals = new Temporals(Locale.US, ZoneOffset.UTC); 39 | 40 | @Test 41 | public void localDate() { 42 | Temporal time = LocalDate.of(2015, 12, 31); 43 | assertEquals("December 31, 2015", temporals.format(time)); 44 | } 45 | 46 | @Test 47 | public void localDateTime() { 48 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45); 49 | assertEquals("December 31, 2015, 11:59:45 PM", temporals.format(time)); 50 | } 51 | 52 | @Test 53 | public void zonedDateTime() { 54 | Temporal time = ZonedDateTime.of(2015, 12, 31, 23, 59, 45, 0, ZoneOffset.UTC); 55 | assertEquals("December 31, 2015 at 11:59:45 PM Z", temporals.format(time)); 56 | } 57 | 58 | @Test 59 | public void instant() { 60 | Temporal time = Instant.ofEpochSecond(1); 61 | // Default formatting for Instant 62 | assertEquals("1970-01-01T00:00:01Z", temporals.format(time)); 63 | } 64 | 65 | @Test 66 | public void localTime() { 67 | Temporal time = LocalTime.of(23, 59, 45); 68 | assertEquals("11:59:45 PM", temporals.format(time)); 69 | } 70 | 71 | @Test 72 | public void offsetTime() { 73 | Temporal time = OffsetTime.of(23, 59, 45, 0, ZoneOffset.MAX); 74 | assertEquals("23:59:45GMT+18:00", temporals.format(time)); 75 | } 76 | 77 | @Test 78 | public void offsetDateTime() { 79 | Temporal time = OffsetDateTime.of(2015, 12, 31, 23, 59, 45, 0, ZoneOffset.MAX); 80 | assertEquals("December 31, 2015, 11:59:45 PMGMT+18:00", temporals.format(time, Locale.US)); 81 | assertEquals("31. Dezember 2015, 23:59:45GMT+18:00", temporals.format(time, Locale.GERMANY)); 82 | } 83 | 84 | @Test 85 | public void year() { 86 | Temporal time = Year.of(2015); 87 | assertEquals("2015", temporals.format(time)); 88 | } 89 | 90 | @Test 91 | public void yearMonth() { 92 | Temporal time = YearMonth.of(2015, 12); 93 | assertEquals("December 2015", temporals.format(time, Locale.US)); 94 | } 95 | 96 | @Test 97 | public void yearMonthForYMDLocales() { 98 | Temporal time = YearMonth.of(2015, 12); 99 | assertEquals("2015 December", temporals.format(time, Locale.CANADA)); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsCreationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import java.time.LocalDate; 19 | import java.time.LocalDateTime; 20 | import java.time.ZoneOffset; 21 | import java.time.ZonedDateTime; 22 | import java.time.temporal.Temporal; 23 | import java.util.Locale; 24 | import org.junit.Test; 25 | import static org.junit.Assert.*; 26 | 27 | public class TemporalsCreationTest { 28 | 29 | private final Temporals temporals = new Temporals(Locale.US); 30 | 31 | @Test 32 | public void testCreateLocalDate() { 33 | Temporal temporal = temporals.create(2015, 12, 31); 34 | assertNotNull(temporal); 35 | assertTrue(temporal instanceof LocalDate); 36 | LocalDate date = (LocalDate) temporal; 37 | assertEquals(2015, date.getYear()); 38 | assertEquals( 12, date.getMonthValue()); 39 | assertEquals( 31, date.getDayOfMonth()); 40 | } 41 | 42 | @Test 43 | public void testCreateLocalDateTime() { 44 | Temporal temporal = temporals.create(2015, 12, 31, 23, 59); 45 | assertNotNull(temporal); 46 | assertTrue(temporal instanceof LocalDateTime); 47 | LocalDateTime time = (LocalDateTime) temporal; 48 | assertEquals(2015, time.getYear()); 49 | assertEquals( 12, time.getMonthValue()); 50 | assertEquals( 31, time.getDayOfMonth()); 51 | assertEquals( 23, time.getHour()); 52 | assertEquals( 59, time.getMinute()); 53 | assertEquals( 0, time.getSecond()); 54 | assertEquals( 0, time.getNano()); 55 | } 56 | 57 | @Test 58 | public void testCreateLocalDateTimeWithSeconds() { 59 | Temporal temporal = temporals.create(2015, 12, 31, 23, 59, 45); 60 | assertNotNull(temporal); 61 | assertTrue(temporal instanceof LocalDateTime); 62 | LocalDateTime time = (LocalDateTime) temporal; 63 | assertEquals(2015, time.getYear()); 64 | assertEquals( 12, time.getMonthValue()); 65 | assertEquals( 31, time.getDayOfMonth()); 66 | assertEquals( 23, time.getHour()); 67 | assertEquals( 59, time.getMinute()); 68 | assertEquals( 45, time.getSecond()); 69 | assertEquals( 0, time.getNano()); 70 | } 71 | 72 | @Test 73 | public void testCreateLocalDateTimeWithMilliseconds() { 74 | Temporal temporal = temporals.create(2015, 12, 31, 23, 59, 45, 999); 75 | assertNotNull(temporal); 76 | assertTrue(temporal instanceof LocalDateTime); 77 | LocalDateTime time = (LocalDateTime) temporal; 78 | assertEquals(2015, time.getYear()); 79 | assertEquals( 12, time.getMonthValue()); 80 | assertEquals( 31, time.getDayOfMonth()); 81 | assertEquals( 23, time.getHour()); 82 | assertEquals( 59, time.getMinute()); 83 | assertEquals( 45, time.getSecond()); 84 | assertEquals( 999, time.getNano()); 85 | } 86 | 87 | @Test 88 | public void testCreateNow() { 89 | Temporal temporal = temporals.createNow(); 90 | assertNotNull(temporal); 91 | assertTrue(temporal instanceof LocalDateTime); 92 | } 93 | 94 | @Test 95 | public void testCreateNowForTimeZone() { 96 | Temporal temporal = temporals.createNowForTimeZone(ZoneOffset.UTC); 97 | assertNotNull(temporal); 98 | assertTrue(temporal instanceof ZonedDateTime); 99 | ZonedDateTime time = (ZonedDateTime) temporal; 100 | assertEquals(ZoneOffset.UTC, time.getZone()); 101 | } 102 | 103 | @Test 104 | public void testCreateToday() { 105 | Temporal temporal = temporals.createToday(); 106 | assertNotNull(temporal); 107 | assertTrue(temporal instanceof LocalDate); 108 | } 109 | 110 | @Test 111 | public void testCreateTodayForTimeZone() { 112 | Temporal temporal = temporals.createTodayForTimeZone(ZoneOffset.UTC); 113 | assertNotNull(temporal); 114 | assertTrue(temporal instanceof ZonedDateTime); 115 | ZonedDateTime time = (ZonedDateTime) temporal; 116 | assertEquals(ZoneOffset.UTC, time.getZone()); 117 | assertEquals(0, time.getHour()); 118 | assertEquals(0, time.getMinute()); 119 | assertEquals(0, time.getSecond()); 120 | assertEquals(0, time.getNano()); 121 | } 122 | 123 | @Test 124 | public void testCreateDate() { 125 | Temporal temporal = temporals.createDate("2015-12-31"); 126 | assertNotNull(temporal); 127 | assertTrue(temporal instanceof LocalDate); 128 | LocalDate date = (LocalDate) temporal; 129 | assertEquals(2015, date.getYear()); 130 | assertEquals( 12, date.getMonthValue()); 131 | assertEquals( 31, date.getDayOfMonth()); 132 | } 133 | 134 | @Test 135 | public void testCreateDateTime() { 136 | Temporal temporal = temporals.createDateTime("2015-12-31T23:59:00"); 137 | assertNotNull(temporal); 138 | assertTrue(temporal instanceof LocalDateTime); 139 | LocalDateTime time = (LocalDateTime) temporal; 140 | assertEquals(2015, time.getYear()); 141 | assertEquals( 12, time.getMonthValue()); 142 | assertEquals( 31, time.getDayOfMonth()); 143 | assertEquals( 23, time.getHour()); 144 | assertEquals( 59, time.getMinute()); 145 | assertEquals( 0, time.getSecond()); 146 | assertEquals( 0, time.getNano()); 147 | } 148 | 149 | @Test 150 | public void testCreateDateWithPattern() { 151 | Temporal temporal = temporals.createDate("31.12.2015", "dd.MM.yyyy"); 152 | assertNotNull(temporal); 153 | assertTrue(temporal instanceof LocalDate); 154 | LocalDate date = (LocalDate) temporal; 155 | assertEquals(2015, date.getYear()); 156 | assertEquals( 12, date.getMonthValue()); 157 | assertEquals( 31, date.getDayOfMonth()); 158 | } 159 | @Test 160 | public void testCreateDateTimeWithPattern() { 161 | Temporal temporal = temporals.createDateTime("31.12.2015 23:59", "dd.MM.yyyy HH:mm"); 162 | assertNotNull(temporal); 163 | assertTrue(temporal instanceof LocalDateTime); 164 | LocalDateTime time = (LocalDateTime) temporal; 165 | assertEquals(2015, time.getYear()); 166 | assertEquals( 12, time.getMonthValue()); 167 | assertEquals( 31, time.getDayOfMonth()); 168 | assertEquals( 23, time.getHour()); 169 | assertEquals( 59, time.getMinute()); 170 | assertEquals( 0, time.getSecond()); 171 | assertEquals( 0, time.getNano()); 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsFormattingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import org.junit.Test; 19 | 20 | import java.time.*; 21 | import java.time.temporal.Temporal; 22 | import java.util.Locale; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertNull; 26 | 27 | /** 28 | * Tests regarding formatting of temporal objects. 29 | */ 30 | public class TemporalsFormattingTest { 31 | 32 | private final Temporals temporals = new Temporals(Locale.US, ZoneOffset.UTC); 33 | 34 | @Test 35 | public void testFormat() { 36 | Temporal time = ZonedDateTime.of(2015, 12, 31, 23, 59, 45, 0, ZoneOffset.UTC); 37 | assertEquals("December 31, 2015 at 11:59:45 PM Z", temporals.format(time)); 38 | } 39 | 40 | @Test 41 | public void testFormatWithNullTemporal() { 42 | assertNull(temporals.format(null)); 43 | } 44 | 45 | @Test 46 | public void testFormatWithLocale() { 47 | Temporal time = ZonedDateTime.of(2015, 12, 31, 23, 59, 45, 0, ZoneOffset.UTC); 48 | assertEquals("31. Dezember 2015 um 23:59:45 Z", temporals.format(time, Locale.GERMANY)); 49 | } 50 | 51 | @Test 52 | public void testFormatWithLocaleAndNullTemporal() { 53 | assertNull(temporals.format(null, Locale.GERMANY)); 54 | } 55 | 56 | @Test 57 | public void testFormatWithPattern() { 58 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59); 59 | String pattern = "yyyy-MM-dd HH:mm:ss"; 60 | String expectd = "2015-12-31 23:59:00"; 61 | assertEquals(expectd, temporals.format(time, pattern)); 62 | } 63 | 64 | @Test 65 | public void testFormatWithPatternAndZone() { 66 | Temporal time = ZonedDateTime.of(2015, 12, 31, 23, 59, 0, 1, ZoneOffset.UTC); 67 | String pattern = "yyyy-MM-dd HH:mm:ss"; 68 | String expectd = "2015-12-31 18:59:00"; 69 | assertEquals(expectd, temporals.format(time, pattern, "Etc/GMT+5")); 70 | } 71 | 72 | @Test 73 | public void testFormatStandardPattern() { 74 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59); 75 | assertEquals("12/31/15, 11:59 PM", temporals.format(time, "SHORT", Locale.US)); 76 | assertEquals("Dec 31, 2015, 11:59:00 PM", temporals.format(time, "MEDIUM", Locale.US)); 77 | assertEquals("December 31, 2015 at 11:59:00 PM Z", temporals.format(time, "LONG", Locale.US)); 78 | assertEquals("Thursday, December 31, 2015 at 11:59:00 PM Z", temporals.format(time, "FULL", Locale.US)); 79 | } 80 | 81 | @Test 82 | public void testFormatWithPatternAndNullTemporal() { 83 | assertNull(temporals.format(null, "y")); 84 | } 85 | 86 | @Test 87 | public void testFormatWithPatternAndLocale() { 88 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59); 89 | String pattern = "EEEE, d MMMM, yyyy"; 90 | String expectd = "Donnerstag, 31 Dezember, 2015"; 91 | assertEquals(expectd, temporals.format(time, pattern, Locale.GERMANY)); 92 | } 93 | 94 | @Test 95 | public void testFormatWithPatternAndLocaleAndNullTemporal() { 96 | assertNull(temporals.format(null, "y", Locale.GERMANY)); 97 | } 98 | 99 | @Test 100 | public void localTimeWithPattern() { 101 | Temporal time = LocalTime.of(23, 59, 45); 102 | assertEquals("23:59:45", temporals.format(time, "HH:mm:ss")); 103 | } 104 | 105 | @Test 106 | public void offsetDateTimeWithPattern() { 107 | OffsetDateTime time = OffsetDateTime.of(LocalDateTime.of(2015, 12, 31, 23, 59, 45), ZoneOffset.UTC); 108 | assertEquals("12/31/2015 23:59:45", temporals.format(time, "MM/dd/yyyy HH:mm:ss")); 109 | } 110 | 111 | @Test 112 | public void offsetTimeWithPattern() { 113 | OffsetTime time = OffsetTime.of(LocalTime.of(23, 59, 45), ZoneOffset.UTC); 114 | assertEquals("23:59:45", temporals.format(time, "HH:mm:ss")); 115 | } 116 | 117 | @Test 118 | public void yearWithPattern() { 119 | Year time = Year.of(2015); 120 | assertEquals("2015", temporals.format(time, "yyyy")); 121 | } 122 | 123 | @Test 124 | public void yearMonthWithPattern() { 125 | YearMonth time = YearMonth.of(2015, 12); 126 | assertEquals("12/2015", temporals.format(time, "MM/yyyy")); 127 | } 128 | 129 | @Test 130 | public void testDay() { 131 | Temporal time = LocalDate.of(2015, 12, 31); 132 | assertEquals(31, temporals.day(time).intValue()); 133 | } 134 | 135 | @Test 136 | public void testDayWithNullTemporal() { 137 | assertNull(temporals.day(null)); 138 | } 139 | 140 | @Test 141 | public void testMonth() { 142 | Temporal time = LocalDate.of(2015, 12, 31); 143 | assertEquals(12, temporals.month(time).intValue()); 144 | } 145 | 146 | @Test 147 | public void testMonthWithNullTemporal() { 148 | assertNull(temporals.month(null)); 149 | } 150 | 151 | @Test 152 | public void testMonthName() { 153 | Temporal time = LocalDate.of(2015, 12, 31); 154 | assertEquals("December", temporals.monthName(time)); 155 | } 156 | 157 | @Test 158 | public void testMonthNameWithNullTemporal() { 159 | assertNull(temporals.monthName(null)); 160 | } 161 | 162 | @Test 163 | public void testMonthNameShort() { 164 | Temporal time = LocalDate.of(2015, 12, 31); 165 | assertEquals("Dec", temporals.monthNameShort(time)); 166 | } 167 | 168 | @Test 169 | public void testMonthNameShortWithNullTemporal() { 170 | assertNull(temporals.monthNameShort(null)); 171 | } 172 | 173 | @Test 174 | public void testYear() { 175 | Temporal time = LocalDate.of(2015, 12, 31); 176 | assertEquals(2015, temporals.year(time).intValue()); 177 | } 178 | 179 | @Test 180 | public void testYearWithNullTemporal() { 181 | assertNull(temporals.year(null)); 182 | } 183 | 184 | @Test 185 | public void testDayOfWeek() { 186 | Temporal time = LocalDate.of(2015, 12, 31); 187 | assertEquals(4, temporals.dayOfWeek(time).intValue()); 188 | } 189 | 190 | @Test 191 | public void testDayOfWeekWithNullTemporal() { 192 | assertNull(temporals.dayOfWeek(null)); 193 | } 194 | 195 | @Test 196 | public void testDayOfWeekName() { 197 | Temporal time = LocalDate.of(2015, 12, 31); 198 | assertEquals("Thursday", temporals.dayOfWeekName(time)); 199 | } 200 | 201 | @Test 202 | public void testDayOfWeekNameWithNullTemporal() { 203 | assertNull(temporals.dayOfWeekName(null)); 204 | } 205 | 206 | @Test 207 | public void testDayOfWeekNameShort() { 208 | Temporal time = LocalDate.of(2015, 12, 31); 209 | assertEquals("Thu", temporals.dayOfWeekNameShort(time)); 210 | } 211 | 212 | @Test 213 | public void testDayOfWeekNameShortWithNullTemporal() { 214 | assertNull(temporals.dayOfWeekNameShort(null)); 215 | } 216 | 217 | @Test 218 | public void testHour() { 219 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45, 1); 220 | assertEquals(23, temporals.hour(time).intValue()); 221 | } 222 | 223 | @Test 224 | public void testHourWithNullTemporal() { 225 | assertNull(temporals.hour(null)); 226 | } 227 | 228 | @Test 229 | public void testMinute() { 230 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45, 1); 231 | assertEquals(59, temporals.minute(time).intValue()); 232 | } 233 | 234 | @Test 235 | public void testMinuteWithNullTemporal() { 236 | assertNull(temporals.minute(null)); 237 | } 238 | 239 | @Test 240 | public void testSecond() { 241 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45, 1); 242 | assertEquals(45, temporals.second(time).intValue()); 243 | } 244 | 245 | @Test 246 | public void testSecondWithNullTemporal() { 247 | assertNull(temporals.second(null)); 248 | } 249 | 250 | @Test 251 | public void testNanosecond() { 252 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45, 1); 253 | assertEquals(1, temporals.nanosecond(time).intValue()); 254 | } 255 | 256 | @Test 257 | public void testNanosecondWithNullTemporal() { 258 | assertNull(temporals.nanosecond(null)); 259 | } 260 | 261 | @Test 262 | public void testFormatISO() { 263 | Temporal time = LocalDateTime.of(2015, 12, 31, 23, 59, 45, 1).atZone(ZoneOffset.MAX); 264 | assertEquals("2015-12-31T23:59:45.000+1800", temporals.formatISO(time)); 265 | } 266 | 267 | @Test 268 | public void testFormatISOWithNullTemporal() { 269 | assertNull(temporals.formatISO(null)); 270 | } 271 | 272 | @Test 273 | // https://github.com/thymeleaf/thymeleaf-extras-java8time/issues/17 274 | public void testIssue17() { 275 | Instant time = Instant.ofEpochSecond(1); 276 | assertEquals("1970-01-01", temporals.format(time, "yyyy-MM-dd", Locale.US)); 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsListTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import java.time.LocalDate; 19 | import java.time.LocalDateTime; 20 | import java.time.ZoneOffset; 21 | import java.time.temporal.Temporal; 22 | import static java.util.Arrays.asList; 23 | import java.util.List; 24 | import java.util.Locale; 25 | import org.junit.Test; 26 | import static org.junit.Assert.*; 27 | 28 | /** 29 | * Tests regarding formatting of lists of temporal objects. 30 | */ 31 | public class TemporalsListTest { 32 | 33 | private final Temporals temporals = new Temporals(Locale.US, ZoneOffset.UTC); 34 | 35 | @Test 36 | public void testListFormat() { 37 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 38 | List expected = asList("January 1, 2015", "December 31, 2015"); 39 | assertEquals(expected, temporals.listFormat(list)); 40 | } 41 | 42 | @Test 43 | public void testListFormatWithLocale() { 44 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 45 | List expected = asList("1. Januar 2015", "31. Dezember 2015"); 46 | assertEquals(expected, temporals.listFormat(list, Locale.GERMANY)); 47 | } 48 | 49 | @Test 50 | public void testListFormatWithPattern() { 51 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 52 | String pattern = "yyyy-MM-dd"; 53 | List expected = asList("2015-01-01", "2015-12-31"); 54 | assertEquals(expected, temporals.listFormat(list, pattern)); 55 | } 56 | 57 | @Test 58 | public void testListFormatWithPatternAndLocale() { 59 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 60 | String pattern = "EEEE, d MMMM, yyyy"; 61 | List expected = asList("Donnerstag, 1 Januar, 2015", "Donnerstag, 31 Dezember, 2015"); 62 | assertEquals(expected, temporals.listFormat(list, pattern, Locale.GERMANY)); 63 | } 64 | 65 | @Test 66 | public void testListDay() { 67 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 68 | List expected = asList(1, 31); 69 | assertEquals(expected, temporals.listDay(list)); 70 | } 71 | 72 | @Test 73 | public void testListMonth() { 74 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 75 | List expected = asList(1, 12); 76 | assertEquals(expected, temporals.listMonth(list)); 77 | } 78 | 79 | @Test 80 | public void testListMonthName() { 81 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 82 | List expected = asList("January", "December"); 83 | assertEquals(expected, temporals.listMonthName(list)); 84 | } 85 | 86 | @Test 87 | public void testListMonthNameShort() { 88 | List list = asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31)); 89 | List expected = asList("Jan", "Dec"); 90 | assertEquals(expected, temporals.listMonthNameShort(list)); 91 | } 92 | 93 | @Test 94 | public void testListYear() { 95 | List list = asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)); 96 | List expected = asList(2014, 2015); 97 | assertEquals(expected, temporals.listYear(list)); 98 | } 99 | 100 | @Test 101 | public void testListDayOfWeek() { 102 | List list = asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)); 103 | List expected = asList(3, 4); 104 | assertEquals(expected, temporals.listDayOfWeek(list)); 105 | } 106 | 107 | @Test 108 | public void testListDayOfWeekName() { 109 | List list = asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)); 110 | List expected = asList("Wednesday", "Thursday"); 111 | assertEquals(expected, temporals.listDayOfWeekName(list)); 112 | } 113 | 114 | @Test 115 | public void testListDayOfWeekNameShort() { 116 | List list = asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31)); 117 | List expected = asList("Wed", "Thu"); 118 | assertEquals(expected, temporals.listDayOfWeekNameShort(list)); 119 | } 120 | 121 | @Test 122 | public void testListHour() { 123 | List list = asList(LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)); 124 | List expected = asList(1, 23); 125 | assertEquals(expected, temporals.listHour(list)); 126 | } 127 | 128 | @Test 129 | public void testListMinute() { 130 | List list = asList(LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)); 131 | List expected = asList(1, 59); 132 | assertEquals(expected, temporals.listMinute(list)); 133 | } 134 | 135 | @Test 136 | public void testListSecond() { 137 | List list = asList(LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)); 138 | List expected = asList(1, 10); 139 | assertEquals(expected, temporals.listSecond(list)); 140 | } 141 | 142 | @Test 143 | public void testListNanosecond() { 144 | List list = asList(LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)); 145 | List expected = asList(1, 9); 146 | assertEquals(expected, temporals.listNanosecond(list)); 147 | } 148 | 149 | @Test 150 | public void testListFormatISO() { 151 | List list = asList(LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9)); 152 | List expected = asList("2015-01-01T01:01:01.000+0000", "2015-12-31T23:59:10.000+0000"); 153 | assertEquals(expected, temporals.listFormatISO(list)); 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/test/java/org/thymeleaf/extras/java8time/expression/TemporalsSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The THYMELEAF team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.thymeleaf.extras.java8time.expression; 17 | 18 | import java.time.LocalDate; 19 | import java.time.LocalDateTime; 20 | import java.time.ZoneOffset; 21 | import java.time.temporal.Temporal; 22 | import static java.util.Arrays.asList; 23 | import java.util.Locale; 24 | import java.util.Set; 25 | import java.util.TreeSet; 26 | import org.junit.Test; 27 | import static org.junit.Assert.*; 28 | 29 | /** 30 | * Tests regarding formatting of sets of temporal objects. 31 | */ 32 | public class TemporalsSetTest { 33 | 34 | private final Temporals temporals = new Temporals(Locale.US, ZoneOffset.UTC); 35 | 36 | @Test 37 | public void testSetFormat() { 38 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 39 | Set expected = new TreeSet<>(asList("January 1, 2015", "December 31, 2015")); 40 | assertEquals(expected, temporals.setFormat(set)); 41 | } 42 | 43 | @Test 44 | public void testSetFormatWithLocale() { 45 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 46 | Set expected = new TreeSet<>(asList("1. Januar 2015", "31. Dezember 2015")); 47 | assertEquals(expected, temporals.setFormat(set, Locale.GERMANY)); 48 | } 49 | 50 | @Test 51 | public void testSetFormatWithPattern() { 52 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 53 | String pattern = "yyyy-MM-dd"; 54 | Set expected = new TreeSet<>(asList("2015-01-01", "2015-12-31")); 55 | assertEquals(expected, temporals.setFormat(set, pattern)); 56 | } 57 | 58 | @Test 59 | public void testSetFormatWithPatternAndLocale() { 60 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 61 | String pattern = "EEEE, d MMMM, yyyy"; 62 | Set expected = new TreeSet<>(asList("Donnerstag, 1 Januar, 2015", "Donnerstag, 31 Dezember, 2015")); 63 | assertEquals(expected, temporals.setFormat(set, pattern, Locale.GERMANY)); 64 | } 65 | 66 | @Test 67 | public void testSetDay() { 68 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 69 | Set expected = new TreeSet<>(asList(1, 31)); 70 | assertEquals(expected, temporals.setDay(set)); 71 | } 72 | 73 | @Test 74 | public void testSetMonth() { 75 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 76 | Set expected = new TreeSet<>(asList(1, 12)); 77 | assertEquals(expected, temporals.setMonth(set)); 78 | } 79 | 80 | @Test 81 | public void testSetMonthName() { 82 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 83 | Set expected = new TreeSet<>(asList("January", "December")); 84 | assertEquals(expected, temporals.setMonthName(set)); 85 | } 86 | 87 | @Test 88 | public void testSetMonthNameShort() { 89 | Set set = new TreeSet<>(asList(LocalDate.of(2015, 1, 1), LocalDate.of(2015, 12, 31))); 90 | Set expected = new TreeSet<>(asList("Jan", "Dec")); 91 | assertEquals(expected, temporals.setMonthNameShort(set)); 92 | } 93 | 94 | @Test 95 | public void testSetYear() { 96 | Set set = new TreeSet<>(asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31))); 97 | Set expected = new TreeSet<>(asList(2014, 2015)); 98 | assertEquals(expected, temporals.setYear(set)); 99 | } 100 | 101 | @Test 102 | public void testSetDayOfWeek() { 103 | Set set = new TreeSet<>(asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31))); 104 | Set expected = new TreeSet<>(asList(3, 4)); 105 | assertEquals(expected, temporals.setDayOfWeek(set)); 106 | } 107 | 108 | @Test 109 | public void testSetDayOfWeekName() { 110 | Set set = new TreeSet<>(asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31))); 111 | Set expected = new TreeSet<>(asList("Wednesday", "Thursday")); 112 | assertEquals(expected, temporals.setDayOfWeekName(set)); 113 | } 114 | 115 | @Test 116 | public void testSetDayOfWeekNameShort() { 117 | Set set = new TreeSet<>(asList(LocalDate.of(2014, 1, 1), LocalDate.of(2015, 12, 31))); 118 | Set expected = new TreeSet<>(asList("Wed", "Thu")); 119 | assertEquals(expected, temporals.setDayOfWeekNameShort(set)); 120 | } 121 | 122 | @Test 123 | public void testSetHour() { 124 | Set set = new TreeSet<>(asList( 125 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9))); 126 | Set expected = new TreeSet<>(asList(1, 23)); 127 | assertEquals(expected, temporals.setHour(set)); 128 | } 129 | 130 | @Test 131 | public void testSetMinute() { 132 | Set set = new TreeSet<>(asList( 133 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9))); 134 | Set expected = new TreeSet<>(asList(1, 59)); 135 | assertEquals(expected, temporals.setMinute(set)); 136 | } 137 | 138 | @Test 139 | public void testSetSecond() { 140 | Set set = new TreeSet<>(asList( 141 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9))); 142 | Set expected = new TreeSet<>(asList(1, 10)); 143 | assertEquals(expected, temporals.setSecond(set)); 144 | } 145 | 146 | @Test 147 | public void testSetNanosecond() { 148 | Set set = new TreeSet<>(asList( 149 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9))); 150 | Set expected = new TreeSet<>(asList(1, 9)); 151 | assertEquals(expected, temporals.setNanosecond(set)); 152 | } 153 | 154 | @Test 155 | public void testSetFormatISO() { 156 | Set set = new TreeSet<>(asList( 157 | LocalDateTime.of(2015, 1, 1, 1, 1, 1, 1), LocalDateTime.of(2015, 12, 31, 23, 59, 10, 9))); 158 | Set expected = new TreeSet<>(asList("2015-01-01T01:01:01.000+0000", "2015-12-31T23:59:10.000+0000")); 159 | assertEquals(expected, temporals.setFormatISO(set)); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=warn 2 | org.slf4j.simpleLogger.logFile=System.out 3 | org.slf4j.simpleLogger.showDateTime=true 4 | org.slf4j.simpleLogger.levelInBrackets=true 5 | 6 | org.slf4j.simpleLogger.log.org.thymeleaf=trace 7 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.CONFIG=debug 8 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.TIMER=debug 9 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=debug 10 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.cache.FRAGMENT_CACHE=debug 11 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.cache.MESSAGE_CACHE=debug 12 | org.slf4j.simpleLogger.log.org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE=debug -------------------------------------------------------------------------------- /src/test/resources/test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | Testing conditional comments 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 |

21 | This works! 22 |

23 | 24 | 25 | 26 | 27 | 28 | --------------------------------------------------------------------------------