I am currently reading dates in JSON format as follows:
"dates": {
"startdate": "2017-08-29T22:00:00.000UTC";
}
And in my application, I set the JsonFormat as follows to be able to read it correctly:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'UTC'")
private Date startdate;
But UTC isn't the TimeZone I want to work with, what should I change 'UTC' into to be able to read my dateTime in the Europe/Paris zone?
Alter the input to comply with the ISO 8601 standard. The Z is short for Zulu and means UTC.
String input = "2017-08-29T22:00:00.000UTC".replace( "UTC" , "Z" ) ;
Parse as an Instant object.
Instant instant = Instant.parse( input ) ;
Adjust into your desired time zone.
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Avoid the Date class as that troublesome class is now legacy, supplanted by the java.time classes.
Related
Hello I am trying to convert a Date and Time from a SQL data base that is being stored in UTC time to my systems Local Time zone that will be displayed in a table in a JAVAFX application.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String appointmentStart = rs.getString("Start");
LocalDateTime ldtStart = LocalDateTime.parse(appointmentStart, formatter);
ZonedDateTime zdtStart = ZonedDateTime.of(ldtStart, ZoneId.systemDefault());
appointmentStart = zdtStart.format(formatter);
String appointmentEnd = rs.getString("End");
LocalDateTime ldtEnd = LocalDateTime.parse(appointmentEnd, formatter);
ZonedDateTime zdtEnd = ZonedDateTime.of(ldtEnd, ZoneId.systemDefault());
appointmentEnd = zdtEnd.format(formatter);
This is what I have written. The table is being populated in UTC time and is not converting. I get no errors but is this logic wrong? If so how can I do this?
Welcome to Stack Overflow!
There are a couple issues with your code:
Look and code lines 3 and 4. You don't actually parse as UTC.
Since your appointmentStart string doesn't include the timezone, you need to specify that in your DateTimeFormatter.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneOffset.UTC);
String appointmentStart = rs.getString("Start");
// UTC
ZonedDateTime ldtStart = ZonedDateTime.parse(appointmentStart, formatter);
// Local.
ZonedDateTime start = ldtStart.withZoneSameInstant(ZoneId.systemDefault());
tl;dr
DateTimeFormatter // Class for generating text that represents the value of a date-time object.
.ofLocalizedDateTime( FormatStyle.FULL ) // Automatically localize the generated text.
.withLocale( Locale.FRANCE ) // Whatever human language and cultural norms are best for presentation to this particular user. Or call `Locale.getDefault`.
.format( // Generate text.
LocalDateTime // Represent a date with a time-of-day. Does *not* represent a moment as it lacks the context of a time zone or offset from UTC.
.parse( // Interpret text as a date-time value.
"2023-01-23 15:30:55"
.replace( " " , "T" ) // Comply with ISO 8601 format. Then we can proceed with parsing without bothering to specify a formatting pattern.
) // Returns a `LocalDateTime` object.
.atOffset( ZoneOffset.UTC ) // Returns a `OffsetDateTime` object.
.atZoneSameInstant(
ZoneId.systemDefault()
) // Returns a `ZonedDateTime` object.
) // Return a `String` object.
lundi 23 janvier 2023 à 07:30:55 heure normale du Pacifique nord-américain
Details
The Answer by CryptoFool and the Answer by Oliver are headed in the right direction. But I do not like that they directly inject an offset before parsing the date-time alone. I also do not like that they use ZonedDateTime where OffsetDateTime is more appropriate.
Apparently you have an input string with a date and a time-of-day but lacking the context of a time zone or offset from UTC. For example: "2023-01-23 15:30:55".
String input = "2023-01-23 15:30:55" ;
That input nearly complies with the ISO 8601 standard format for date-time values. To fully comply, swap that SPACE in the middle with a T.
String modified = input.replace( " " , "T" ) ;
The java.time classes use ISO 8601 formats by default when parsing/generating text. So no need to specify a formatting pattern.
Parse as a date-with-time object, an LocalDateTime object.
LocalDateTime ldt = LocalDateTime.parse( modified );
You said you want to assume that date and time is intended to represent a moment as seen with an offset from UTC of zero hours-minutes-seconds. Java offers a constant for that offset, ZoneOffset.UTC. Apply that ZoneOffset to produce an OffsetDateTime object.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
Next you asked to adjust from that offset of zero to view the same moment in the JVM’s current default time zone. Same moment, same point on the timeline, but different wall-clock time.
Get that default zone.
ZoneId z = ZoneId.systemDefault() ;
Apply that ZoneId to the OffsetDateTime to get a ZonedDateTime.
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
Generate text in localized format. Specify the Locale to use in localizing for human language and cultural norms.
Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale );
String output = zdt.format( f );
Pull all that code together.
String input = "2023-01-23 15:30:55";
String modified = input.replace( " " , "T" );
LocalDateTime ldt = LocalDateTime.parse( modified );
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
ZoneId z = ZoneId.systemDefault();
ZonedDateTime zdt = odt.atZoneSameInstant( z );
Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale );
String output = zdt.format( f );
Dump to console.
input = 2023-01-23 15:30:55
modified = 2023-01-23T15:30:55
ldt = 2023-01-23T15:30:55
odt.toString() = 2023-01-23T15:30:55Z
z.toString() = America/Los_Angeles
zdt.toString() = 2023-01-23T07:30:55-08:00[America/Los_Angeles]
output = Monday, January 23, 2023 at 7:30:55 AM Pacific Standard Time
There may be a slicker way to do this, but here's how I would do this:
// We have an unzoned datetime String that we assume is in UTC
String appointmentStart = "2022-12-24 11:00:00";
// Add a UTC time (+0) offset to the end of our String, and parse it to get a UTC-zoned ZonedDateTime
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ");
ZonedDateTime utcStart = ZonedDateTime.parse(appointmentStart + "+0000", formatter);
// Convert to the local timezone
ZonedDateTime localZonedStart = utcStart.withZoneSameInstant(ZoneId.systemDefault());
// Define a formatter that doesn't include the time zone
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// Convert the local time to a String
String localZonedStartString = localZonedStart.format(formatter2);
// Print the resulting String
System.out.println(localZonedStartString);
Result:
2022-12-24 03:00:00
I'm in Pacific time zone, which is offset by -8 hours from UTC, and we see that the resulting string is correct for my local time zone.
I'm trying parse this String 2020-05-20 14:27:00.943000000 +00:00 and this Wed May 20 14:27:00 CEST 2020 to a ISO_INSTANT, but always return this exception
java.time.format.DateTimeParseException: Text '2020-05-20 14:27:00.943000000 +00:00' could not be parsed at index 10
My code is:
protected Instant parseDateTime(String fechaHora) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
TemporalAccessor temporalAccessor = formatter.parse(fechaHora);
LocalDateTime localDateTime = LocalDateTime.from(temporalAccessor);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
Instant result = Instant.from(zonedDateTime);
return result; }
How can I convert this types?
tl;dr
OffsetDateTime.parse(
"2020-05-20 14:27:00.943000000 +00:00" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss.SSSSSSSSS xxx" )
)
.toInstant()
Fixing your code
Your code is flawed in a few ways.
Use of TemporalAccessor is unnecessary and inappropriate here. To quote its Javadoc:
This interface is a framework-level interface that should not be
widely used in application code. Instead, applications should create
and pass around instances of concrete types
LocalDateTime is not appropriate here as it strips away vital information, the time zone or offset-from-UTC.
You specified a formatter whose formatting pattern does not match your inputs.
Solution
Manipulate your input string to comply with standard ISO 8601 format. Replace the SPACE between date and time with T. Delete SPACE between time and offset.
String input = "2020-05-20 14:27:00.943000000 +00:00" ;
String[] strings = input.split( " " ) ;
String modifiedInput = strings[0] + "T" + strings[1] + strings[2] ;
Parse as an OffsetDateTime, a date with time-of-day in the context of an offset-from-UTC.
OffsetDateTime odt = OffsetDateTime.parse( modifiedInput ) ;
Or, define a formatting pattern to match your input string. Use the DateTimeFormatter class. This has been covered many many times already on Stack Overflow, so search to learn more.
The predefined formatter DateTimeFormatter.ISO_INSTANT that you tried to use does not match your input. Your input does not comply with the ISO 8601 standard used by that formatter.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss.SSSSSSSSS xxx" ) ;
String input = "2020-05-20 14:27:00.943000000 +00:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
See this code run live at IdeOne.com.
odt.toString(): 2020-05-20T14:27:00.943Z
If you need to return an Instant, call toInstant.
Instant instant = odt.toInstant() ;
To see that same moment in the context of a time zone, apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
The OffsetDateTime and ZonedDateTime objects represent the same moment, same point on the timeline.
The cause of your exception is the different format between your String 2020-05-20 14:27:00.943000000 +00:00 and ISO_INSTANT ; from DateTimeFormatter ISO_INSTANT accepts strings like 2011-12-03T10:15:30Z and this is not your case. A possible solution to this problem is use a custom DateTimeFormatter like below:
String fechaHora = "2020-05-20 14:27:00.943000000 +00:00";
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS ZZZZZ");
TemporalAccessor temporalAccessor = formatter.parse(fechaHora);
LocalDateTime localDateTime = LocalDateTime.from(temporalAccessor);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
Instant result = Instant.from(zonedDateTime);
System.out.println(result); //<-- it will print 2020-05-20T12:27:00.943Z
I want to get the UTC instant (since my DB is storing in UTC) from Java (which is also in UTC) of a particular time zone, this is what I have tried so far:
public static Instant getStartOfTheDayDateTime(Instant instant, String zoneId) {
ZonedDateTime zoned = instant.atZone(ZONE_ID_TO_ZONE_MAP.get(zoneId));
ZoneId zone = ZoneId.of(zoneId);
return zoned.toLocalDate().atStartOfDay(zone).toInstant();
// ZonedDateTime startOfTheDay = zoned.withHour(0)
// .withMinute(0)
// .withSecond(0)
// .withNano(0);
//
// return startOfTheDay.toInstant();
}
public static Instant getEndOfTheDayDateTime(Instant instant, String zoneId) {
ZonedDateTime zoned = instant.atZone(ZONE_ID_TO_ZONE_MAP.get(zoneId));
ZonedDateTime endOfTheDay = zoned.withHour(0)
.withMinute(0)
.withSecond(0)
.withNano(0)
.plusDays(1);
return endOfTheDay.toInstant();
}
Every attempt shows:
2020-04-10 22:00:00.0(Timestamp), 2020-04-11 22:00:00.0(Timestamp)
Is this the start/end of the day UTC time in Europe/Paris zone ?
I was expecting to have 2020-04-11 02:00:00.0(Timestamp), 2020-04-12 02:00:00.0(Timestamp)
Right now, Paris is on summer time: UTC+2. Paris is 'ahead' of UTC by 2 hours.
So 00:00:00 in Paris local time is 22:00:00 UTC.
Is this the start/end of the day UTC time in Europe/Paris zone ?
Yes. Europe/Paris is in daylight savings time. Midnight in Paris occurred at 22:00 UTC time.
I was expecting to have 2020-04-11 02:00:00.0(Timestamp), 2020-04-12 02:00:00.0(Timestamp)
That's not right, 02:00 UTC would have been 04:00 in Paris time.
Ask programmatically if a moment is in DST
Is this the start/end of the day UTC time in Europe/Paris zone ?
Get start of day.
ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtStartOfDay = instant.atZone( z ).toLocalDate().atStartOfDay( z ) ;
Ask if that moment is in DST for that zone.
ZoneRules rules = z.getRules();
boolean isDst = rules.isDaylightSavings( zdtStartOfDay.toInstant() );
Pass date-time objects rather than mere strings
public static Instant getStartOfTheDayDateTime(Instant instant, String zoneId)
I suggest you ask the calling programmer to pass a valid ZoneId object rather than a mere string. It should not be the job of this method to validate their string input. If it is reasonable to expect a Instant then it is also reasonable to expect a ZoneId.
public static Instant getStartOfTheDayDateTime(Instant instant, ZoneID zoneId )
Half-Open
public static Instant getEndOfTheDayDateTime(Instant instant, String zoneId) {
Trying to determine the last moment of the day is impossible because of infinitely divisible last second.
Also this approach to defining a span of time is awkward. It makes abutting multiple spans tricky. Various software systems and protocols differ in their resolution of that last fractional second, using milliseconds, microseconds, nanoseconds, or some other fraction.
The common practice in date-time handling is to track a span of time using the Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive.
So a full day begins with the first moment of the day and runs up to, but does not include, the first moment of the next day.
ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtStartOfDay = instant.atZone( z ).toLocalDate().atStartOfDay( z ) ;
ZonedDateTime zdtStartOfNextDay = instant.atZone( z ).toLocalDate().plusDays( 1 ).atStartOfDay( z ) ;
You might want to break that code out to more lines, for easier reading/debugging.
Instant instant = Instant.now() ; // Or passed in.
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
LocalDate ld = zdt.toLocalDate() ;
LocalDate ldNextDay = ld.plusDays( 1 ) ;
ZonedDateTime zdtStartOfNextDay = ldNextDay.atStartOfDay( z ) ;
See this code run live at IdeOne.com. For example:
System.out.println( instant ) ; // 2020-04-13T00:15:25.235341Z
System.out.println( zdt ) ; // 2020-04-13T02:15:25.235341+02:00[Europe/Paris]
System.out.println( ld ) ; // 2020-04-13
System.out.println( ldNextDay ) ; // 2020-04-14
System.out.println( zdtStartOfNextDay ) ; // 2020-04-14T00:00+02:00[Europe/Paris]
ThreeTen-Extra Interval
If you do this kind of work with spans of time often, then I suggest adding the ThreeTen-Extra library to your project. That library includes the Interval class to track a span-of-time as a a pair of Instant objects.
Interval interval = Interval.of( zdtStartOfDay.toInstant() , zdtStartOfNextDay.toInstant() ) ;
You can then make use the several handy comparison methods such as abuts, contains, encloses, intersection, overlaps, and union.
Timestamp
Never use the java.sql.Timestamp class. This class is part of the terrible date-time classes that shipped with the earliest versions of Java. These classes are now legacy, supplanted entirely by the modern java.time classes defined in JSR 310 and built into Java 8 and later.
As of JDBC 4.2 we can exchange java.time objects with a database. Use getObject and setObject and updateObject.
The JDBC spec oddly requires support for OffsetDateTime but not the more commonly used Instant and ZonedDateTime. Your particular driver may support these other types. If not, convert.
Retrieval from database.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;
Sending to the database.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
I need to pass in a Date object into a service which my API is calling. I have the info on the day, month, and year for the Date but also need a timestamp. The service is expecting it in this format:
<date>2015-04-01T00:00:00-05:00</date>
How can I add something to the Date to get this format?
Never use java.util.Date. Supplanted by java.time.Instant.
Get your date portion.
LocalDate ld = LocalDate.of( 2015 , 4 , 1 ) ;
Or use the readable Month enum.
LocalDate ld = LocalDate.of( 2015 , Month.APRIL , 1 ) ;
Get the time of day when the day starts in some particular time zone. Do not assume the day starts at 00:00:00, may be some other time such as 01:00:00. Let java.time figure that out for you.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
Generate a string in your desired format, a standard ISO 8601 format.
DateTimeFormatter f = DateTimeFormatter.ISO_OFFSET_DATE_TIME ;
String output = zdt.format( f ) ;
To see that moment in UTC, extract a Instant.
Instant instant = zdt.toInstant() ;
Conversion
If you must inter-operate with old code not yet updated for java.time, you can call new conversion methods added to the old classes. These include Date::from( Instant ).
java.util.Date d = java.util.Date.from( instant ) ;
Going the other direction.
Instant instant = d.toInstant() ;
Get back to a time zone other than UTC.
ZonedDateTime zdt = instant.atZone( ZoneId.of( "Pacific/Auckland" ) ) ; // Same moment, different wall-clock time.
Working with dates in Java is an ugly mess, always has been. Date class is mostly deprecated now. I am using LocalDateTime where you can construct it by calling year, month, day, hour, minute, and seconds. Here is what I could come up with:
LocalDateTime ldt = LocalDateTime.of(1997, Month.SEPTEMBER, 2, 1, 23, 0);
ZonedDateTime systemTime = ldt.atZone(ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; //Basil's idea
System.out.println(systemTime.format(formatter));
Output:
1997-09-02T01:23:00-05:00
You could use SimpleDateFormat for this.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
dateFormat.format(new Date());
I have this code to add 1 hour or 1 day in date Java 8, but doesn´t work
String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(DATE_FORMAT);
Date parse = format.parse("2017-01-01 13:00:00");
LocalDateTime ldt = LocalDateTime.ofInstant(parse.toInstant(), ZoneId.systemDefault());
ldt.plusHours(1);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date te = Date.from(zdt.toInstant());
What´s wrong? The code shows: Sun Jan 01 13:00:00 BRST 2017
LocalDateTime is immutable and returns a new LocalDateTime when you call methods on it.
So you must call
ldt = ldt.plusHours(1);
Apart from the issue that you don't use the result of your date manipulation (ldt = ldt.plusHours(1)), you don't really need to go via a LocalDateTime for this operation.
I would simply use an OffsetDateTime since you don't care about time zones:
OffsetDateTime odt = parse.toInstant().atOffset(ZoneOffset.UTC);
odt = odt.plusDays(1).plusHours(1);
Date te = Date.from(odt.toInstant());
You could even stick to using Instants:
Instant input = parse.toInstant();
Date te = Date.from(input.plus(1, DAYS).plus(1, HOURS));
(with an import static java.time.temporal.ChronoUnit.*;)
tl;dr
LocalDateTime.parse( // Parse input string that lacks any indication of offset-from-UTC or time zone.
"2017-01-01 13:00:00".replace( " " , "T" ) // Convert to ISO 8601 standard format.
).atZone( // Assign a time zone to render a meaningful ZonedDateTime object, an actual point on the timeline.
ZoneId.systemDefault() // The Question uses default time zone. Beware that default can change at any moment during runtime. Better to specify an expected/desired time zone generally.
).plus(
Duration.ofDays( 1L ).plusHours( 1L ) // Add a span of time.
)
Details
Do not mix the troublesome old legacy classes Date and Calendar with the modern java.time classes. Use only java.time, avoiding the legacy classes.
The java.time classes use the ISO 8601 standard formats by default when parsing and generating strings. Convert your input string by replacing the SPACE in the middle with a T.
String input = "2017-01-01 13:00:00".replace( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;
ALocalDateTime does not represent an actual moment, not a point on the timeline. It has no real meaning until you assign a time zone.
ZoneId z = ZoneId.systemDefault() ; // I recommend specifying the desired/expected zone rather than relying on current default.
ZonedDateTime zdt = ldt.atZone( z ) ;
A Duration represents a span of time not attached to the timeline.
Duration d = Duration.ofDays( 1L ).plusHours( 1L ) ;
ZonedDateTime zdtLater = zdt.plus( d ) ;