Spring + Thymeleaf: time in user's timezone
Asked Answered
R

6

6

How can I print date and time is specified timezone with Thymeleaf? Something like:

<span th:text="${#dates.format(myDate, 'yyyy-MM-dd HH:mm', 'PST')}">2010-01-01 16:30</span>
Rework answered 30/3, 2014 at 11:19 Comment(3)
What version of Thymeleaf are you using? As of version 2.1, this is implemented in both #dates.format and #calendars.format: linkBartie
@Bartie 2.1. How do I use it? There is also an unanswered question in that issue asking "how do a perform a format using a specific timezone using this fix?"Rework
This works: <td th:text="${ticket.ticketDate} ? ${#dates.format(ticket.ticketDate, 'dd/MM/yyyy zzz')} : ''">Ticket Date</td>. Response that I get is 31/03/2014 CET... So it picks up locale. Or you want to tell Thymeleaf which locale to use?Bartie
B
4

As I was puzzled by this question, I searched extensively for possible solutions.

These are my findings: I did not found any clean function for changing timezone and displaying it like it is in jsp:

<fmt:timeZone value="US">
<fmt:formatDate value="${today}"  type="both" />
</fmt:timeZone>

Possible solution, that works would be to create calendar instance using createForTimeZone and format it, since it returns a raw calendar value, so from this:

#calendars.createForTimeZone(year, month, day, hour, minute, second, milisecond, Object timezone)

you would get something like this:

java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="PST",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=PST,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=1,WEEK_OF_YEAR=14,WEEK_OF_MONTH=1,DAY_OF_MONTH=24,DAY_OF_YEAR=91,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=6,HOUR_OF_DAY=7,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

As you can see (you have to look carefully) it did converted time to the timezone provided.

Now, I still haven't gotten to the point where I can get it all to work fine, but if you add calendars.format in front of this, you would get it to properly show time in the given timezone. ${#calendars.format(#calendars.createForTimeZone(year, month, day, hour, minute, second, milisecond, Object timezone), 'dd-MMM-yyyy HH:mm')}

Adding "zzz" to the end of the string, always return my locale timezone. I guess there are way to work this out so it looks better, but main point for me was to find out if it was possible at all.

Examples that work:

