>我有两个日期 - 发货日期和交货日期。商业案例是 - 发货日期不能晚于交货日期。
我的一个输入日期由三个独立的String
部分组成 - 日期(例如 05.07.2021)、时间(例如 00:00:00)、时区(例如 CST)。
我尝试将此输入日期转换为Calendar
实例,然后使用Calender.after()
.但看起来它在比较时没有考虑时区。
输入:
ShipDate - 05.07.2021, 01:00:00, EST
DelDate - 05.07.2021, 00:00:00, CST
预期:
虽然时间不同,但考虑到时区转换,发货和交货日期应该相同,通过商业案例。
请帮助实现预期。
法典:
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");
}
}
您可以使用Java 8中引入的datetime API,即java.time
。
您可以使用一个ZonedDateTime
来实现预期,这里有一个小例子:
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");
}
此示例的输出为
2021-07-05T00:00-05:00[America/Chicago] and 2021-07-05T01:00-04:00[America/New_York] are the same moment in time
请注意,您可以通过单独处理输入的每个部分来解析或创建ZonedDateTime
组成的部分。
但如果你这样做...
。它不会在每种情况下产生相同的结果,因为ZoneId.of(delTz, ZoneId.SHORT_IDS)
似乎将"EST"
解释为 UTC 全年的-05:00
小时,而DateTimeFormatter
似乎使用考虑夏令时的真实区域。
感谢 Ole V. V. 在此答案下方的评论中指出与上述示例的这种差异。
下面是将三个字母的时区缩写解析为固定偏移量的示例:
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");
}
你的代码出了什么问题?
这是好奇者的答案。 deHaar早就发布了很好的答案,展示了如何解决您的问题。当你试图使用设计不佳且早已过时的SimpleDateFormat
、Calendar
和TimeZone
类时,我们真的不需要知道出了什么问题,因为我们根本不应该使用这些类。然而,在这种情况下,好奇心不会完全杀死猫,所以让我们看看。
您的两个Calendar
对象之间的比较按照我认为您预期的方式工作:它是比较时间点以查看一个是否在另一个之后。换句话说,它不是比较不同时区的挂钟时间。
但在你开始比较之前,还有两件事出了问题。
第一件事是,您的两个三个字母的时区缩写 CST 和 EST 为您提供相同的 UTC 偏移量,即 -05:00。什么?!如何?!这是因为不一致的CST被理解为表示美国/芝加哥时区,即考虑夏令时(DST),而EST的字面意思是全年的(北美)东部标准时间,即没有夏令时。因此,虽然芝加哥在标准时间的偏移量为 -06:00,但您的日期 7 月 5 日正好是一年中的夏令时,因此应用偏移量 -05:00。东部标准时间全年的偏移量为 -05:00,因此此处应用了相同的偏移量。
要吸取的教训:不要使用三个字母的时区缩写。使用实时时区 ID,如美国/温尼伯、美国/芝加哥、美国/多伦多或美洲/New_York,始终使用区域/城市。
第二件事在这里严重错误:
delCal.setTime(sdf.parse(delDate + " " + delTime));
您原本期望这会将芝加哥时区中已有Calendar
设置为芝加哥时区的 7 月 5 日 00:00。这(可能)没有发生。sdf.parse()
返回一个Date
对象。尽管有这个名字,但Date
既没有日期也没有一天中的一个小时。它有一个时间点。而您的SimpleDateFormat
不知道您考虑了中部时间,会将连接的字符串解析为对应于JVM 默认时区中的7 月 5 日 00:00 的时间点。在我的时区(欧洲/哥本哈根),我在芝加哥得到了4 7月17:00。您现在可以猜到,以下行随后解析为对应于同一 JVM 默认时区中夜间 01:00 的时间点,因此 1 小时后(除非今晚您所在时区的时间发生了非常特殊的情况)。
要吸取的教训:不要使用SimpleDateFormat
,Calendar
和TimeZone
。对于这些旧类,很容易犯错误,这些错误很难查看和追踪。