I'm trying to adjust the time of a java.time.Instant.
I tried this code:
Instant validFrom = //from my method parameter
validFrom = validFrom.with(ChronoField.HOUR_OF_DAY, 19).with(ChronoField.MINUTE_OF_DAY, 00)
.with(ChronoField.SECOND_OF_DAY, 00).with(ChronoField.MILLI_OF_DAY, 00);
But I've an exception:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
at java.time.Instant.with(Instant.java:720)
This is quite expected reading the documentation and checking the source code.
It's not really clear to me why I can't do that. Is there another way to to this operation without incurring in many conversions?
This is because Instant has no timezone information. 19:00 hours doesn't represent anything useful, unless you attach a timezone to it.
You can convert it to a ZonedDateTime object, and then convert it back to an Instant like this:
public void testInstant(){
Instant now = Instant.now();
ZonedDateTime utc = now.atZone(ZoneId.of("UTC"));
ZonedDateTime zonedDateTime = utc.withHour(19)
.withMinute(0)
.withSecond(0);
Instant instant = zonedDateTime.toInstant();
}
Related
I'm learning Java and come across this issue. I have a date string with the given format.
String dbTime = "01/01/1998 12:30:00";
final String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss";
Now I wanted to initialize/create a Date object of UTC timezone.
For this, I have tried below code
SimpleDateFormat sdfAmerica = new SimpleDateFormat(DATE_FORMAT);
TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
sdfAmerica.setTimeZone(utcTimeZone);
String sDateInAmerica = sdfAmerica.format(date); // Convert to String first
Date dateInAmerica = new Date();
try {
dateInAmerica = formatter.parse(sDateInAmerica); // Create a new Date object
} catch (ParseException e) {
e.printStackTrace();
}
This will convert the time into UTC instead of just creating a date object.
01/02/1998 23:00:00
Now I'm confused as to which is the correct approach to convert the time.
I have time in string format and I have to convert it into different formats mainly UTC to PST or PST to UTC.
After some research, I found this tutorial but was unable to get the expected output.
The java.util.Date class is not optimal to start with. While it looks like a full date from the outside, it actually only represents a timestamp without storing actual timezone information.
On Java 8 and later I'd suggest to stick with the better designed java.time.* classes.
String dbTime = "01/01/1998 12:30:00";
String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss";
// parsed date time without timezone information
LocalDateTime localDateTime = LocalDateTime.parse(dbTime, DateTimeFormatter.ofPattern(DATE_FORMAT));
// local date time at your system's default time zone
ZonedDateTime systemZoneDateTime = localDateTime.atZone(ZoneId.systemDefault());
// value converted to other timezone while keeping the point in time
ZonedDateTime utcDateTime = systemZoneDateTime.withZoneSameInstant(ZoneId.of("UTC"));
// timestamp of the original value represented in UTC
Instant utcTimestamp = systemZoneDateTime.toInstant();
System.out.println(utcDateTime);
System.out.println(utcTimestamp);
As you can see from the names alone there are different classes for different use-cases of dates.
java.time.LocalDateTime for example only represents a date and time without a specific timezone context and therefore can be used to parse your string value directly.
To convert timezones, you first have to convert into the a ZonedDateTime, which accepts date, time and timezone. I've intialized the sample on "systemDefault", as on most smaller apps you can use the JVM and OS'es default value to assume the current timezone.
You could also use ZoneId.of("America/Los_Angeles") directly if you want to make sure the value is interpreted as pacific time.
This value can be converted into another ZonedDateTime in another timezone, e.g. UTC.
For UTC especially you could also use the Instant class, which represents only a UTC timestamp and can also be used as a basis for most other types
I have a Spring Boot JPA Application that interacts with a 3rd party API.
The response payload of the API has a key
"created_at": 1591988071
I need to parse this field into java.time.Instant so that I can do some comparisons with the value I have in the Database.
I have learned that I can use the below mentioned piece of code.
Instant instant = Instant.ofEpochSecond(1591988071);
Output :
2020-06-12T18:54:31Z
But to be honest, this output is off by a couple of hours.
I found another approach, wherein if I use
String dateAsText = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date(1591988071 * 1000L));
System.out.println(dateAsText);
I get the desired output but in String format.
2020-06-13 00:24:31
Can someone tell me how to obtain the above String output but converted into type java.time.Instant ?
It is likely you're in a different timezone than UTC. The instant is giving you time in UTC. That's indicated by the Z at the end of your first output.
You would want to look at atZone
Instant instant = Instant.ofEpochSecond(1591988071);
System.out.println(instant);
final ZonedDateTime losAngeles = instant.atZone(ZoneId.of("America/Los_Angeles"));
System.out.println(losAngeles);
final ZonedDateTime mumbai = instant.atZone(ZoneId.of("UTC+0530"));
System.out.println(mumbai);
This gives you something you might expect
2020-06-12T18:54:31Z
2020-06-12T11:54:31-07:00[America/Los_Angeles]
2020-06-13T00:24:31+05:30[UTC+05:30]
Input to my method will be a String containing a date in UTC. I need to compare the input date with current date and time and check the difference between two dates. The result should be in days.
I tried the following with no success.
String dateString = "2019-06-18T16:23:41.575 UTC";
final DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'").withZone(ZoneId.of("UTC"));
OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
System.out.println("======================:"+parsedDate.format(formatter1));
OffsetDateTime currentUTC = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println("Until (with crono): " + parsedDate.until(currentUTC, ChronoUnit.DAYS));
I need the result in an int (i.e., number of days).
The line OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1); throws an exception with the following stack trace:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2019-06-18T16:23:41.575 UTC' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1959)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1894)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
at thiagarajanramanathan.misc.App.main(App.java:86)
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:370)
at java.base/java.time.format.Parsed.query(Parsed.java:235)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1890)
... 3 more
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.ZoneOffset.from(ZoneOffset.java:348)
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:359)
... 5 more
As you can see from this thread: Unable to obtain OffsetDateTime from TemporalAccessor
I changed the following lines:
//OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
ZonedDateTime parsedDate = ZonedDateTime.parse(dateString, formatter1);
When your code is run with this modification, I could get the following results
for "2019-06-18T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 0
Since it's less than 24 hours, it returns 0
for "2019-06-17T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 1
Similarly, since it's over 24 hours but under 2 days, it returns 1.
I think this is what you want. Please try it and let me know if this works for you.
Parsing
I would simplify the parsing if your input by getting it to comply with the ISO 8601 standard.
String input = "2019-06-18T16:23:41.575 UTC".replace( " UTC", "Z" ) ;
Instant instant = Instant.parse( input ) ;
Days as 24-hour chunks
If your definition of elapsed days is 24-hour chunks of time, use Duration.
Duration d = Duration.between( instant , Instant.now() ;
long days = d.toDays() ;
Days according to calendar
If you want a count of days elapsed as seen on the calendar, meaning dates rather than 24-hour chunks of time, you must specify a time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
Extract the dates.
LocalDate start = zdt.toLocalDate() ;
LocalDate stop = now.toLocalDate() ;
long days = ChronoUnit.DAYS.between( start , stop ) ;
The difference between an time zone and an offset
You have got two good answers already. You are touching on an interesting and a bit tricky part of java.time, so I should like to make my contribution too. My key point is that a time zone and a UTC offset are not the same. To obtain an OffsetDateTime you need an offset. You provide a time zone through the call .withZone(ZoneId.of("UTC")) on the formatter, but it doesn’t help you. Yes, you and I know that UTC is the base of all offsets and therefore itself defines an offset of 0. But Java didn’t discover that from your code.
I admit I was surprised to discover that the following simple change was enough that your code runs on Java 9:
final DateTimeFormatter formatter1
= DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'")
.withZone(ZoneOffset.UTC);
However on Java 8 I still get the same exception as before. The output I got on Java 9.0.4 was:
======================:2019-06-18T16:23:41.575 UTC
Until (with crono): 0
The only change is that I am now passing a ZoneOffset rather than a ZoneId object to withZone (this is possible because ZoneOffset is a subclass of ZoneId).
A formatter that works on Java 8 too is one where we supply a default offset. For that we need a DateTimeFormatterBuilder:
final DateTimeFormatter formatter1 = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendLiteral(" UTC")
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
Yet another and perhaps simpler option would be to parse into a LocalDateTime first (which requires neither offset nor time zone) and then convert to OffsetDateTime by calling .atOffset(ZoneOffset.UTC).
The problem is, that I have to change my code from Calendar object to LocalDateTime object. But I don't get the same timestamp at the end. In the first call I got the same with localDateTime, on the next calls I get other timestamps and I use the same parameter to calculate the timestamps. I don't know why I get different results. It isn't logic for me. What I want to do is: I get a UTC Timestamp. I want to set it on german(Europe/Berlin) time(important about summer and winter season). Then I want to calculate the start of the day(00:00) and the end of the day(23:59). Then I want to get the timestamp for this times.
I build an API with spring-boot. The above described function is invoked by a controller class from spring-boot. The first call after the start of the API calculates the expected results. But all next calls give other results. Always with 7200 difference. I tried other ways with localDateTime, but it never gaves the same timestamp as with calendar.
LocalDateTimeWay:
LocalDateTime localDateTime =
LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
LocalDateTime dayStartLocal = localDateTime.withHour(0)
.withMinute(0)
.withSecond(0)
.withNano(0);
ZonedDateTime startZonedDateTime = dayStartLocal.atZone(ZoneId.systemDefault());
long dayStartTimeStamp = startZonedDateTime.toInstant().getEpochSecond();
LocalDateTime dayEndLocal = localDateTime.withHour(23)
.withMinute(59)
.withSecond(59)
.withNano(999);
ZonedDateTime endZonedDateTime = dayEndLocal.atZone(ZoneId.systemDefault());
long dayEndTimeStamp = endZonedDateTime.toInstant().getEpochSecond();
CalendarWay:
Calendar cal=Calendar.getInstance();
cal.setTimeInMillis(timestamp*1000);
cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
cal.set(Calendar.HOUR_OF_DAY,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
long dayStartTimeStamp = calendar.getTimeInMillis()/1000L;
cal.set(Calendar.HOUR_OF_DAY,23);
cal.set(Calendar.MINUTE,59);
cal.set(Calendar.SECOND,59);
cal.set(Calendar.MILLISECOND,999);
long dayEndTimeStamp = calendar.getTimeInMillis()/1000L;
I want by the param timestamp 1536933600. The result 1536876000 and 1536962399. But I get after the first request by localDateTime method 1536883200 and 1536969599.
You are using system default zone for your java.time code and Europe/Berlin zone for Calendar code. The 7200 is most likely the difference between your system time zone and Europe/Berlin (2 hours).
Replace all ZoneId.systemDefault() with ZoneId.of("Europe/Berlin") and you will get the same values in both versions:
timestamp = 1536933600
dayStartTimeStamp = 1536876000
dayEndTimeStamp = 1536962399
I have UTC datetime for example
String dateTime = "2018-04-23 19:50:53.236";
I want to convert this into specific TimeZone US/Eastern,and then i want to check that converted datetime comes under DaylightSavings
TimeZone
TimeZone.getTimeZone("US/Eastern");
DaylightSavings Code
ZoneId.of("US/Eastern")
.getRules()
.isDaylightSavings(
Instant.now()
)
If isDaylightSavings returns true the i have to append offset (-04:00) to input dateTime
Sample Output
dateTime = "2018-04-23T19:50:53-04:00"
If isDaylightSavings returns false the i have to append offset (-05:00) to input dateTime
Sample Output
dateTime = "2018-04-23T19:50:53-05:00"
I have pieces of code, but i'm confused how to combine all of them, and last question
How to generate current datetime f different zone in UTC with offset, for example consider this US/Eastern
Sample Output
dateTime = "2019-01-14T14:12:53-05:00"
As has been said in the comments, this is more automatic than you think.
DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
ZoneId zone = ZoneId.of("America/New_York");
String dateTime = "2018-04-23 19:50:53.236";
ZonedDateTime usEasternTime = LocalDateTime.parse(dateTime, inputFormatter)
.atOffset(ZoneOffset.UTC)
.atZoneSameInstant(zone);
String formattedDateTime = usEasternTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
System.out.println(formattedDateTime);
Output is:
2018-04-23T15:50:53.236-04:00
Your requested offset of -04:00 is output as part of the standard ISO 8601 format. The time output is 15:50:53, where you had asked for 19:50:53. I understood that 19:50:53 was in UTC, and at this UTC time, the time in Eastern USA is 15:50:53 or 4 hours less.
If instead we take a date in winter, we get -05:00 and the time of day is 5 hours less than the UTC time:
String dateTime = "2018-11-23 19:50:53.236";
2018-11-23T14:50:53.236-05:00
EDIT:
any idea how to remove milliseconds…
String formattedDateTime = usEasternTime.truncatedTo(ChronoUnit.SECONDS)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
2018-04-23T15:50:53-04:00
(continued)
…and this [America/New_York]
When you print the ZonedDateTime, the Zone ID is printed too. Above I use a built-in formatter to control the output. The other option is to convert to OffsetDateTime:
OffsetDateTime odt = usEasternTime.truncatedTo(ChronoUnit.SECONDS)
.toOffsetDateTime();
System.out.println(odt);
2018-04-23T15:50:53-04:00
Had the 19:50:53 been in Eastern Time, it would have been still a bit simpler:
ZonedDateTime usEasternTime = LocalDateTime.parse(dateTime, inputFormatter)
.atZone(zone);
2018-04-23T19:50:53.236-04:00
The current way to identify a time zone is region/city, so I use America/New_York even though the now deprecated US/Eastern still works and produces the same result.
The TimeZone class has design problems and is outdated, replaced by ZoneId, so just use the latter.
Link: List of tz database time zones on Wikipedia