I have a queue that listens to a topic and my listener receives a DTO.
I need to parse the String to LocalDateTime but I'm getting this error
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Text '2020-06-18 11:12:46' could not be parsed at index 10
Here is the message details
{"id":444, "details":{"TYPE":[1]},"dateOccurred":"2020-06-18 11:12:46"}"]
And here is how I set it in my DTO
public class PlanSubscriptionDto {
private Long id;
private Map<String, List<Long>> details;
private LocalDateTime dateOccurred;
public void setDateOccurred(String dateTime) {
this.dateOccurred = LocalDateTime.parse(dateTime, DateTimeFormatter.ISO_DATE_TIME);
//ive also tried this
//this.dateOccurred = LocalDateTime.parse(dateTime, DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG));
}
}
Thank you for your help!
Any advice would we great.
Use a format pattern string to define the format.
public class PlanSubscriptionDto {
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
private Long id;
private Map<String, List<Long>> details;
private LocalDateTime dateOccurred;
public void setDateOccurred(String dateTime) {
this.dateOccurred = LocalDateTime.parse(dateTime, FORMATTER);
}
}
Why didn’t your code work?
ISO 8601 format has a T between the date and the time. There’s no T in your date-time string, so DateTimeFormatter.ISO_DATE_TIME cannot parse it.
Variants
Now we’re at it, I’d like to show a couple of other options. Taste differs, so I can’t tell which one you’ll like the best.
You may put in the T to obtain ISO 8601 format. Then you will need no explicit formatter. The one-arg LocalDateTime.parse() parses ISO 8601 format.
public void setDateOccurred(String dateTime) {
dateTime = dateTime.replace(' ', 'T');
this.dateOccurred = LocalDateTime.parse(dateTime);
}
Or sticking to the formatter and the space between date and time, we can define the formatter in this wordier way:
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
What we get for the extra code lines is (1) more reuse of built-in formatters (2) this formatter will accept time without seconds and time with a fraction of second too because DateTimeFormatter.ISO_LOCAL_TIME does.
Link
Wikipedia article: ISO 8601
I just learned that this is also an option for the parsing :)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "uuuu-MM-dd HH:mm:ss")
private LocalDateTime dateOccurred;
Related
I'm trying to convert date from API "2022-08-16T06:25:00.000" to HH:mm (6:25) but getting DateTimeParseException.
My code: ZonedDateTime.parse(it.time[0], DateTimeFormatter.ofPattern("HH:mm"))
"time": [
"2022-08-16T06:25:00.000",
"2022-08-16T07:40:00.000"
],
String dateTimeStr = "2022-08-16T06:25:00.000";
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm");
String time = dateTime.format(fmt);
System.out.println(time);
or, if you want to use the time as an instance of LocalTime, you can get it by dateTime.toLocalTime()
You don't need to define any DateTimeFormatter in this situation.
use a LocalDateTime because the input String does not hold any information about the zone
don't use a DateTimeFormatter for parsing that only parses hour of day and minutes of hour, the String to be parsed just contains more information
Here's an example without any DateTimeFormatter explicitly defined (but it will use default ones for parsing, at least):
public static void main(String[] args) {
// example input
String fromApi = "2022-08-16T06:25:00.000";
// parse it to a LocalDateTime because there's no zone in the String
LocalDateTime localDateTime = LocalDateTime.parse(fromApi);
// extract the time-of-day part
LocalTime localTime = localDateTime.toLocalTime();
// and print its toString() implicitly
System.out.println(localTime);
}
Output: 06:25
The above code will produce output of the pattern HH:mm, which will have leading zeros at hours of day to always have a two-digit representation.
If you insist on single-digit hours of day, you will have to prepare a DateTimeFormatter, like in this alternative example:
public static void main(String[] args) {
// example input
String fromApi = "2022-08-16T06:25:00.000";
// parse it to a LocalDateTime because there's no zone in the String
LocalDateTime localDateTime = LocalDateTime.parse(fromApi);
// extract the time-of-day part
LocalTime localTime = localDateTime.toLocalTime();
// prepare a DateTimeFormatter that formats single-digit hours of day
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("H:mm");
// print the LocalTime formatted by that DateTimeFormatter
System.out.println(localTime.format(dtf));
}
Output this time: 6:25
The other answers use Java. Since you've added a [kotlin] tag, here is a Kotlin-based answer for the sake of completeness. In order to make it different to the Java answers, I'm using kotlinx.datetime, which is still at the experimental stage at version 0.4.0.
import kotlinx.datetime.LocalDateTime
fun main() {
println(LocalDateTime.parse("2022-08-16T06:25:00.000").time) // prints "06:25"
// If you want "6:25" you can format it yourself:
println(with(LocalDateTime.parse("2022-08-16T06:25:00.000")) {
"$hour:$minute"
})
}
How about different approach
String dateTimeStr = "2022-08-16T06:25:00.000";
Matcher m=Pattern.of("T(\\d{2}:\\d{2}):").matcher(dateTimeStr);
m.find();
System.out.println(m.group(1);; //should print 06:25
And yet another "alternative" answer. It relies on the fact that in an ISO-compliant date-time format, the time starts in the 11th position.
private static final int ISO_TIME_POS = 11;
....
String dateTimeStr = "2022-08-16T06:25:00.000";
String timeStr = dateTimeStr.substring(ISO_TIME_POS, ISO_TIME_POS + 5);
System.out.println(timeStr); // prints "06:25"
I need to convert this date "2021-09-27 16:32:36" into zulu format like this "yyyy-MM-dd'T'HH:mm:ss.SSS'Z".
tl;dr
"2021-09-27 16:32:36"
.replace( " " , "T" )
.concat( ".Z" )
2021-09-27T16:32:36Z
A fractional second of zero can be omitted under ISO 8601.
String manipulation
Usually I would recommend using java.time classes. But in your case the obvious solution is simple string manipulation, as suggested by Andy Turner.
String iso8601 = "2021-09-27 16:32:36".replace( " " , "T" ).concat( ".000Z" ) ;
I would recommend dropping the zero fractional second. The string would still comply with ISO 8601.
String iso8601 = "2021-09-27 16:32:36".replace( " " , "T" ).concat( "Z" ) ;
The resulting string 2021-09-27T16:32:36Z represents a moment as seen with an offset of zero hours-minutes-seconds ahead/behind UTC.
If you need to do further work, parse that as an Instant. Example: Instant.parse( iso8601 )
Time zone is crucial
The Zulu time that you are asking for defines a definite and precise point in time. The string you have got does not. If we don’t know its time zone, it may denote times in a span of more than 24 hours.
For this answer I am assuming that the time is in US Central time (America/Chicago).
The format you are asking for is ISO 8601.
java.time
Like the other answers I am recommending java.time, the modern Java date and time API, for all of your date and time work. It has good support for ISO 8601.
I am using this formatter for parsing your string:
private static final DateTimeFormatter PARSER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ROOT);
Now the work goes like this:
ZoneId zone = ZoneId.of("America/Chicago");
String dateString = "2021-09-27 16:32:36";
ZonedDateTime dateTime = LocalDateTime.parse(dateString, PARSER).atZone(zone);
String isoZuluString = dateTime.withZoneSameInstant(ZoneOffset.UTC).toString();
System.out.println(isoZuluString);
Output is:
2021-09-27T21:32:36Z
It’s in ISO 8601 format and in Zulu time, so as far as I am concerned, we’re done. The milliseconds you asked for are not there. They were not in the original string either, and according to the ISO 8601 format they are not mandatory, so you should be fine. Only if you encounter a particularly picky service that requires a fraction of second in the string even when it is .000, use a formatter for producing it:
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendPattern("'T'HH:mm:ss.SSSX")
.toFormatter(Locale.ROOT);
The formatter could have been written with a format pattern alone. I took this opportunity for demonstrating that we may reuse built-in formatters in our own to make it easier and safer to get ISO 8601 right. Format like this:
String isoZuluString = dateTime.withZoneSameInstant(ZoneOffset.UTC)
.format(FORMATTER);
2021-09-27T21:32:36.000Z
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Example with printing on the console :
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ZuluZulu {
public static void zuluFormatter(String localDateTime) {
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String s = localDateTime;
LocalDateTime dt = LocalDateTime.parse(s, formatter);
System.out.println("dateTime Simple Format without T = " + dt.format(formatter));
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'");
System.out.println("DateTime Zulu format = " + dt.format(formatter2));
}
public static void main(String[] args) {
zuluFormatter("2021-09-27 16:32:36");
}
}
Output :
dateTime Simple Format without T = 2021-09-27 16:32:36
DateTime Zulu format = 2021-09-27 16:32:36.000Z
this example is exactly what you need without printing on the console :
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ZuluZulu {
public static String zuluFormatter(String localDateTime) {
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern(pattern);
String s = localDateTime;
LocalDateTime dt = LocalDateTime.parse(s, formatter);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-
MM-dd HH:mm:ss.SSS'Z'");
return dt.format(formatter2);
}
public static void main(String[] args) {
System.out.println(zuluFormatter("2021-09-27 16:32:36"));
}
}
I have searched online but couldn't really find the way to do it as I hope.
My data look like "20201005114527", "20201002173838" .......
and would like to convert them into LocalDateTime.
It will be converted into json again afterwards.
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "xxx/xxx")
private LocalDateTime xxxxxDate;
But I'm just confused of converting those "number-only strings" into LocalDateTime.
Use the format mask yyyyMMddHHmmss?
#JsonFormat(pattern = "yyyyMMddHHmmss")
private LocalDateTime xxxxxDate;
Parsing a date-time string containing only digits isn’t any different from parsing a date-time string in any other format.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss");
String numberOnlyString = "20201005114527";
LocalDateTime xxxxxDate = LocalDateTime.parse(numberOnlyString, formatter);
System.out.println(xxxxxDate);
Output:
2020-10-05T11:45:27
My controller has a GET endpoint which accept lots of query parameters. So, instead of having multiple #QueryParam I created a CriteriaDTO in order to perform dynamic queries to our Mongo database
My controller endpoint :
#GetMapping
public List<MyObject> getAll(#Valid CriteriaDTO criteriaDTO){
return myObjectService.findAll(criteriaDTO);
}
public class CriteriaDTO {
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
// getters, setters ...
}
And, I want to pass the minDate is the URL with the following format yyyy-MM-dd but I need to convert it to the following format yyyy-MM-dd'T'HH:mm:ss.SSS.
My question is : Is there any annotation or something else which accepts the first format yyyy-MM-dd and automatically convert it to another ?
To be clear if I make the following call :
http://localhost:8080/api/myobject?minDate=2020-01-01
And then criteriaDTO.getminDate() will return 2020-01-01'T'00:00:00.000
Thanks for your help :)
You can do it in a more simple way than searching an annotation-magic solution.
Just add to your CriteriaDTO an additional getter for LocalDateTime:
public LocalDateTime getMinDateTime() {
return this.minDate.atTime(0, 0, 0, 0);
}
and use it wherever you need time instead of date.
Define setter and parse with SimpleDateFormat
public void setMinDate() {
if(!minDate.empty()) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
this.minDate = formatter.parse(minDate)
}
}
I would recommend to use atStartOfDay instead of converting this 2020-01-01 to 2020-01-01'T'00:00:00.000 using custom deserializer. And also since you are manipulating the input data i would recommend to do it as separate operation
LocalDateTime date = criteriaDTO.getminDate().atStartOfDay() //2020-01-01'T'00:00
And you can also add formatter DateTimeFormatter to get required output
date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) //2020-01-01'T'00:00:00
You have several options. Check what you exactly need,
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.of(date, LocalTime.MIDNIGHT);
System.out.println(dateTime); //2020-02-04T00:00:00
System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(dateTime)); //2020-02-04T00:00:00
System.out.println(date.atStartOfDay()); ////2020-02-04T00:00
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
System.out.println(formatter.format(dateTime)); //2020-02-04T00:00:00.000
You need to modify the getter in dto to format it, for example:
class CriteriaDTO {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
private int offset = 0
private int limit = 20
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate minDate
public String getMinDate() {
return formatter.format(LocalDateTime.of(minDate, LocalTime.MIDNIGHT));
}
// getters, setters ...
}
I am using the following timestamp format:
yyyyMMddHHmmssSSS
The following method works fine:
public static String formatTimestamp(final Timestamp timestamp, final String format) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
return timestamp.toLocalDateTime().format(formatter);
}
And, when I pass in a Timestamp with that format string, it returns, for example:
20170925142051591
I then require to map from that string to a Timestamp again, essentially the reverse operation. I know that I can use a SimpleDateFormat and its parse() method, but I'd prefer to stick to java.time style formatting, if possible.
I wrote this (rather hacky) bit of code, which works with some formats, but not with this particular one:
public static Timestamp getTimestamp(final String text, final String format, final boolean includeTime) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
final TemporalAccessor temporalAccessor = formatter.parse(text);
if (includeTime) {
final LocalDateTime localDateTime = LocalDateTime.from(temporalAccessor);
return DateTimeUtil.getTimestamp(localDateTime);
} else {
final LocalDate localDate = LocalDate.from(temporalAccessor);
return DateTimeUtil.getTimestamp(localDate);
}
}
It fails on the second line, at the formatter.parse(text); part.
Stack Trace:
java.time.format.DateTimeParseException: Text '20170925142051591' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at java.time.LocalDateTime.parse(LocalDateTime.java:477)
at com.csa.core.DateTimeUtil.main(DateTimeUtil.java:169)
Is there a simpler way to achieve what I want without utilising SimpleDateFormat?
It's a bug: https://bugs.openjdk.java.net/browse/JDK-8031085
The link above also provides the workaround: using a java.time.format.DateTimeFormatterBuilder with a java.time.temporal.ChronoField for the milliseconds field:
String text = "20170925142051591";
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time
.appendPattern("yyyyMMddHHmmss")
// milliseconds
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
// create formatter
.toFormatter();
// now it works
formatter.parse(text);
Unfortunately, there seems to be no way to parse this using only DateTimeFormatter.ofPattern(String).