So I have:
Date startDate which is Sun Mar 27 17:32:01 EEST 2022
and
String period which is PT240H
And I need to generate a new date based on those 2 values. I need to add that 240H period to the startDate. The 240H meaning 10 days which I need to add to startDate and I will eventually need to have a new date which should be Wed Apr 6 17:32.01 EEST 2022.
PS. I am new to Java, hopefully I don't ask stupid things.
tl;dr
java.util.Date.from(
myJavaUtilDate
.toInstant()
.plus( Duration.parse( "PT240H" ) )
)
Details
Putting together those posted Comments…
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310. Avoid using Date, Calendar, and such.
If handed a java.util.Date object, immediately convert to its replacement class, java.time.Instant. Use new conversion methods added to the old classes.
Instant instant = myJavaUtilDate.toInstant() ;
Parse your input string in standard ISO 8601 format as a Duration object.
Duration d = Duration.parse( "PT240H" ) ;
Add to our Instant to produce a second Instant, per immutable objects.
Instant later = instant.plus( d ) ;
You said:
The 240H meaning 10 days
Incorrect, 240 hours is not necessarily 10 days. Adding a value of 240 hours may or may not result in a moment ten days later, if you adjust into a time zone. Some dates in some time zones vary in length, running 23, 23.5, 25, or other numbers of hours long.
And be aware that both java.util.Date and Instant represent a moment as seen in UTC, that is, with an offset of zero hours-minutes-seconds. Unfortunately, the Date#toString method dynamically applies the JVM’s current default time zone while generating its text — giving a false illusion. This confusing behavior is one of the many design flows in the legacy date-time classes.
If you must interoperate with old code not yet updated to java.time, you can convert back to Date. But I strongly recommend moving away from these legacy classes ASAP.
java.util.Date date = Date.from( someInstant ) ;
Example code
FYI, EEST is not a time zone. Such 2-4 letter pseudo-zones indicate whether Daylight Saving Time (DST) is in effect, and hint at possible time zones. These should be used only for presentation to the user, never in your business logic, data storage, nor data exchange.
Real time zones are named in format of Continent/Region such as Africa/Casablanca and Asia/Tokyo.
The pseudo-zone EEST implies many different time zones. In this example code I use the real time zone "Europe/Bucharest". I am guessing that is your zone, given your user profile.
First we need to recreate your moment reported by Date#toString as ‘Sun Mar 27 17:32:01 EEST 2022’.
// Recreate original conditions.
LocalDate ld = LocalDate.of( 2022 , Month.MARCH , 27 ); // Sun Mar 27 17:32:01 EEST 2022
LocalTime lt = LocalTime.of( 17 , 32 , 1 );
ZoneId z = ZoneId.of( "Europe/Bucharest" );
TimeZone.setDefault( TimeZone.getTimeZone( z ) );
ZonedDateTime zdtStarting = ZonedDateTime.of( ld , lt , z );
Instant then = zdtStarting.toInstant();
java.util.Date startingPoint = Date.from( then );
Convert from legacy class to modern.
Instant instant = startingPoint.toInstant();
Add your desired 240 hours. Adjust into a time zone to obtain a ZonedDateTime, so we can better see its true meaning.
Duration duration = Duration.parse( "PT240H" );
Instant later = instant.plus( duration );
Date endingPoint = Date.from( later );
ZonedDateTime zdtLater = later.atZone( z );
Dump to console.
System.out.println( "-------| Start |--------------------" );
System.out.println( "zdtStarting = " + zdtStarting );
System.out.println( "startingPoint = " + startingPoint );
System.out.println( "instant = " + instant );
System.out.println( "-------| End |--------------------" );
System.out.println( "later = " + later );
System.out.println( "endingPoint = " + endingPoint );
System.out.println( "zdtLater = " + zdtLater );
When run.
-------| Start |--------------------
zdtStarting = 2022-03-27T17:32:01+03:00[Europe/Bucharest]
startingPoint = Sun Mar 27 17:32:01 EEST 2022
instant = 2022-03-27T14:32:01Z
-------| End |--------------------
later = 2022-04-06T14:32:01Z
endingPoint = Wed Apr 06 17:32:01 EEST 2022
zdtLater = 2022-04-06T17:32:01+03:00[Europe/Bucharest]
This question already has answers here:
Timezone conversion
(13 answers)
Closed 1 year ago.
I'm trying to convert the hour 8:00am EST (America/New_York), to display in whatever time that would be on the users system default zone. The date is not needed, only the time. I have been searching and I can only find ways to convert the current time.
You need to use ZonedDateTime and display only time part using DateTimeFormatter. Something like this:
//assuming the server is in US
ZoneId serverZone = ZoneId.of("US/Eastern");
ZoneId userZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime nyTime = ZonedDateTime.now(serverZone);
ZonedDateTime userTime = nyTime.withZoneSameInstant(userZone);
String pattern = "hh:mm a z VV";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println("Server time: " + dtf.format(nyTime));
System.out.println("User time: " + dtf.format(userTime));
Output:
Server time: 08:29 AM EDT US/Eastern
User time: 09:29 PM JST Asia/Tokyo
For the sake of the example I am assuming that you want to convert today at 8 AM EDT to the user’s default time zone. Without a date we cannot perform the conversion correctly. Like onkar ruikar in the other answer I am using and recommending java.time, the modern Java date and time API, for all of your time work.
ZoneId sourceZone = ZoneId.of("America/New_York");
ZoneId targetZone = ZoneId.systemDefault();
LocalTime sourceTime = LocalTime.of(8, 0);
ZonedDateTime sourceDateTime = ZonedDateTime.now(sourceZone).with(sourceTime);
ZonedDateTime targetDateTime = sourceDateTime.withZoneSameInstant(targetZone);
LocalTime targetTime = targetDateTime.toLocalTime();
System.out.println(targetTime);
Output when I ran today in America/Sao_Paulo time zone:
09:00
At this time of year (September), North American Eastern Time is at UTC offset -04:00 in most places including New York, and São Paulo is at -03:00, so 1 hour has been added to the time.
If instead I run the code in December, the output will be:
11:00
By then New York will be at offset -05:00 and São Paulo at -02:00 due to summer time, so 3 hours need to be added.
Link
Oracle tutorial: Date Time explaining how to use java.time.
I just wanted to get offset(+02:00) from TimeZone but seems like it is not working for IST, JST, EST, and BET.
TimeZone exchangeTimeZone = banker.getTimeZone();
String timeZone = ZonedDateTime.now(ZoneId.of(exchangeTimeZone.getID())).getOffset().getId();
It is returning error "Unknown time-zone ID: EST". Date object is not avilable with me.
Use ZoneId.SHORT_IDS
ZonedDateTime.now(ZoneId.of(exchangeTimeZone.getID(), ZoneId.SHORT_IDS))
.getOffset().getId();
TimeZone.toZoneId()
// Never do this in your code: Get a TimeZone with ID EST for demonstration only.
TimeZone tz = TimeZone.getTimeZone("EST");
String currentOffsetString = ZonedDateTime.now(tz.toZoneId())
.getOffset()
.getId();
System.out.println(currentOffsetString);
Output when running just now:
-05:00
Contrary to the one-arg ZoneId.of method that you used in your code, TimeZone.toZoneId() does handle the deprecated three letter abbreviations (which may be considered an advantage or a disadvantage depending on your situation and your taste). So the above code works with many such three letter abbreviations too, including EST.
I am only hesitatingly including the first code line above. There are several things wrong with it: We should not create old-fashioned TimeZone objects in our code but rely on the modern ZonedId and related classes from java.time, the modern Java date and time API. We should not rely on three letter time zone abbreviations either. They are deprecated, not standardized and typically ambiguous. EST, for example, may mean Australian Eastern Standard Time or North American Eastern Standard Time. To increase confusion some will expect you to get Eastern Time with summer time (DST) in summer. With TimeZone you don’t. You get a time zone that uses standard time all year. The same is not true for AST, PST nor CST.
However you often cannot control what you get from an API. And if you’re unlucky enough to get an old-fashioned TimeZone object with ID EST, the above code shows you the conversion you need to get into the realm of java.time.
You could try to find a mapping for an abbreviation you got:
public static ZoneId getFromAbbreviation(String abbreviation) {
return ZoneId.of(ZoneId.SHORT_IDS.get(abbreviation));
}
You could get the offsets like in this main:
public static void main(String[] args) {
ZoneId istEquivalent = getFromAbbreviation("IST");
ZoneId estEquivalent = getFromAbbreviation("EST");
ZoneId jstEquivalent = getFromAbbreviation("JST");
ZoneId betEquivalent = getFromAbbreviation("BET");
ZonedDateTime istNow = ZonedDateTime.now(istEquivalent);
ZonedDateTime estNow = ZonedDateTime.now(estEquivalent);
ZonedDateTime jstNow = ZonedDateTime.now(jstEquivalent);
ZonedDateTime betNow = ZonedDateTime.now(betEquivalent);
System.out.println("IST --> " + istEquivalent + " with offset " + istNow.getOffset());
System.out.println("EST --> " + estEquivalent + " with offset " + estNow.getOffset());
System.out.println("JST --> " + jstEquivalent + " with offset " + jstNow.getOffset());
System.out.println("BET --> " + betEquivalent + " with offset " + betNow.getOffset());
}
the output is
IST --> Asia/Kolkata with offset +05:30
EST --> -05:00 with offset -05:00
JST --> Asia/Tokyo with offset +09:00
BET --> America/Sao_Paulo with offset -03:00
As you can see, EST simply doesn't have a zone name, just an offset.
I want to convert a String 24 May 2020 07:40 AM to date format Mon May 24 07:40:55 IST 2020. I tried using Calendar and SimpleDateFormatter but didn't find a solution. Any help is appreciated.
I want the return type to be Date since I have to compare it with a couple of Dates.
java.time
When you’ve got some Date objects — likely from a legacy API that you cannot afford to upgrade to java.time just now — I still recommend that you use java.time, the modern Java date and time API, for your comparisons.
In the following example I am using Instant from java.time, but you may use ZonedDateTime or some other modern type too.
DateTimeFormatter fromFormatter = DateTimeFormatter.ofPattern("d MMM uuuu hh:mm a", Locale.ENGLISH);
Date anOldfashionedDate = new Date(1_590_286_000_000L);
Date anotherOldfashionedDate = new Date(1_590_287_000_000L);
System.out.println("The Date objects are " + anOldfashionedDate + " and " + anotherOldfashionedDate);
String aString = "24 May 2020 07:40 AM";
Instant instantFromDate = anOldfashionedDate.toInstant();
Instant instantFromAnotherDate = anotherOldfashionedDate.toInstant();
Instant instantFromString = LocalDateTime.parse(aString, fromFormatter)
.atZone(ZoneId.of("Asia/Kolkata"))
.toInstant();
System.out.println("Comparing " + instantFromDate + " and " + instantFromString + ": "
+ instantFromDate.compareTo(instantFromString));
System.out.println("Comparing " + instantFromAnotherDate + " and " + instantFromString + ": "
+ instantFromAnotherDate.compareTo(instantFromString));
Output is (when running in Asia/Kolkata time zone):
The Date objects are Sun May 24 07:36:40 IST 2020 and Sun May 24 07:53:20 IST 2020
Comparing 2020-05-24T02:06:40Z and 2020-05-24T02:10:00Z: -1
Comparing 2020-05-24T02:23:20Z and 2020-05-24T02:10:00Z: 1
An Instant prints in UTC; this is what its toString method generates. The trailing Z means UTC. Since India Standard Time is 5 hours 30 minutes ahead of UTC, 07:40 AM in India is the same time as 02:10 in UTC.
Given that you embark on using java.time now, you are well prepared when one day your legacy API gets upgraded to using java.time too.
The opposite conversion
If you do insist on using Date, to answer your question as asked, the opposite conversion is easy too:
Date oldfashionedDateFromInstantFromString = Date.from(instantFromString);
System.out.println("Converting to old-fashioned: " + oldfashionedDateFromInstantFromString);
Converting to old-fashioned: Sun May 24 07:40:00 IST 2020
Link
Oracle tutorial: Date Time explaining how to use java.time.
I have noticed strange behavior of date and time in java. I have the following code:
public class TestDateTime {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Helsinki"));
Calendar calendar = GregorianCalendar.getInstance();
assert(calendar.getTimeZone().equals(TimeZone.getDefault()));
//Set 1899-12-30T23:00:00
calendar.set(1899,11,30,23,0,0);
calendar.set(Calendar.MILLISECOND,0);
long timeInMillis = calendar.getTimeInMillis();
java.util.Date calendarDateTime = new java.util.Date(timeInMillis);
LocalDateTime localDateTime = LocalDateTime.ofInstant(ofEpochMilli(timeInMillis), ZoneId.systemDefault());
System.out.println("Time in millis: " + timeInMillis);
System.out.println("Date: " + calendarDateTime.toString());
System.out.println("Local DateTime: " + localDateTime.toString());
}
}
The output is:
Time in millis: -2209086000000
Date: Sat Dec 30 23:00:00 EET 1899
Local DateTime: 1899-12-30T22:39:49
timeInMillis must contain the number of milliseconds passed from 1970-01-01T00:00:00Z.
The instance of Date class stores number of milliseconds passed from 1970-01-01T00:00:00Z.
Date.toString() method returns local date and time for the default timezone.
So the Date.toString() and LocalDateTime.toString() must return the same date and time, but we see the difference (more than 20 minutes).
Is this a bug of java, or I use date and time incorrectly in Java?
This is a weirdness caused by Finland time change, see Clock Changes in Helsinki, Finland (Helsingfors) in 1921:
May 1, 1921 - Time Zone Change (HMT → EET)
When local standard time was about to reach
Sunday, May 1, 1921, 12:00:00 midnight clocks were turned forward 0:20:11 hours to
Sunday, May 1, 1921, 12:20:11 am local standard time instead
Those 20 minutes 11 seconds seem to be what you're observing.
As Jim Garrison said in his answer, LocalDateTime is correctly handling that, while Calendar is not.
In reality, it seems that the old TimeZone is getting the offset wrong, while the new ZoneId is getting it right, as can be seen in the following test code:
public static void main(String[] args) {
compare(1800, 1, 1, 0, 0, 0);
compare(1899,12,31, 23,59,59);
compare(1900, 1, 1, 0, 0, 0);
compare(1900,12,30, 23, 0, 0);
compare(1921, 4,30, 0, 0, 0);
compare(1921, 5, 1, 0, 0, 0);
compare(1921, 5, 2, 0, 0, 0);
}
private static void compare(int year, int month, int day, int hour, int minute, int second) {
Calendar calendar = new GregorianCalendar();
calendar.clear();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
calendar.set(year, month-1, day, hour, minute, second);
Date date = calendar.getTime();
ZonedDateTime zdt = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.of("Europe/Helsinki"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z XXX");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z XXX");
System.out.printf("%04d-%02d-%02d %02d:%02d:%02d %s = %d %s = %d %d%n",
year, month, day, hour, minute, second,
sdf.format(date), date.getTime(),
dtf.format(zdt), zdt.toInstant().toEpochMilli(),
date.getTime() - zdt.toInstant().toEpochMilli());
}
Output
1800-01-01 00:00:00 1800-01-01 00:00:00 EET +02:00 = -5364669600000 1800-01-01 00:00:00 EET +01:39 = -5364668389000 -1211000
1899-12-31 23:59:59 1899-12-31 23:59:59 EET +02:00 = -2208996001000 1899-12-31 23:59:59 EET +01:39 = -2208994790000 -1211000
1900-01-01 00:00:00 1900-01-01 00:00:00 EET +02:00 = -2208996000000 1900-01-01 00:00:00 EET +01:39 = -2208994789000 -1211000
1900-12-30 23:00:00 1900-12-30 23:00:00 EET +01:39 = -2177548789000 1900-12-30 23:00:00 EET +01:39 = -2177548789000 0
1921-04-30 00:00:00 1921-04-30 00:00:00 EET +01:39 = -1536025189000 1921-04-30 00:00:00 EET +01:39 = -1536025189000 0
1921-05-01 00:00:00 1921-05-01 00:20:11 EET +02:00 = -1535938789000 1921-05-01 00:20:11 EET +02:00 = -1535938789000 0
1921-05-02 00:00:00 1921-05-02 00:00:00 EET +02:00 = -1535853600000 1921-05-02 00:00:00 EET +02:00 = -1535853600000 0
LocalDateTime is CORRECT. According to the TZ database, the GMT offset at that date was 1:39:49:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31
1:39:49 - HMT 1921 May # Helsinki Mean Time
2:00 Finland EE%sT 1983
2:00 EU EE%sT
Historical timezones are incredibly complex, and prior to standardization offsets were inherited from settings based on things like mean solar noon. When going back that far just about any offset is possible, and the IANA TZ database is the master reference for historical data.
From what I can see in the database, the weird offset did not get standardized to 2:00:00 until 1921 when HMT was replaced with EE(S)T.
As others pointed out, the difference is because the LMT (local mean time) value is not being taken into account by the Date object. This has been discussed before here, with regard to Joda-Time - the precursor to Java 8's time package.
Additionally, the Joda-Time FAQ says the following:
Why is the offset for a time-zone different to the JDK?
There are two main reasons for this.
The first reason is that both the JDK and Joda-Time have time-zone
data files. It is important to keep both up to date and in sync if you
want to compare the offset between the two.
The second reason affects date-times before the modern time-zone
system was introduced. The time-zone data is obtained from the
time-zone database. The database contains information on "Local Mean
Time" (LMT) which is the local time that would have been observed at
the location following the Sun's movements.
Joda-Time uses the LMT information for all times prior to the first
time-zone offset being chosen in a location. By contrast, the JDK
ignores the LMT information. As such, the time-zone offset returned by
the JDK and Joda-Time are different for date-times before the modern
time-zone system.
The last part (which I bolded) is relavent to both Joda-Time and Java 8, even though Java 8 has one set of time zone data files (unlike Joda-Time).
To be more precise about API-inconsistency:
While the new java.time-API always uses the LMT-informations of TZDB, we have also to state that the old JDK-class java.util.TimeZone makes a cut in year 1900 with the consequence that LMT-informations are not taken into account before the year 1900, but after 1900, yes, it is still taken into account! Just make your experiments with an appropriate zone... (Asia/Kamchatka for example)
We cannot really say that either the LMT-strategy of java.time-API is correct or the traditional 1900-strategy. Keep also in mind that there is an open JDK-issue to abolish the LMT-strategy. Citation:
The current TimeZone code does not use LMT. Joda-Time does, as does
JSR-310. This is wrong.
Recent discussion on the tzdb mailing list has indicated that the data
is not properly maintained or reliably linked to the city of the zone
ID. It is also relatively meaningless, being a notional value for a
single city within a large region.
Removing LMT is a good thing.
And Xueming Shen from Oracle says as comment in this issue:
The current j.u.TimeZone implementation DOES use LMT. If the LMT is
defined/used cross the 1900.1.1 j.u.TimeZone cutoff date (by the tzdb
data). For example the offset for Asia/Kamchatka from 1900.1.1 to the
1922.11.10 will be the LMT 10.34.36. Yes, if the LMT end date is before 1900.1.1, the LMT will not be used by the j.u.TZ.
As additional historical note, the JDK-issue was originally suggested by the main author of java.time-API S. Colebourne, see also the ancestor on threeten-issue-tracker.