java.text.ParseException:无法解析的日期:“09:07:31 AM PDT”

java.text.ParseException: Unparseable date: "09:07:31 AM PDT"

我在响应中有一个字段,我想在添加几分钟之前对其进行解析。我写了以下导致错误的代码。

SimpleDateFormat df = new SimpleDateFormat("HH:MM:ss a SSS");
        Date date = df.parse(currentTime);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MINUTE, 10);

此处,当前时间为“09:07:31 AM PDT”

您需要在格式中使用正确的字母组合,此处:

SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss aa zzz");

您可能还想查看 API 文档 [1]。

注意:如果您使用上述格式,解析将有效,但您还需要日期部分才能准确计算。这是一个示例,您的机器当前时区的当前日期用作日期部分。

        String recievedTime = "09:07:31 AM PDT";
        String currentDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss aa zzz");
        Date date = df.parse(currentDate +" " + recievedTime);

[1] https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html

tl;博士

LocalTime                                        // Represent a time-of-day without a date and without a time zone or offset-from-UTC.
.parse(               
    "09:07:31 AM PDT".substring( 0 , 11 ) ,      // Remove the senseless `PDT` from the input.
    DateTimeFormatter.ofPattern( "hh:mm:ss a" )  // Define a formatting pattern to match our modified input string.
)                                                // Returns a `LocalTime` object.
.toString()                                      // Generates text in standard ISO 8601 format to represent the value of our `LocalTime` object.

09:07:31

避免 Date & Calendar

您使用的 date-time classes 与 Java 的最早版本捆绑在一起。这些在几年前被 JSR 310 中定义的现代 java.time classes 所取代。

切勿使用 DateCalendarSimpleDateFormat 等。

Dateclass不适合

java.util.Date class 代表一个时刻,一个 time-of-day 的日期,如 UTC 所示。您的输入缺少日期。所以你的输入不能用这个 class.

来表示

Time-of-day 带区域没有意义

您输入的 time-of-day 是一个错误的时区。 PDT 可能代表“太平洋夏令时”,表示 Daylight Saving Time (DST) 是否生效。这不是真正的时区名称。相反,应使用时区名称,例如 America/Los_Angeles

无论如何,time-of-day 与时区的组合毫无意义。没有日期的上下文,时区就没有意义。

LocalTime

我建议您提取 time-of-day 并忽略 PDT。取前11个字符。

String input = "09:07:31 AM PDT";
String s = input.substring( 0 , 11 ); // Uses annoying zero-based index counting. So asking for first through the eleventh characters requires ( 0 , 11 ).

定义格式模式以匹配我们修改后的输入字符串。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "hh:mm:ss a" );

解析为 LocalTime、没有日期和时区的 time-of-day 或 offset-from-UTC。

LocalTime lt = LocalTime.parse( s , f );

lt.toString(): 09:07:31

ZonedDateTime

为了好玩,让我们为您的 time-of-day 应用一个时区,并为 ZonedDateTime 应用一个日期。

LocalTime lt = LocalTime.parse( "01:59:00 AM PDT".substring( 0 , 11 ) , DateTimeFormatter.ofPattern( "hh:mm:ss a" ) );
LocalDate ld = LocalDate.of( 2020 , Month.MARCH , 7 );
ZoneId z = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
ZonedDateTime zdtLater = zdt.plusMinutes( 5 );
System.out.println( "zdtLater = " + zdtLater );

我们得到 2:04 上午。

zdtLater = 2020-03-07T02:04-08:00[America/Los_Angeles]

将该日期更改为 8 号。

LocalTime lt = LocalTime.parse( "01:59:00 AM PDT".substring( 0 , 11 ) , DateTimeFormatter.ofPattern( "hh:mm:ss a" ) );
LocalDate ld = LocalDate.of( 2020 , Month.MARCH , 8 );
ZoneId z = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
ZonedDateTime zdtLater = zdt.plusMinutes( 5 );
System.out.println( "zdtLater = " + zdtLater );

我们得到 3:04 上午,而不是 2:04 上午。

zdtLater = 2020-03-08T03:04-07:00[America/Los_Angeles]

日期是 time-of-day 在时区中的含义。

偏移量随时间变化。这就是时区的意义。 America/Los_Angeles这样的时区是北美西海岸很多地区的人们使用的offset的过去、现在和未来变化的历史。 2020 年 3 月 7 日,偏移量比 UTC 晚八小时。 2020 年 3 月 8 日,偏移量更改为比 UTC 晚 7 小时。凌晨 2 点的第一刻,时钟跳了一个小时到凌晨 3 点。 8 日凌晨 2 点从未像 7 日那样出现。

OffsetTime

java.time 框架确实提供了 OffsetTime class。这表示 time-of-day 和 offset-from-UTC.

这个 class 没有意义,原因与上面讨论的相同。带有偏移量但没有日期的 time-of-day 没有任何用处。我认为这被添加到 java.time 只是为了匹配 SQL-standard 类型 TIME WITH TIME ZONE,同时 LocalTime 匹配 TIME WITHOUT TIME ZONE.但是这个 TIME WITH TIME ZONE 类型在 SQL 中没有意义。这个问题是其他人注意到的,而不仅仅是我的意见。这不是 SQL 标准中唯一没有意义的事情。

此外,您的输入带有时区(或者至少时区是 PDT 的 mis-use 指定的),而不是偏移量。偏移量是 hour-minutes-seconds 的数字,仅此而已。如上所述,时区是偏移量变化的历史。