I have following timestamp string "2021010112:12:12.10:00" and I want to convert it to java.time.Instant.
The issue in parsing string through DateTimeFormatter is due to absence of time zone sign before last 4 digits. Logically it is yyyyMMddHH:mm:ss. and 10:00 is time zone offset e.g UTC+10:00, but issue is it does not have sign.
How can I parse this string to Instant object?
Not very elegant, but you could split the input by a dot. That would separate the datetime part from the offset and you can concatenate the desired (and required) sign with the value.
This requires you to know which sign to apply! The code cannot guess it...
Maybe write a method that takes this input String and a sign to be applied as arguments.
Since it seems not possible to parse an unsigned String representation of an offset, you would need something like the following:
public static void main(String[] args) {
String timestamp = "2021010112:12:12.10:00";
// provide a formatter that parses the datetime (the part before the dot)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ss");
// split the timestamp String by the dot to separate datetime from offset
String[] split = timestamp.split("\\.");
// parse the datetime part using the formatter defined above
LocalDateTime ldt = LocalDateTime.parse(split[0], dtf);
// and build up an offset using offset part adding a plus sign
ZoneOffset zoneOffset = ZoneOffset.of("+" + split[1]);
// then create an OffsetDateTime from the LocalDateTime and the ZoneOffset
OffsetDateTime result = OffsetDateTime.of(ldt, zoneOffset);
// finally get an Instant from it
Instant instant = result.toInstant(); // <--- INSTANT HERE
// and print the values
System.out.println(result + " = " + instant.toEpochMilli());
}
This outputs
2021-01-01T12:12:12+10:00 = 1609467132000
ParsePosition
There are two good answers already. Here’s my suggestion.
String timestampString = "2021010112:12:12.10:00";
ParsePosition position = new ParsePosition(0);
TemporalAccessor parsed = PARSER.parse(timestampString, position);
LocalDateTime dateTime = LocalDateTime.from(parsed);
String offsetString = timestampString.substring(position.getIndex());
if (Character.isDigit(offsetString.charAt(0))) { // no sign
offsetString = "+" + offsetString;
}
ZoneOffset offset = ZoneOffset.of(offsetString);
Instant timestamp = dateTime.atOffset(offset).toInstant();
System.out.println(timestamp);
Output:
2021-01-01T02:12:12Z
The downside is the use of the TemporalAccessor interface, a low-level interface that we should not normally use in application code. The upsides include that the code accepts strings with and without sign before the offset and we don’t need any split operation or other application of regular expressions. If the UTC offset is negative, the sign must be there, or we can’t tell. Let’s also try this:
String timestampString = "2021010112:12:12.-10:00";
2021-01-01T22:12:12Z
I believe that the ParsePosition class is the only class from the old java.text package that is reused in java.time, which I personally find curious.
The answer by deHaar is correct. This answer shows an easier way of solving this problem.
You can use the regex, (\.)(\d{1,2}:\d{1,2}) to replace the . (group#1 in the regex) before the timezone offset part (group#2 in the regex) with a +.
Demo:
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "2021010112:12:12.10:00";
strDateTime = strDateTime.replaceFirst("(\\.)(\\d{1,2}:\\d{1,2})", "+$2");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ssXXX", Locale.ENGLISH);
Instant instant = OffsetDateTime.parse(strDateTime, dtf).toInstant();
System.out.println(instant);
}
}
Output:
2021-01-01T02:12:12Z
ONLINE DEMO
Alternatively, you can use the regex, \.(\d{1,2}:\d{1,2}) and prepend group#1 with a + sign. Note that the DateTimeFormatter needs to be adjusted accordingly.
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "2021010112:12:12.10:00";
strDateTime = strDateTime.replaceFirst("\\.(\\d{1,2}:\\d{1,2})", ".+$1");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuuMMddHH:mm:ss.XXX", Locale.ENGLISH);
Instant instant = OffsetDateTime.parse(strDateTime, dtf).toInstant();
System.out.println(instant);
}
}
ONLINE DEMO
The benefits of using this alternative solution is as described in the following comment by Ole V.V.:
Just speculating, maybe a minus sign would be present in case of a
negative UTC offset. If so, maybe use
strDateTime.replaceFirst("\\.(\\d{1,2}:\\d{1,2})", ".+$1") to obtain
2021010112:12:12.+10:00 in the positive offset case, after which both
positive and negative offset (and zero) could be parsed.
Related
I have a UTC date-time like this (a String): 2022-11-22T17:15:00
And a ZoneID like this: "America/Tijuana"
Using java.time API, I want to get the actual datetime for that zone, which is: 2022-11-22T09:15:00 (the time is 09:15 instead of 17:15)
ZonedDateTime.toLocalDateTime() returns: 2022-11-22T17:15
ZonedDateTime.toString() returns:
2022-11-22T17:15-08:00[America/Tijuana]
None of the above gives me what I'm looking for.
This is my code:
ZoneId zonaID = ZoneId.of('America/Tijuana');
CharSequence dateUTC = "2022-11-22T17:15:00";
LocalDateTime dateTimeL = LocalDateTime.parse(dateUTC);
ZonedDateTime myZDT = ZonedDateTime.now();
ZonedDateTime myZDTFinal = myZDT.of(dateTimeL, zonaID);
System.out.println("using toLocalDateTime: " + myZDTFinal.toLocalDateTime());
System.out.println("using toString: " + myZDTFinal.toString());
I know that this might be a duplicated question but there's so many questions about date-times and I just haven't been able to figure out this.
Any help will be really appreciated.
You have to convert your date to UTC, then convert the convert this zone to your expected zone using withZoneSameInstant like this:
ZonedDateTime toUTCZone = ZonedDateTime.of(dateTimeL, ZoneOffset.UTC);
ZonedDateTime myZDTFinal = toUTCZone.withZoneSameInstant(zonaID);
Output
2022-11-22T09:15-08:00[America/Tijuana]
There can be many ways to achieve the result. A simple approach would be
Parse the given string into LocalDateTime.
Convert it into an OffsetDateTime at UTC using LocalDateTime#atOffset.
Use OffsetDateTime#atZoneSameInstant to convert the resulting OffsetDateTime into a ZonedDateTime at ZoneId.of("America/Tijuana").
Get LocalDateTime out of the resulting ZonedDateTime by using ZonedDateTime#toLocalDateTime.
If required, format this LocalDateTime into the desired string.
LocalDateTime
.parse("2022-11-22T17:15:00") // Parse the given date-time string into LocalDateTime
.atOffset(ZoneOffset.UTC) // Convert it into a ZonedDateTime at UTC
.atZoneSameInstant(ZoneId.of("America/Tijuana")) // Convert the result into a ZonedDateTime at another time-zome
.toLocalDateTime() // Get the LocalDateTime out of the ZonedDateTime
.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss", Locale.ENGLISH))); // If required
Demo:
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDateTime ldtInTijuana = LocalDateTime.parse("2022-11-22T17:15:00")
.atOffset(ZoneOffset.UTC)
.atZoneSameInstant(ZoneId.of("America/Tijuana"))
.toLocalDateTime();
System.out.println(ldtInTijuana);
// Custom format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
String formatted = ldtInTijuana.format(formatter);
System.out.println(formatted);
}
}
Output:
2022-11-22T09:15
2022-11-22T09:15:00
Note that LocalDateTime#toString removes second and fraction-of-second values if they are zero. Suppose you want to keep them (as you have posted in your question), you can use a DateTimeFormatter as shown above.
An alternate approach:
Alternatively, you can append Z at the end of your ISO 8601 formatted date-time string to enable Instant to parse it and then convert the Instant into a ZonedDateTime corresponding to the ZoneId.of("America/Tijuana") by using Instant#atZone. The symbol, Z refers to UTC in a date-time string.
The rest of the steps will remain the same.
Demo:
public class Main {
public static void main(String[] args) {
String text = "2022-11-22T17:15:00";
text = text + "Z"; // Z refers to UTC
Instant instant = Instant.parse(text);
LocalDateTime ldt = instant.atZone(ZoneId.of("America/Tijuana")).toLocalDateTime();
System.out.println(ldt);
}
}
Output:
2022-11-22T09:15
Learn more about the modern Date-Time API from Trail: Date Time.
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 am trying to convert java String date into java.sql.Timestamp. I am able to convert this by using SimpleDateFormat with String date value as "2021-01-07 02:02:16.172", but when trying with the value as "2021-08-04T00:00:00.000" with seperator 'T', it gives me error. Below is the java code:
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateTest {
public static void main(String[] args) throws ParseException {
//String date = "2021-08-04T00:00:00.000Z";// How to convert this?
String date = "2021-01-07 02:02:16.172";// conversion successful
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
Date parsedDate = dateFormat.parse(date);
Timestamp timestamp = new java.sql.Timestamp(parsedDate.getTime());
System.out.println(timestamp);
}
}
You could use the modern API for dates, times and related information (like offsets from UTC): java.time
Strings in different formats need to be handled differently:
your first example String is formatted in ISO standard, so it can be parsed without defining a custom format. The parsing implicitly uses a DateTimeFormatter.ISO_OFFSET_DATE_TIME, which will result in an OffsetDateTime
your seconds String lacks the 'T' between date and time as well as an offset, that means you can just directly parse it to a LocalDateTime
java.sql.Timestamp got methods for conversion to java.time classes, at least to/from an Instant and a LocalDateTime. Since an Instant is a well defined moment in time, you can derive it from an OffsetDateTime:
public static void main(String[] args) throws Exception {
// your two example datetimes
String isoDateTime = "2021-08-04T00:00:00.000Z";
String customDateTime = "2021-01-07 02:02:16.172";
// you will need a custom formatter for the second one
DateTimeFormatter customDtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
// parse the Strings to java.time objects
// ISO standard, no extra formatter needed for the first one
OffsetDateTime odt = OffsetDateTime.parse(isoDateTime);
// the second one requires the formatter defined above
LocalDateTime ldt = LocalDateTime.parse(customDateTime, customDtf);
// convert them into Timestamps
Timestamp tsOne = Timestamp.from(odt.toInstant());
Timestamp tsTwo = Timestamp.valueOf(ldt);
// and print them
System.out.println("First Timestamp: " + tsOne);
System.out.println("Second Timestamp: " + tsTwo);
}
The output of this is
First Timestamp: 2021-08-04 02:00:00.0
Second Timestamp: 2021-01-07 02:02:16.172
This would be the new style...
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS");
would be the old style
I'm trying to take two strings and make it into a Date object. I'm having trouble trying to work out what formats I need to use.
The first string is a date and is in the format of : 5th Jan
The second string is a time and is in the format of : 8:15
The main issue is what the format would be for the 5th
Since your date string, 5th Jan doesn't have a year, you will have to use some default year e.g. the current year, which you can get from LocalDate.now(). You can put defaults using DateTimeFormatterBuilder#parseDefaulting. Additionally, you can also make the parser
case-insensitive by using DateTimeFormatterBuilder#parseCaseInsensitive.
In order to parse a date string, 5th Jan, you can use the pattern, d'th' MMM. However, in order to deal with other suffixes like in 3rd, 1st etc., you should use the pattern, d['th']['st']['rd']['nd'] MMM where the patterns inside the square bracket are optional.
In order to parse a time string like 8:15, you can use the pattern, H:m.
Demo:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.now();
DateTimeFormatter dtfForDate = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.parseDefaulting(ChronoField.YEAR, date.getYear())
.appendPattern("d['th']['st']['rd']['nd'] MMM")
.toFormatter(Locale.ENGLISH);
DateTimeFormatter dtfForTime = DateTimeFormatter.ofPattern("H:m", Locale.ENGLISH);
String strDate = "5th Jan";
String strTime = "8:15";
LocalDateTime ldt = LocalDate.parse(strDate, dtfForDate)
.atTime(LocalTime.parse(strTime, dtfForTime));
// Print the default string value i.e. the value returned by ldt.toString()
System.out.println(ldt);
// The default format omits seconds and fraction of second if they are 0. In
// order to retain them in the output string, you can use DateTimeFormatter
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss");
String formatted = dtf.format(ldt);
System.out.println(formatted);
}
}
Output:
2021-01-05T08:15
2021-01-05T08:15:00
Learn about the modern date-time API from Trail: Date Time.
Here is my method to parse String into LocalDateTime.
public static String formatDate(final String date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS");
LocalDateTime formatDateTime = LocalDateTime.parse(date, formatter);
return formatDateTime.atZone(ZoneId.of("UTC")).toOffsetDateTime().toString();
}
but this only works for input String like
2017-11-21 18:11:14.05
but fails for 2017-11-21 18:11:14.057
with DateTimeParseException.
How can I define a formatter that works for both .SS and .SSS?
You would need to build a formatter with a specified fraction
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss")
.appendFraction(ChronoField.MILLI_OF_SECOND, 2, 3, true) // min 2 max 3
.toFormatter();
LocalDateTime formatDateTime = LocalDateTime.parse(date, formatter);
The answers by Basil Bourque and Sleiman Jneidi are excellent. I just wanted to point out that the answer by EMH333 has a point in it too: the following very simple modification of the code in the question solves your problem.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.[SSS][SS]");
The square bracket in the format pattern string enclose optional parts, so this accepts 3 or 2 decimals in the fraction of seconds.
Potential advantage over Basil Bourque’s answer: gives better input validation, will object if there is only 1 or there are four decimals on the seconds (whether this is an advantage depends entirely on your situation).
Advantage over Sleiman Jneidi’s answer: You don’t need the builder.
Possible downside: it accepts no decimals at all (as long as the decimal point is there).
As I said, the other solutions are very good too. Which one you prefer is mostly a matter of taste.
tl;dr
No need to define a formatter at all.
LocalDateTime.parse(
"2017-11-21 18:11:14.05".replace( " " , "T" )
)
ISO 8601
The Answer by Sleiman Jneidi is especially clever and high-tech, but there is a simpler way.
Adjust your input string to comply with ISO 8601 format, the format used by default in the java.time classes. So no need to specify a formatting pattern at all. The default formatter can handle any number of decimal digits between zero (whole seconds) and nine (nanoseconds) for the fractional second.
Your input is nearly compliant. Just replace the SPACE in the middle with aT.
String input = "2017-11-21 18:11:14.05".replace( " " , "T" );
LocalDateTime ldt = LocalDateTime.parse( input );
ldt.toString(): 2017-11-21T18:11:14.050
Using Java 8 you can use the DateTimeFormatterBuilder and a Pattern. See this answer for a little more information
public static String formatDate(final String date) {
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd HH:mm:ss.SSS]"
+ "[yyyy-MM-dd HH:mm:ss.SS]"));
DateTimeFormatter formatter = dateTimeFormatterBuilder.toFormatter();
try {
LocalDateTime formatDateTime = LocalDateTime.parse(date, formatter);
return formatDateTime.atZone(ZoneId.of("UTC")).toOffsetDateTime().toString();
} catch (DateTimeParseException e) {
return "";
}
}
Ideally, you would account for a time that has 0 nanoseconds as well. If the time so happens to land perfectly on 2021-02-28T12:00:15.000Z, it may actually be serialised to 2021-02-28T12:00:15Z (at least, for something like java.time.OffsetDateTime it would be). It would therefore be more appropriate to use the following:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS][.SS][.S]");
... and if you require time zone, like I did, then it would look this:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS][.SS][.S]z");
DateTimeFormatter allows specifying optional units using square brackets.
Demo:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss[.[SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]]",
Locale.ENGLISH);
// Test
Stream.of(
"2015-05-04 12:34:56.123456789",
"2015-05-04 12:34:56.123456",
"2015-05-04 12:34:56.123",
"2015-05-04 12:34:56"
).forEach(s -> System.out.println(LocalDateTime.parse(s, formatter)));
}
}
Output:
2015-05-04T12:34:56.123456789
2015-05-04T12:34:56.123456
2015-05-04T12:34:56.123
2015-05-04T12:34:56
Learn more about the modern date-time API from Trail: Date Time.