Reputation: 119
I have two dates - Ship Date and Delivery Date. Business case is - Ship Date cannot be after Delivery Date.
My one input date is made up of three separate String
components - date (e.g.05.07.2021), time (e.g.00:00:00), timezone (e.g.CST).
I tried converting this input date to Calendar
instance and then used Calender.after()
. But it looks like it doesn't consider timezone while comparing.
Input:
ShipDate - 05.07.2021, 01:00:00, EST
DelDate - 05.07.2021, 00:00:00, CST
Expected:
Though the time is different, considering timezone conversion, both ship and delivery dates should be same, passing business case.
Please help achieve the expected.
Code:
public static void main(String[] args) {
String delDate = "05.07.2021";
String delTime = "00:00:00";
String delTz = "CST";
String shipDate = "05.07.2021";
String shipTime = "01:00:00";
String shipTz = "EST";
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
Calendar delCal = Calendar.getInstance(TimeZone.getTimeZone(delTz));
Calendar shipCal = Calendar.getInstance(TimeZone.getTimeZone(shipTz));
try {
delCal.setTime(sdf.parse(delDate + " " + delTime));
shipCal.setTime(sdf.parse(shipDate + " " + shipTime));
} catch (ParseException e) {
e.printStackTrace();
}
if (shipCal.after(delCal)) {
System.out.println("ERROR: Ship datetime is after Del datetime");
} else {
System.out.println("SUCCESS: Ship datetime and Del datetime are correct");
}
}
Upvotes: 0
Views: 669
Reputation: 18588
You could use the datetime API that was introduced in Java 8, that is java.time
.
There is a ZonedDateTime
which you can use in order to achieve the expected, here's a small example:
public static void main(String[] args) {
// your example input
String delDate = "05.07.2021";
String delTime = "00:00:00";
String delTz = "CST";
String shipDate = "05.07.2021";
String shipTime = "01:00:00";
String shipTz = "EST";
// concatenate the input, just comma separated
String delDateTimeZone = String.join(",", delDate, delTime, delTz);
String shipDateTimeZone = String.join(",", shipDate, shipTime, shipTz);
// define a formatter that parses Strings like the concatenated ones
DateTimeFormatter dateTimeZoneDtf = DateTimeFormatter.ofPattern(
"dd.MM.uuuu,HH:mm:ss,z");
// parse them to objects that consider time zones
ZonedDateTime delZdt = ZonedDateTime.parse(delDateTimeZone, dateTimeZoneDtf);
ZonedDateTime shipZdt = ZonedDateTime.parse(shipDateTimeZone, dateTimeZoneDtf);
// find out if their instants are equal
String e = delZdt.toInstant().equals(shipZdt.toInstant()) ?
"the same moment" : "different moments";
// and print them along with a statement about their equality
System.out.println(delZdt + " and " + shipZdt + " are " + e + " in time");
}
The output of this example is
2021-07-05T00:00-05:00[America/Chicago] and 2021-07-05T01:00-04:00[America/New_York] are the same moment in time
Please note that you can parse or create the parts a ZonedDateTime
consist of by handling every part of your input separately.
But if you do...
... it will not produce the same results in every case due to ZoneId.of(delTz, ZoneId.SHORT_IDS)
seems to be interpreting "EST"
as -05:00
hours from UTC all year while the DateTimeFormatter
appears to use a real zone that considers daylight saving times.
Thanks to Ole V. V. for pointing out this difference to the example above in a comment below this answer.
Here's the example that resolves the three-letter time zone abbreviation to a fixed offset:
public static void main(String[] args) {
// your example input
String delDate = "05.07.2021";
String delTime = "00:00:00";
String delTz = "CST";
String shipDate = "05.07.2021";
String shipTime = "01:00:00";
String shipTz = "EST";
// define a formatter that parses the date String
DateTimeFormatter dateDtf = DateTimeFormatter.ofPattern("dd.MM.uuuu");
// parse the dates using the date formatter
LocalDate delLocalDate = LocalDate.parse(delDate, dateDtf);
LocalDate shipLocalDate = LocalDate.parse(shipDate, dateDtf);
// and parse the times of day without (they are in standard ISO format already)
LocalTime delLocalTime = LocalTime.parse(delTime);
LocalTime shipLocalTime = LocalTime.parse(shipTime);
// then create zone objects from the zone short ids
ZoneId delZone = ZoneId.of(delTz, ZoneId.SHORT_IDS);
ZoneId shipZone = ZoneId.of(shipTz, ZoneId.SHORT_IDS);
// create objects that consider time zones using the
ZonedDateTime delZdt = ZonedDateTime.of(delLocalDate, delLocalTime, delZone);
ZonedDateTime shipZdt = ZonedDateTime.of(shipLocalDate, shipLocalTime, shipZone);
// find out if their instants are equal
String e = delZdt.toInstant().equals(shipZdt.toInstant()) ?
"the same moment" : "different moments";
// and print them along with a statement about their equality
System.out.println(delZdt + " and " + shipZdt + " are " + e + " in time");
}
Upvotes: 3
Reputation: 86379
This is the answer for the curious. deHaar has long posted the good answer showing how to solve your problem. We have no real need to know what went wrong when you tried to use the poorly designed and long outdated SimpleDateFormat
, Calendar
and TimeZone
classes since we should not be using those classes at all anyway. However, in this case curiosity won’t completely kill the cat, so let’s see.
Comparison between your two Calendar
objects works the way I think you had expected: it is comparing the points in time to see whether one is after the other. In other words, it is not comparing the wall-clock times from different time zones.
But before you get around to doing the comparison, two other things go wrong.
The first thing is that your two three letter time zone abbreviations, CST and EST, give you the same UTC offset, namely -05:00. What?! How?! This is because inconsistently CST is taken to mean America/Chicago time zone, that is, with summer time (DST) taken into account, whereas EST is taken literally to mean (North American) Eastern Standard Time all year, that is, with no summer time. So while Chicago is at offset -06:00 during standard time, your date, 5 July, falls in the summer time part of the year, so offset -05:00 is applied. Eastern Standard Time is at offset -05:00 all year, so the same offset is applied here.
Lesson to learn: Do not use three letter time zone abbreviations. Use real time zone IDs like America/Winnipeg, America/Chicago, America/Toronto or America/New_York, always region/city.
The second thing goes badly wrong here:
delCal.setTime(sdf.parse(delDate + " " + delTime));
You had expected this to set the Calendar
that was already in Chicago time zone to 5 July 00:00 in Chicago time zone. This (probably) did not happen. sdf.parse()
returns a Date
object. Despite the the name a Date
neither holds a date nor an hour of day in it. It holds a point in time. And your SimpleDateFormat
not knowing that you had Central Time in mind, parses your concatenated string into a point in time that corresponds to 5 July 00:00 in the default time zone of the JVM. In my time zone (Europe/Copenhagen) I got 4 July 17:00 in Chicago. You may be able to guess by now that the following line then parses into a point in time corresponding to 01:00 in the night in the same JVM default time zone, so 1 hour later (unless something very special happens to time in your time zone in this night).
Lesson to learn: Don’t use SimpleDateFormat
, Calendar
and TimeZone
. With those old classes it’s too easy to make errors that are too hard to look through and track down.
Upvotes: 2