Spring scheduled fixedRateString as Duration - java

The #Scheduled documentation here states that the fixedRateString value can be the delay in milliseconds as a String value, e.g. a placeholder or a java.time.Duration compliant value. Meaning I can either write
#Scheduled(fixedRateString = "45s")
OR
#Scheduled(fixedRateString = "45000")
And it should be the same. However when I try to run it I get
Encountered invalid #Scheduled method 'updateWarmupInstances': Invalid fixedRateString value "45s" - cannot parse into long
So it this a mistake on Spring's part or am I doing something wrong
?

To use the method #Scheduled(fixedRateString) for durations, you could use a String with the standard duration:
#Scheduled(fixedRateString = "PT45S")
The prefix PT is for ISO-8601 standard and in this example, it's mean the duration of 45 seconds.
Another example could be a duration of 1h:
#Scheduled(fixedRateString = "PT1H")

You are looking at the return value of the method, not the input. The input can only be a String in milliseconds, but the return value is a value compliant with Duration.

Related

Java Spring deserializing ISO Zulu time error

I am trying to parse dates with the format "yyyy-MM-dd'T'HH:mm:ssZ". I have a controller with a POST endpoint which takes an object. In that object is a date field of object type "Instant". it has the following annotations:
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
#JsonSerialize(using = InstantSerializer.class)
#JsonDeserialize(using = DefaultInstantDeserializer.class)
private Instant endTime;
I had to create the DefaultInstantDeserializer because using the provided one gave me an error because InstantDeserializer (from the jackson-datatype library) has no no-args constructor.
This is that class
public class DefaultInstantDeserializer extends InstantDeserializer<Instant> {
public DefaultInstantDeserializer() {
super(Instant.class, DateTimeFormatter.ISO_INSTANT,
Instant::from,
a -> Instant.ofEpochMilli(a.value),
a -> Instant.ofEpochSecond(a.integer, a.fraction),
null,
true // yes, replace zero offset with Z
);
}
}
using Postman, I sent the object, with that field being 2022-02-08T15:22:24+00:00
I get this error from in the logs from Spring:
Failed to deserialize java.time.Instant: (java.time.format.DateTimeParseException) Text '2022-02-08T15:14:28+00:00' could not be parsed at index 19\n
It looks like it cannot parse the time starting at +00:00. I thought the Z indicated Zulu time/ GMT/ UTC time, which is a 0 offset. Is "yyyy-MM-dd'T'HH:mm:ssZ" not a valid date format? My requirements say I must use that pattern. In Postman the pattern I am using to send the data is YYYY-MM-DDTHH:mm:ssZ. There is some disconnect between the 0 offset and the Z value, I thought the deserializer would take care of that, but I think not. Any suggestions?
Thanks

JodaTime format error - different formats in different machine

Im running the following code to generate DateTime
DateTimeFormatter formatter = DateTimeFormat.forPattern(DATE_TIME_FORMAT);
DateTime dt = formatter.parseDateTime("ddMMyyyy");
I have a test that takes a string and converts it to Joda DateTime and then tests that value with an expected value.
My test passes on my machine but fails on another machine.
I'm seeing the following error for String 13092019
ime elapsed: 0.189 sec <<< FAILURE!
org.junit.ComparisonFailure:
expected:<...ateTime>1970-01-01T0[0]:00:00</common:event...>
but was:<...ateTime>1970-01-01T0[1]:00:00</common:event...>
I just can't seem to understand why and how two different dates are being generated for 13092019
Neither your expected nor your observed result has got any UTC offset. So I’m thinking that you probably want a LocalDateTime rather than a DateTime. If so, here’s one way to get your expected result, with 00 as hour of day (rather than 01, as JUnit observed).
private static final String DATE_TIME_FORMAT = "ddMMyyyy";
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormat.forPattern(DATE_TIME_FORMAT);
String text = "13092019";
LocalDateTime ldt = LocalDate.parse(text, formatter)
.toLocalDateTime(LocalTime.MIDNIGHT);
System.out.println(ldt);
}
Output from the program is:
2019-09-13T00:00:00.000
[0] and [1] are just JUnit’s way of highlighting the difference between the two results. Everything before and after […] is the same in the expected and the actual value. Neither the expected nor the actual value had any [ or ] in it when you ran the test. JUnit added them for emphasis.
I just can't seem to understand why and how two different dates are
being generated for 13092019
Neither can I without a reproducible example. Scorpioo590 is probably correct that it’s a time zone issue. Most likely you also get different results for other dates. It would seem that the date used in your JUnit run was 1970-01-01 (the epoch day)?

Scheduling task using Spring Scheduler

I am using cron expression for the last working day of the month like this:
#Scheduled(cron = "0 0 8 LW * ?")
But after running this I got:
java.lang.IllegalStateException: Encountered invalid #Scheduled method 'fetchEmployeesDetailsAndSendNotification': For input string: "LW"
although the cron expression is valid.
Why am I getting this exception, and how can I fix it?
It would seem that your pattern is incorrect. The quartz scheduler format is not exactly the same as the Linux crontab format.
While quartz allows the definition of LW. The spring scheduler format (which you are using via the #Scheduled annotation) does not.
See the javadoc for Spring's CronSequenceGenerator which references the linux man page for the correct crontab patterns
The pattern is a list of six single space-separated fields: representing second, minute, hour, day, month, weekday. Month and weekday names can be given as the first three letters of the English names.
Try this:
#Scheduled(cron = "0 0 8 28-31 * ?")
public void yourMethod() {
Calendar calendar = Calendar.getInstance();
if (calendar.get(Calendar.DATE) == calendar.getActualMaximum(Calendar.DATE)) {
// do something...
}
}

Freemarker get current timestamp

How can I get the current timestamp (number of seconds since 01/01/1970) in a freemarker file? I know the .now var but I didn't figure how to get a basic timestamp format.
Use the ?long operator:
.now?long
for milliseconds, or
${.now?long / 1000}
for seconds

Spring + Thymeleaf: time in user's timezone

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>
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>
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,
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.
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.
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.
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.

Categories