The bounty expires in 22 hours. Answers to this question are eligible for a +200 reputation bounty.
Peter Penzov is looking for an answer from a reputable source.
I have the following code that I want to migrate to Java 17:
Gradle dependency:
implementation 'org.jadira.usertype:usertype.core:7.0.0.CR1'
Entity:
import org.joda.time.DateTime;
#Entity
#Table(name = "logs")
public class Log {
#Column(name = "inserted_date")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime insertedDate;
}
.....
DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yy-mm-dd'T'HH:mm:ss'Z'");
log.setInsertedDate(DateTime.now());
dateFormatter.print(log.getInsertedDate().withZone(DateTimeZone.UTC)));
I updated the code to this:
Entity:
import java.time.OffsetDateTime;
#Entity
#Table(name = "logs")
public class Log {
#Column(name = "inserted_date")
private OffsetDateTime insertedDate;
}
.....
DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yy-mm-dd'T'HH:mm:ss'Z'");
log.setInsertedDate(OffsetDateTime.now());
dateFormatter.print(log.getInsertedDate().withZone(DateTimeZone.UTC)));
But I get error Cannot resolve method 'withZone' in 'OffsetDateTime'. Do you know what is the proper way to update method withZone?
edit: I tried this
from: log.setTimestamp(dateFormatter.print(auditLog.getInsertedDate().withZone(DateTimeZone.UTC)));
to: log.setTimestamp(dateFormatter.print(auditLog.getInsertedDate().atZoneSameInstant(ZoneOffset.UTC)));
I get for this line: auditLog.getInsertedDate().atZoneSameInstant(ZoneOffset.UTC) error:
Cannot resolve method 'print(ZonedDateTime)'
Can you advice how to solve this?
First of all, it looks like your time format isn't what you intended it to be.
"yy-mm-dd'T'HH:mm:ss'Z'" would mean a format that prints:
year-minutes-days'T'Hours(24 hour format):minutes:secondsZ
example: 23-48-19T14:48:17Z
I'm assuming you want to follow years with month of the year and so the format should be:
"yy-MM-dd'T'HH:mm:ss'Z'"
example: 23-02-19T14:48:17Z
To know more about constructing patterns for formatting please refer the section "Patterns for Formatting and Parsing" in the java time api docs
Now, coming to your question,
You seem to be mixing the joda time library with java date time api. Written purely with the java date time api, your code should look something like:
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yy-MM-dd'T'HH:mm:ss'Z'");
log.setInsertedDate(OffsetDateTime.now());
log.setTimestamp(dateFormatter.format(auditLog.getInsertedDate().atZoneSameInstant(ZoneId.of("UTC"))));
PS: I know that working with date time objects can be frustrating and confusing in Java especially because there used to be more than one way to do it. I would recommend reading the java date time api tutorials to get a better understanding and to always use it (when writing in java 8+) to avoid confusion.
Related
Checkmarx complains that "the file utilizes "format" that is accessed by other concurrent functionality in a way that is not thread-safe, which may result in a Race Condition over this resource. It highlights the format method. How do we resolve this?
String endDate =
configProperties.getDateFormatter().format(Date.from(date.plusMonths(-1L * auditTimeMonthLimit).atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant()));
Other part of code
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public SimpleDateFormat getDateFormatter() {
return dateFormatter;
}
SimpleDateFormat is not thread safe. This is a good explanation.
There is not a lot of code in the example, but having a final instance of SimpleDateFormat implies it may be used by multiple threads.
Maybe configProperties is a global singleton? It is hard to tell, but if that code is accessed by multiple threads (including as part of a web controller or other type of web endpoint handler) and that is a single instance for every thread then you have a problem.
One possible solution (maybe not ideal, but you can translate it to something that works for you):
public SimpleDateFormat getDateFormatter() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
}
There are likely better options for formatting than this, so maybe doing it entirely different would be better.
Legacy date-time API is error-prone e.g. java.util.Date and SimpleDateFormatter aren’t thread-safe, leading to potential concurrency issues for users. It is recommended to stop using them completely and switch to the modern date-time API.
The output you are trying to achieve is already the default format of Instant and therefore, you do not need to use a formatter.
However, probably you are not familiar with the modern date-time API and therefore, for the sake of your learning, I have also demonstrated the use of DateTimeFormatter.
Demo using java.time API:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.now(); // Some date
int auditTimeMonthLimit = 5; // Some value
String endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toString();
System.out.println(endDate);
// In case you wanted the UTC date-time out of the local date
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneOffset.UTC)
.toString();
System.out.println(endDate);
// In case you wanted the start date of the default time-zone to be converted
// into the UTC date-time
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
.toString();
System.out.println(endDate);
// A custom format
ZonedDateTime zdt = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
endDate = formatter.format(zdt);
System.out.println(endDate);
}
}
Output in my time-zone:
2022-06-17T00:00+01:00[Europe/London]
2022-06-17T00:00Z
2022-06-16T23:00:00Z
2022-06-16T23:00:00.000Z
Important points:
I recommend you use LocalDate#minusMonths i.e. instead of using date.plusMonths(-1L * auditTimeMonthLimit), you should use date.minusMonths(auditTimeMonthLimit).
'Z' is not the same as Z.
For your use case, I recommend you use LocalDate#atStartOfDay(ZoneId zone) instead of the non-parametrized LocalDate#atStartOfDay. This will make you
Learn more about the modern Date-Time API from Trail: Date Time.
We want to add days to the current date and format it in a specific way. This was solved in Groovy 2.4.13 and the following date manipulation works fine:
today = new Date()+90;today.format('yyyy-MM-dd HH:mm:ss.S');
Result: 2019-12-02 08:07:15.294
In Groovy 2.5.4 the same expression throws this exception:
groovy.lang.MissingMethodException: No signature of method:
java.util.Date.plus() is applicable for argument types: (Integer)
values: [90] Possible solutions: parse(java.lang.String),
split(groovy.lang.Closure), use([Ljava.lang.Object;),
is(java.lang.Object), wait(), clone() at
Script1.run(Script1.groovy:3)
I was able to reproduce this behaviour in "Groovy sandboxes" online:
Working fine here: groovy-playground (Version 2.4.1.5)
Failing here: groovyconsole (Version 2.5.7)
What is the working alternative in this case? I have read about a new Date API, but couldn't find the details about how to use it, with date manipulation (+ 90 days for example).
Take a look at TimeCategory
import groovy.time.TimeCategory
def theDate = use(TimeCategory){new Date() + 90.days}.format('yyyy-MM-dd HH:mm:ss.S')
I agree with Ole V.V.'s recommendations to use the new Date/Time API. Here is how you would write his Java sample in a more Groovy style.
// you can assemble aggregate types by left shifting the aggregates
// I'm not endorsing this approach, necessarily, just pointing it out as an alternative
ZonedDateTime now = LocalDate.now() << LocalTime.now() << ZoneId.of('Africa/Bamako')
// the plus operator is overloaded
ZonedDateTime in90Days = now + 90
// you can pass a String to format without needed a full DateTimeFormatter instance
println in90Days.format('uuuu-MM-dd HH:mm:ss.S')
While Groovy adds some further support for the old Java Date class, I still believe that you should not use it. It was always poorly designed and is now long outdated. Instead use java.time, the modern Java date and time API. I am sorry that I will have to trust you to translate from Java code.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Africa/Bamako"));
ZonedDateTime in90Days = now.plusDays(90);
System.out.println(in90Days.format(formatter));
Output when running just now was:
2020-01-01 08:37:13.3
Please substitute your desired time zone if it didn’t happen to be Africa/Bamako.
Link: Oracle tutorial: Date Time explaining how to use java.time.
You can use Calendar to achieve that
Calendar cal = new GregorianCalendar();
cal.add(Calendar.DATE, 90);
Date date = cal.getTime();
All steps must be separate and not in a single line.
So I got this Instant date in Java: "2018-05-19T22:00:00Z".
How can I get the day of the week? like monday..
I was trying this but it doesn't work:
${date?string["EEEE"]}
Thanks
Freemarker does not work with Instant, so you would need to convert that to string in Java (something like "2018-05-19T22:00:00Z") and then convert the string doing the following:
<#setting locale="en_US">
${"2018-05-19T22:00:00Z"?datetime.iso?string["EEEE"]}
First convert the string to datetime in iso format, and then back again to string with the pattern of your choosing.
By changing the locale setting, you may get the day of the week in different languages.
I think that the best way to work in Freemarker is to always have strings or integers variables.
At this point FreeMarker does not support Java 8 time, see the contribute page:
What should I contribute?
Usually, contributors come because they want to fix/improve a certain thing. But if you just want to help in general, here are some topics ...
Support for Java 8 date/time API-s (this is actually certainly a difficult one). Note that there's a 3rd party solution for this, https://github.com/amedia/freemarker-java-8, but it's not as seamless as a native solution could be.
...
So you could check out the third party solution.
See also:
Java.time (Java 8) support in Freemarker
${date?string["EEEE"]} works fine as long as date is a java.util.Date object.
You can test it in this way:
<#assign date = .now>
${date?string["EEEE"]}
I guess that it doesn't work because your date is a String, in this case you should parse it in Java (server-side) and expose a java.util.Date variable to the template.
I would advise you to use new Date Time API defined in Java 8.Using Date Time API you can fetch all data related to Date and Time easily.
Java 8 has defined a separate Enum for handling days of the week named – DayOfWeek (java.time.DayOfWeek)
java.time.DayOfWeek is an Enum which defines 7 constants representing the seven days of the week – MONDAY(int value=1), TUESDAY(2), WEDNESDAY(3)… till SUNDAY(7).
import java.time.*;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testLocalDateTime();
}
public void testLocalDateTime() {
// Get the current date and time
LocalDateTime currentTime = LocalDateTime.parse("2018-05-19T22:00:00");
System.out.println("Current DateTime: " + currentTime);
DayOfWeek dayOfWeek = currentTime.getDayOfWeek();
System.out.println(currentTime + " was a " + dayOfWeek);
}
}
I'm using swagger-ui and a stub server in Java Spring-MVC generated using swagger-codegen. I have a date format somewhere and if I enter some date, it always returns me the UNIX Date Time format. I would like to have the RFC-3339 format for the date and date-time. Does anyone knows how to do this?
I found the answer..
I had to overwrite the JsonFormat since I'm returning a JSON object..
Therefore, if you have the same problem as me, use:
#JsonFormat(pattern="...")
Date foo;
Do not forget to use import com.fasterxml.jackson.annotation.JsonFormat;
This question already has answers here:
Converting ISO 8601-compliant String to java.util.Date
(31 answers)
Closed 7 years ago.
It seems a Javascript date in String, but how to format it to a java.util.Date? Is it possible by SimpleDateFormat?
Okay, I didn't read it carefully.
One of the examples mentioned in the documentation exactly matches your case:
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX" 2001-07-04T12:08:56.235-07:00
So
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse(yourString);
will give what you want.
The pattern of javascript date you are getting is
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX" 2001-07-04T12:08:56.235-07:00
in terms of java.
Use the sample code:
import java.util.*;
import java.lang.*;
import java.io.*;
import java.text.SimpleDateFormat;
class Test
{
public static void main (String[] args) throws java.lang.Exception
{
// your code goes here
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(sdf.parse("2015-08-01T06:51:14.000+08:00"));
Date date = sdf.parse("2015-08-01T06:51:14.000+08:00");
}
}
From the API documentation of SimpleDateFormat (see the examples), the pattern to use in your case is:
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
Bear in mind that using Z instead of XXX only works if time zone is written as -0700 (no colon).