我有两个日期 - 发货日期和交货日期。业务案例是 - 发货日期不能晚于交货日期。
我的一个输入日期由三个独立的 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");
}
}
答案 0 :(得分:3)
您可以使用 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");
}
答案 1 :(得分:2)
这是好奇者的答案。 deHaar 早就发布了很好的答案,展示了如何解决您的问题。当您尝试使用设计不佳且已过时的 SimpleDateFormat
、Calendar
和 TimeZone
类时,我们实际上并不需要知道出了什么问题,因为我们根本不应该使用这些类.但是,在这种情况下,好奇心并不会完全杀死猫,所以让我们看看。
您的两个 Calendar
对象之间的比较按照我认为您预期的方式进行:比较时间点以查看一个对象是否一个接一个。换句话说,它不是比较来自不同时区的挂钟时间。
但在开始进行比较之前,还有两件事出了问题。
第一件事是,您的两个三字母时区缩写 CST 和 EST 为您提供相同的 UTC 偏移量,即 -05:00。什么?!如何?!这是因为不一致的 CST 被用来表示美国/芝加哥时区,即考虑夏令时 (DST),而 EST 从字面上看是指(北美)东部标准时间所有年,也就是说,没有夏令时。因此,虽然芝加哥在标准时间的偏移量为 -06:00,但您的日期 7 月 5 日在一年中的夏季时间部分,因此应用偏移量 -05:00。东部标准时间全年偏移 -05:00,因此此处应用相同的偏移。
学习经验:不要使用三个字母的时区缩写。使用实时时区 ID,例如 America/Winnipeg、America/Chicago、America/Toronto 或 America/New_York,始终地区/城市。
这里的第二件事严重错误:
delCal.setTime(sdf.parse(delDate + " " + delTime));
您原以为这会将已经位于芝加哥时区的 Calendar
设置为芝加哥时区的 7 月 5 日 00:00。这(可能)没有发生。 sdf.parse()
返回一个 Date
对象。尽管名称为 Date
,但它既不包含日期也不包含一天中的一个小时。它持有一个时间点。并且您的 SimpleDateFormat
不知道您想到的是中央时间,将您的串联字符串解析为一个时间点,该时间点对应于 JVM 的默认时区 中的 7 月 5 日 00:00。在我的时区(欧洲/哥本哈根),我于 7 月 4 日 17:00 在芝加哥。您现在可能已经猜到了,下面这行代码会解析为同一 JVM 默认时区的晚上 01:00 对应的时间点,所以 1 小时后(除非您所在的时间发生了非常特殊的事情)在今晚的区域)。
学习经验:不要使用 SimpleDateFormat
、Calendar
和 TimeZone
。使用那些旧类很容易犯错误,而这些错误很难看透和追查。