英文:
java.text.ParseException: Unparseable date: "09:07:31 AM PDT"
问题
我有一个在响应中的字段,它以时间形式呈现,我想在添加一些分钟之前对其进行解析。我已经编写了下面的代码,但结果出现错误。
SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss a zzz");
Date date = df.parse(currentTime);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MINUTE, 10);
在这里,currentTime
的值是 "09:07:31 AM PDT"。
英文:
I have a field in the response that is coming as time which I want to parse before adding some minutes into it. I have written the below code which results in error.
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);
here, currentTime is coming in as "09:07:31 AM PDT"
答案1
得分: 3
以下是代码的翻译部分:
# tl;dr
LocalTime // 表示一天中的时间,没有日期和时区或UTC偏移。
.parse(
"09:07:31 AM PDT".substring(0, 11), // 从输入中去掉无意义的“PDT”。
DateTimeFormatter.ofPattern("hh:mm:ss a") // 定义一个格式化模式以匹配我们修改后的输入字符串。
) // 返回一个`LocalTime`对象。
.toString() // 生成标准的ISO 8601格式的文本,表示我们的`LocalTime`对象的值。
> 09:07:31
# 避免使用 `Date` 和 `Calendar`
您正在使用最早版本的Java捆绑的可怕日期时间类。这些类早在多年前就被现代的*java.time*类(JSR 310定义)所取代了。
永远不要使用`Date`、`Calendar`、`SimpleDateFormat`等类。
# `Date` 类不适用
`java.util.Date` 类表示一个时刻,即UTC时间下的日期和时间。您的输入缺少日期。因此,您的输入不能由此类表示。
# 具有时区的时间没有意义
您的输入表示一个带有虚假时区的时间。`PDT` 可能代表“太平洋夏令时间”,用于指示是否正在使用夏令时(DST)。这不是一个真正的时区名称。应该使用类似 `America/Los_Angeles` 这样的时区名称。
无论如何,时间和时区的组合没有意义。没有日期的上下文,时区没有意义。
# `LocalTime`
我建议您提取时间并忽略 `PDT`。取前11个字符。
String input = "09:07:31 AM PDT";
String s = input.substring(0, 11); // 使用令人讨厌的从零开始的索引计数。因此,要求前11个字符需要 (0, 11)。
定义一个格式化模式以匹配我们修改后的输入字符串。
DateTimeFormatter f = DateTimeFormatter.ofPattern("hh:mm:ss a");
解析为 `LocalTime`,表示没有日期和没有时区或UTC偏移的时间。
LocalTime lt = LocalTime.parse(s, f);
> lt.toString(): 09:07:31
# `ZonedDateTime`
为了好玩,让我们为您的时间应用一个时区,以及一个日期,以获取一个 `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 AM。
> 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 AM,而不是 2:04 AM。
> zdtLater = 2020-03-08T03:04-07:00[America/Los_Angeles]
日期赋予了时区中的时间意义。
偏移随时间变化。这就是时区的含义。时区如 `America/Los_Angeles` 是过去、现在和未来在北美西海岸许多地区使用的偏移变化的历史。在2020年3月7日,偏移为UTC的8小时。在2020年3月8日,偏移更改为UTC的7小时。在凌晨2点的第一刻,时钟跳跃了一个小时到凌晨3点。在8号,与7号不同,凌晨2点的那个小时没有发生。
# `OffsetTime`
*java.time* 框架确实提供了 [`OffsetTime`][2] 类。这表示带有UTC偏移的时间。
出于上述讨论的相同原因,这个类没有意义。带有偏移但没有日期的时间没有任何有用的目的。我假设这只是为了匹配SQL标准类型 `TIME WITH TIME ZONE` 而添加到 *java.time* 中的,与 `LocalTime` 匹配 `TIME WITHOUT TIME ZONE`。但是在SQL中,这个 `TIME WITH TIME ZONE` 类型没有意义。这个问题不仅是我的观点,其他人也指出了这一点。这不是SQL标准中唯一没有意义的东西。
此外,您的输入带有一个时区(或至少是通过错误使用 `PDT` 意图带有时区),而不是偏移。偏移是一定数量的小时-分钟-秒,仅此而已。时区是偏移变化的历史,正如上面讨论的那样。
[1]: https://en.wikipedia.org/wiki/Daylight_saving_time
[2]: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/OffsetTime.html
英文:
tl;dr
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
Avoid Date
& Calendar
You are using terrible date-time classes that were bundled with the earliest versions of Java. These were supplanted years ago by the modern java.time classes defined in JSR 310.
Never use Date
, Calendar
, SimpleDateFormat
, and such.
Date
class does not fit
The java.util.Date
class represents a moment, a date with time-of-day, as seen in UTC. Your input lacks a date. So your input cannot be represented by this class.
Time-of-day with zone makes no sense
Your input represents a time-of-day with what is a false time zone. The PDT
likely stands for "Pacific Daylight Saving Time", to indicate whether Daylight Saving Time (DST) is in effect or not. This is not a true time zone name. Instead a time zone name such as America/Los_Angeles
should be used.
At any rate, the combination of a time-of-day with a time zone makes no sense. Without the context of a date, the time zone carries no meaning.
LocalTime
I suggest you extract the time-of-day and ignore the PDT
. Take the first 11 characters.
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 ).
Define a formatting pattern to match our modified input string.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "hh:mm:ss a" );
Parse as a LocalTime
, a time-of-day without a date and without a time zone or offset-from-UTC.
LocalTime lt = LocalTime.parse( s , f );
>lt.toString(): 09:07:31
ZonedDateTime
For fun, let's apply a time zone to your time-of-day, and a date to get a 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 );
We get 2:04 AM.
>zdtLater = 2020-03-07T02:04-08:00[America/Los_Angeles]
Change that date to the 8th.
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 );
We get 3:04 AM, not 2:04 AM.
>zdtLater = 2020-03-08T03:04-07:00[America/Los_Angeles]
The date is what gives meaning to that time-of-day in a time zone.
Offsets vary over time. That is the meaning of a time zone. A time zone such as America/Los_Angeles
is a history of the past, present, and future changes to the offset used by the people in many regions on the west coast of North America. On March 7, 2020 the offset was eight hours behind UTC. On March 8, 2020, the offset was changed to seven hours behind UTC. At the first moment of 2 AM, the clock jumped an hour to 3 AM. The 2 AM hour never happened on the 8th as it did on the 7th.
OffsetTime
The java.time framework does offer the OffsetTime
class. This represents a time-of-day with an offset-from-UTC.
This class makes no sense, for the same reasons discussed above. A time-of-day with an offset but no date serves no useful purpose. I presume this was added to java.time merely to match the SQL-standard type TIME WITH TIME ZONE
, in parallel with LocalTime
matching TIME WITHOUT TIME ZONE
. But this TIME WITH TIME ZONE
type makes no sense in SQL. This problem is noted by others, not merely my opinion. And this is not the only senseless thing in the SQL standard.
Furthermore, your input carries a time zone (or at least a time zone was intended by the mis-use of PDT
), not an offset. An offset is a number of hour-minutes-seconds, nothing more. A time zone is a history of changes in offset, as discussed above.
答案2
得分: 1
你需要在格式中使用正确的字母组合,如下所示:
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss aa zzz");
你可能还想查看 API 文档 1。
注意: 如果使用上述格式,解析将会正常工作,但为了准确的计算,你需要包括日期部分。以下是一个示例,使用当前时区的当前日期作为日期部分:
String receivedTime = "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 + " " + receivedTime);
1 https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html
英文:
You need to use the right combination of letters in format, here:
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss aa zzz");
You might also want to look at API docs 1.
Note: Parsing would work if you use the above format, but you will need the date part as well for accurate calculations. Here is an example with the current date of the current timezone of your machine used as the date part.
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论