Exception when trying to parse a LocalDateTime - java

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

Related

How to convert 2022-08-16T06:25:00.000 to HH:mm

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"

Date formatting in Java to a specific format

The date I fetched from an open API is 2021-04-28. I want to format it in this way: 4/28/2021. Below is the method I tried:
public String formatDateFetchedFromAPI(String transDate) {
LocalDate apiDate = LocalDate.parse(transDate, DateTimeFormatter.BASIC_ISO_DATE);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy");
String actualDate = formatter.format(apiDate);
return actualDate;
}
This throws an error saying:
java.time.format.DateTimeParseException: Text '2021-04-28' could not be parsed at index 4
If the data that I pull from open API is 20210426 then this method works just fine and returns 4/28/2021.
Your date looks like an ISO_LOCAL_DATE and not BASIC_ISO_DATE, so instead you can just use
LocalDate apiDate = LocalDate.parse(transDate);
without DateTimeFormatter, because LocalDate.parse by default uses ISO_LOCAL_DATE.

MessageConversionException occurred when mapping string date to LocalDateTime

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;

Parse seconds-of-day as part of a date string

I need to parse a date string which has the following format:
yyyy-MM-dd TTTTT. All pattern letters are the standard DateTimeFormatter letters, except for the TTTTT part, which is a seconds-of-day field.
As there is no pattern defined for such a field, I needed to come up with something else. My first thought was to try and parse the field as milli-of-day (A), but by using 5 A's my field is treated as the least significant characters which is... yeah, not what I want.
Is there any out-of-the box solution I could use or do I have to resort to some custom made solution (like ignoring the TTTTT field, parsing it manually and using LocalDateTime.plusSeconds() on the resulting date)?
In other words, how can I make the following test case pass?
public class DateTimeParserTest {
private static final String PATTERN = "yyyy-MM-dd TTTTT";
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern(PATTERN);
#Test
public void testParseSecondsOfDay() throws Exception {
String input = "2016-01-01 86399";
LocalDateTime localDateTime = LocalDateTime.parse(input, FORMATTER);
assertEquals("2016-01-01T23:59:59", localDateTime.toString());
}
}
Based on your correction (leaving out the potentially ambivalent part "HH:mm") the Java-8-solution would look like:
private static final DateTimeFormatter FORMATTER =
new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd ")
.appendValue(ChronoField.SECOND_OF_DAY, 5)
.toFormatter();
well it may not be what what you are looking, but i hope it helps
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; //MODIFICATION HERE
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern(PATTERN);
#Test
public void testParseSecondsOfDay() throws Exception {
String input = "2016-01-01 00:00:86399";
int lastIndexOf = input.lastIndexOf(':');
CharSequence parsable = input.subSequence(0,lastIndexOf)+":00";
CharSequence TTTTPart = input.subSequence(lastIndexOf+1, input.length());
LocalDateTime localDateTime = LocalDateTime.parse(parsable, FORMATTER);
Calendar calendar = Calendar.getInstance(); // gets a calendar using the default time zone and locale.
calendar.set(
localDateTime.getYear(),
localDateTime.getMonth().ordinal(),
localDateTime.getDayOfMonth(),
localDateTime.getHour(),
localDateTime.getMinute(),
0);
calendar.add(Calendar.SECOND, Integer.parseInt(TTTTPart.toString()));
StringBuilder toParse = new StringBuilder();
toParse.append(calendar.get(Calendar.YEAR) /* It gives 2016 but you what 2016, why */-1);
toParse.append("-").append(calendar.get(Calendar.MONTH) + 1 < 10 ? "0" : "")
.append(calendar.get(Calendar.MONTH)+1);
toParse.append("-").append(calendar.get(Calendar.DAY_OF_MONTH) < 10 ? "0" : "")
.append(calendar.get(Calendar.DAY_OF_MONTH));
toParse.append(" ").append(calendar.get(Calendar.HOUR_OF_DAY) < 10 ? "0" : "")
.append(calendar.get(Calendar.HOUR_OF_DAY));
toParse.append(":").append(calendar.get(Calendar.MINUTE) < 10 ? "0" : "")
.append(calendar.get(Calendar.MINUTE));
toParse.append(":").append(calendar.get(Calendar.SECOND) < 10 ? "0" : "")
.append(calendar.get(Calendar.SECOND));
localDateTime = LocalDateTime.parse(toParse, FORMATTER);
//Why 2015 and not 2016?
assertEquals("2015-01-01T23:59:59", localDateTime.toString());
}
it bugs me with is 2015 and not 2016, be aware that the solution is hammered to give you 2015 instead of 2016.
I'm still using Joda and no java8, but there it would be
Chronology lenient = LenientChronology.getInstance(ISOChronology.getInstance());
DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:sssss").withChronology(lenient);
DateTime dateTime = DateTime.parse("2016-01-01 00:00:86399", FORMATTER);
assertEquals("2016-01-01T23:59:59", dateTime.toLocalDateTime().toString());
Maybe you can obtain the same behavior using:
DateTimeFormatter formatter =
DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT);

