Reputation: 39437
I am using Java 11.
I have a datetime string which looks like this
"2023-02-17T03:56:50.254"
But I know it represents a point in time in UTC time zone.
How do I parse this string taking into account it's actually in UTC (the parsing has to be independent of my default JVM time-zone)?
And then how do I convert it to another string which represents the same date/time in e.g. America/NewYork time zone?
There are many parsing examples here but none of them seems to represent exactly my case.
Upvotes: 1
Views: 1459
Reputation: 338171
LocalDateTime // Represent a date with time-of-day, but lacking the context of a time zone or offset from UTC.
.parse( "2023-02-17T03:56:50.254" ) // Parse text in standard ISO 8601 format.
.atOffset( // Assign an offset to this date-with-time.
ZoneOffset.UTC // Constant for an offset-from-UTC of zero hours-minutes-seconds.
) // Returns an `OffsetDateTime` object.
.atZoneSameInstant( // Adjust from a moment as seen with an offset of zero to being seen with a time zone some number of hours-minutes-seconds ahead/behind UTC.
ZoneId.of( "America/New_York" ) // Represent a time zone. Name is in format of Continent/Region.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text in standard ISO 8601 format extended wisely to append the name of the zone in square brackets.
See this code run at Ideone.com. Notice how the date as well as the time-of-day differ from that of the input.
2023-02-16T22:56:50.254-05:00[America/New_York]
Per the Comment by Ole V.V., the appropriate class here is OffsetDateTime
rather than ZonedDateTime
.
Continent/Region
.The java.time classes offer two classes to represent each concept:
ZoneOffset
ZoneId
And the java.time classes offer two classes to represent a moment as seen in each way.
OffsetDateTime
ZonedDateTime
By the way, the Instant
class is a more basic building-block class in java.time, representing a moment as seen in UTC, always in UTC (an offset of zero).
LocalDateTime
So, yes parse your input as date with time-of-day, a LocalDateTime
.
LocalDateTime ldt = LocalDateTime.parse( "2023-02-17T03:56:50.254" ) ;
The LocalDateTime
does not represent a moment, as it lacks the context of an offset or zone.
ZoneOffet
& OffsetDateTime
You said:
How do I parse this string taking into account it's actually in UTC
If you are certain that date and time was intended to represent a moment "in UTC", that is with an offset of zero hours-minutes-seconds, then apply a ZoneOffset
rather than a ZoneId
to produce an OffsetDateTime
rather than a ZonedDateTime
.
Java comes with a constant defined for an offset of zero, ZoneOffset.UTC
.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
ZoneId
& ZonedDateTime
You asked:
And then how do I convert it to another string which represents the same date/time in e.g. America/NewYork time zone?
Define your desired time zone. Use ZoneId
for a zone (ZoneOffset
for a mere offset).
ZoneId zoneId = ZoneId.of( "America/New_York" ) ;
Apply that time zone to produce a ZonedDateTime
.
ZonedDateTime zdt = odt.atZoneSameInstant( zoneId ) ;
In this example, both odt
and zdt
represent the same simultaneous moment, the same point on the timeline. That moment is what is meant by “Instant” in the method name atZoneSameInstant
and in the class Instant
. The odt
and zdt
variables differ in their wall-clock time & calendar. Like someone in Reykjavík Iceland calling someone in Los Angeles California — when each party simultaneously glances at the clock and calendar hanging on their respective wall, they see a different time and date.
In contrast, the LocalDateTime
variable named ldt
does not represent a moment, is not a point on the timeline. That class purposely lacks the context of a time zone or offset. So the LocalDateTime
class cannot represent a moment.
Upvotes: 2
Reputation: 39437
Thanks for all the comments and answers. Putting them all together, I think this is what I needed.
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main001 {
public static void main(String[] args) {
// Do first test with a winter date
String text = "2023-02-17T03:56:50.254";
LocalDateTime local = LocalDateTime.parse(text);
ZonedDateTime zdt = local.atZone(ZoneId.of("UTC"));
Instant instant = zdt.toInstant();
System.out.println(instant);
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
.withZone(ZoneId.of("America/New_York"));
System.out.println(formatter.format(instant));
// Do second test with a summer date
text = "2023-07-17T03:56:59.257";
local = LocalDateTime.parse(text);
zdt = local.atZone(ZoneId.of("UTC"));
instant = zdt.toInstant();
System.out.println(instant);
formatter =
DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
.withZone(ZoneId.of("America/New_York"));
System.out.println(formatter.format(instant));
}
}
Output:
2023-02-17T03:56:50.254Z
2023-02-16T22:56:50.254
2023-07-17T03:56:59.257Z
2023-07-16T23:56:59.257
Upvotes: 1
Reputation: 270758
First, parse the string into a LocalDateTime
, because that's all there is in the string - a date and a time.
Then, you can convert it to a ZonedDateTime
or an Instant
, depending on your interpretation - whether this is a date and time in the UTC
zone, or a point in time.
var parsedResult =
LocalDateTime.parse(string/*, DateTimeFormatter.ISO_DATE_TIME*/)
.toInstant(ZoneOffset.UTC); // for an Instant
// .atZone(ZoneOffset.UTC); // for a ZonedDateTime
After you have either of those, you can use withZoneSameInstant
/atZone
respectively to put it into another zone.
// if parsedResult is Instant
var atAnotherZone = parsedResult.atZone(anotherZone);
// if parsedResult is ZonedDateTime
var atAnotherZone = parsedResult.withZoneSameInstant(anotherZone);
Upvotes: 4