${#dates.format(#calendars.createForTimeZone(#calendars.year(ticket.ticketDate), #calendars.month(ticket.ticketDate), #calendars.day(ticket.ticketDate), #calendars.hour(ticket.ticketDate), #calendars.minute(ticket.ticketDate),'PST'), 'yyyy-MMM-dd HH:mm')}

${#calendars.format(#calendars.createForTimeZone(#calendars.year(ticket.ticketDate), #calendars.month(ticket.ticketDate), #calendars.day(ticket.ticketDate), #calendars.hour(ticket.ticketDate), #calendars.minute(ticket.ticketDate),'CET'), 'yyyy-MMM-dd HH:mm')}

and either one would return identical results.

Here are the results when comparing same format, using PST and CET:

2014-Feb-24 16:00
2014-Feb-24 07:00

or:

2014-Mar-01 03:00
2014-Feb-28 18:00

Regards,

Bartie answered 1/4, 2014 at 13:32 Comment(3)
This does not work, because it looks like the #calendars.format and #dates.format take by default the time zone of the server the app is run on anyway. What is created by #calendars.createForTimeZone does not matter because it will be changed by formatting anyway. If I deploy it locally in CET and then deploy to a heroku server which uses UTC then the output is shifted by 2 hours in the view. At least for my application where I use instances of DateTime of Joda.Cassandra
@Cassandra I have your exact same problem! How did you solve it? I have my app deployed on AWS and when I test it locally is ok but once deployed the shown hour is 4 hours before the exact one!Way
@StefanoSambruna Hi, eventually I solved my problem with passing the following argument -Duser.timezone=CET to JVM. This is possible on heroku thanks to Procfile, not sure how it looks with AWS.Cassandra
R
6

Another approach to the same problem may be to use your own static methods:

${T(xx.xxx.utils.DateUtils).format(myDate, 'yyyy-MM-dd HH:mm', 'CET')}

  public static String format(Date date, String pattern, String timeZone) {
    TimeZone tz = TimeZone.getTimeZone(timeZone);
    return DateFormatUtils.format(date, pattern, tz);
  }

or even directly from lang3 (does not work on GAE because of some class access restrictions in sun.util.calendar package):

<div th:with="timeZone=${T(java.util.TimeZone).getTimeZone('CET')}">
   <span th:text="${T(org.apache.commons.lang3.time.DateFormatUtils).format(myDate, 'yyyy-MM-dd HH:mm', timeZone)}"></span>
</div>
Rosie answered 13/8, 2015 at 15:9 Comment(0)
B
4

As I was puzzled by this question, I searched extensively for possible solutions.

These are my findings: I did not found any clean function for changing timezone and displaying it like it is in jsp:

<fmt:timeZone value="US">
<fmt:formatDate value="${today}"  type="both" />
</fmt:timeZone>

Possible solution, that works would be to create calendar instance using createForTimeZone and format it, since it returns a raw calendar value, so from this:

#calendars.createForTimeZone(year, month, day, hour, minute, second, milisecond, Object timezone)

you would get something like this:

java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="PST",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=PST,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=1,WEEK_OF_YEAR=14,WEEK_OF_MONTH=1,DAY_OF_MONTH=24,DAY_OF_YEAR=91,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=6,HOUR_OF_DAY=7,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

As you can see (you have to look carefully) it did converted time to the timezone provided.

Now, I still haven't gotten to the point where I can get it all to work fine, but if you add calendars.format in front of this, you would get it to properly show time in the given timezone. ${#calendars.format(#calendars.createForTimeZone(year, month, day, hour, minute, second, milisecond, Object timezone), 'dd-MMM-yyyy HH:mm')}

Adding "zzz" to the end of the string, always return my locale timezone. I guess there are way to work this out so it looks better, but main point for me was to find out if it was possible at all.

Examples that work:

${#dates.format(#calendars.createForTimeZone(#calendars.year(ticket.ticketDate), #calendars.month(ticket.ticketDate), #calendars.day(ticket.ticketDate), #calendars.hour(ticket.ticketDate), #calendars.minute(ticket.ticketDate),'PST'), 'yyyy-MMM-dd HH:mm')}

${#calendars.format(#calendars.createForTimeZone(#calendars.year(ticket.ticketDate), #calendars.month(ticket.ticketDate), #calendars.day(ticket.ticketDate), #calendars.hour(ticket.ticketDate), #calendars.minute(ticket.ticketDate),'CET'), 'yyyy-MMM-dd HH:mm')}

and either one would return identical results.

Here are the results when comparing same format, using PST and CET:

2014-Feb-24 16:00
2014-Feb-24 07:00

or:

2014-Mar-01 03:00
2014-Feb-28 18:00

Regards,

Bartie answered 1/4, 2014 at 13:32 Comment(3)
This does not work, because it looks like the #calendars.format and #dates.format take by default the time zone of the server the app is run on anyway. What is created by #calendars.createForTimeZone does not matter because it will be changed by formatting anyway. If I deploy it locally in CET and then deploy to a heroku server which uses UTC then the output is shifted by 2 hours in the view. At least for my application where I use instances of DateTime of Joda.Cassandra
@Cassandra I have your exact same problem! How did you solve it? I have my app deployed on AWS and when I test it locally is ok but once deployed the shown hour is 4 hours before the exact one!Way
@StefanoSambruna Hi, eventually I solved my problem with passing the following argument -Duser.timezone=CET to JVM. This is possible on heroku thanks to Procfile, not sure how it looks with AWS.Cassandra
N
0

I found this answer when I wanted to format LocalDateTime to some time zone in the templates. It turned out that the purpose of LocalDateTime is to do not work with time zones at all.

However, there is also a class called ZonedDateTime which purpose is obvious. You can also use LocalDateTime#atZone which creates a new instance of local converted to the new zone.

Note that usual DateTimeFormatter ignores any time zone settings in the case of local date time but not in the case of zoned date time. So you can use usual formatters as well in the templates.

Nasia answered 21/3, 2016 at 16:22 Comment(0)
F
0

The server renders the page based on the server time, in order to get the timezone of the user from the request, the user when submitting the request should attach the timezone information in the request headers or parameters, so that the server knows the appropriate time zone to render. To do that, use javascript to get the browser's time zone.

Fungicide answered 2/5, 2018 at 13:36 Comment(0)
S
0

Using Thymeleaf's #temporals doesn't provide the ability to use the constructor new Temporals(locale, zoneId).

Create your own Temporals temporals = new Temporals(LocaleContextHolder.getLocale(), zone) (somewhere, session bean, or so - zone should probably come from your user's preferences), and put it available in the controller (eg. as "temporalsZone").

Then use it in the UI: th:text="${temporalsZone.format(value, pattern, #locale)}"> and enjoy the full support of #temporals.

Somnus answered 28/8, 2019 at 13:15 Comment(0)
N
0

If you add org.thymeleaf.extras:thymeleaf-extras-java8time as a dependency, you get the #temporals object to help format types like:

  • Instant
  • LocalDateTime
  • ZonedDateTime
  • etc

If you want to format java.util.Date you can use #dates instead.

The methods you're looking for are #dates.format or #temporals.format. You can specify a Locale as the third argument. The general syntax is #temporals.format(<temporal object>, <pattern>, <optional locale>)

Examples:

  • th:text="${#temporals.format(myDate, 'dd-MM-yyyy', new java.util.Locale('en'))}"
  • th:text="${#temporals.format(myDate, 'dd-MM-yyyy', @java.util.Locale@ENGLISH)}"

Note that this is true even if you're working with Kotlin Spring Boot. The syntax in the Thymeleaf template isn't Java, it's an OGNL Expression.

https://commons.apache.org/proper/commons-ognl/language-guide.html

I'll quote the useful syntax used here:

#variable
Context variable reference

@class@method(args)
Static method reference

@class@field
Static field reference

new class(args)
Constructor call

One other option is to specify the Locale in the Thymeleaf context, if you just want to override the default system Locale. I've included a Kotlin snippet of how that might work:

val context = Context() // org.thymeleaf.Context
context.locale = Locale.ENGLISH
context.setVariable("x", 0)

templateEngine.process("classpath:template.html", context)

Then you can simply use th:text="${#temporals.format(datum.timestamp, 'E MMM dd yyyy')} without the explicit Locale argument.

Nissen answered 30/3, 2022 at 4:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.