Convert a string into iso date format

So I am trying to convert a string into an iso format for the date.
This is the string that I am trying to convert "2016-07-05 02:14:35.0"
I would like to have it in this format the iso 8601
"2016-07-05T02:14:35.0"
I have this so far
DateTimeFormatter format = DateTimeFormat.forPattern("YYYY-MM-DDTHH:mm:sszzz");
new LocalDate();
LocalDate newDate = LocalDate.parse(created,format);
created = newDate.toString();
But it is giving me this exception
ERROR: Illegal pattern component: T; nested exception is java.lang.IllegalArgumentException: Illegal pattern component: T
I followed the examples and I don't know what I am doing wrong here.
Any help would be appreciated.
Firstly, that value is a LocalDateTime, not a LocalDate. If you want to get a date out in the end, I'd convert it to a LocalDateTime first, then take the date part of that.
When performing date formatting and parsing, always read the documentation really carefully. It looks like you're using Joda Time (due to using forPattern; if you can move to Java 8 that would be beneficial). That means you should be reading the DateTimeFormat docs.
Current problems with your pattern:
You're using 'D' instead of 'd'; that means day-of-year
You've specified 'T' without quoting it, and it isn't in the pattern anyway
You've ignored the fraction-of-second part of your value
You've specified 'zz' when there's no time zone indicator in the value.
Here's a working example:
import org.joda.time.*;
import org.joda.time.format.*;
public class Test {
public static void main(String[] args) {
String text = "2016-07-05 02:14:35.0";
DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
LocalDateTime localDateTime = LocalDateTime.parse(text, format);
System.out.println(localDateTime);
}
}
If you actually want to parse values with T in the middle, you'd use a pattern of "yyyy-MM-dd'T'HH:mm:ss.S" - note how then the T is quoted so it's treated literally instead of as a format specifier.
Note that this is just parsing. It's not "converting a string into ISO date format" - it's converting a string into a LocalDateTime. If you then want to format that value in an ISO format, you need to be using DateTimeFormatter.print, with an appropriate format. For example, you might want to convert to a format of yyyy-MM-dd'T'HH:mm:ss.S':
import org.joda.time.*;
import org.joda.time.format.*;
public class Test {
public static void main(String[] args) {
String text = "2016-07-05 02:14:35.0";
DateTimeFormatter parser = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.S");
LocalDateTime localDateTime = LocalDateTime.parse(text, parser);
DateTimeFormatter printer = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.S");
String formatted = printer.print(localDateTime);
System.out.println(formatted); // Output 2016-07-05T02:14:35.0
}
}
The code above will only handle a single digit fraction-of-second. You could parse using .SSS instead of .S, but you really need to work out what you want the output to be in different cases (e.g. for 100 milliseconds, do you want .1 or .100?).
You have some errors in your code:
The pattern should be 'yyyy-MM-dd HH:mm:ss.SSS'. Be aware of upper-
and lowercase.
Use LocalDateTime to get date and time. LocalDate only holds the date.
The corrected code:
String created = "2016-07-05 02:14:35.000";
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
LocalDateTime newDate = LocalDateTime.parse(created,format);
created = newDate.toString();
System.out.println(created);
Use the following format to convert
String format = "yyyy-mm-dd hh:mm:ss"
You are using the wrong format to convert. Using T is only to separate the date from time.
Use the format like this
String = "yyyy-MM-dd'T'HH:mm:ss"

Categories

Resources