问题描述
final String pattern = "dd-MMM-yy hh.mm.ss.SSS000 a";
final SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
final String formatted = dateFormat.format(new Date());
final Date date = dateFormat.parse(formatted); // throws ParseException
errorIndex
是 25,它是最后一个零和“AM/PM”短语之间的空格。
Exception in thread "main" java.text.ParseException: Unparseable date: "09-Jun-21 04.40.45.898000 PM"
at java.text.DateFormat.parse(DateFormat.java:366)
但是当我改变如下模式时(这不适合我的情况),一切正常。
final String pattern = "dd-MMM-yy hh.mm.ss.SSSSSS a";
谁能告诉我这是怎么回事?
解决方法
我认为这是解析器的问题。它不一定按字面意思使用模式中的字符数。所以它会尝试将 898000 解析为毫秒。
SimpleDateFormat 反正已经过时了,也许你可以试试 java.time 库?
LocalDateTime date = LocalDateTime.ofInstant((new Date()).toInstant(),ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy hh.mm.ss.SSS'000' a");
String text = date.format(formatter);
Date parsedDate = Date.from(LocalDateTime.parse(text,formatter).atZone(ZoneId.systemDefault()).toInstant());
如果您也可以使用 LocalDateTime,则可以跳过与 Date 的转换:
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy hh.mm.ss.SSS'000' a");
String text = date.format(formatter);
LocalDateTime parsedDate = LocalDateTime.parse(text,formatter);
,
罪魁祸首是格式字符串中的 SSS
。打印时,表示要打印至少 3 位毫秒数。但是在解析的时候,它只是表示你要解析毫秒。来自the documentation:
对于格式化,模式字母的数量是最少的 数字,较短的数字用零填充到这个数量。为了 解析,除非需要,否则忽略模式字母的数量 分隔两个相邻的字段。
即解析时,SSS
模式将整个898000
解析为毫秒,之后再找不到零点。
java.time
java.util
日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*,它于 2014 年 3 月作为 Java SE 8 标准库的一部分发布。
使用 java.time
(现代日期时间 API)的解决方案:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "09-Jun-21 04.40.45.898000 PM";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d-MMM-uu h.m.s.SSSSSS a",Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(strDateTime,dtf);
System.out.println(ldt);
}
}
输出:
0021-06-09T16:40:45.898
在这里,您可以使用 y
代替 u
,而是使用 I prefer u
to y
。
从 Trail: Date Time 了解有关现代 Date-Time API 的更多信息。
使用旧 API 的解决方案:
SimpleDateFormat does not handle fraction-of-second beyond three digits in the millisecond part correctly。您需要调整日期时间字符串以符合此限制,即您需要从毫秒部分中删除数字以使其最多保持三位数。一种简单的方法是使用 Regex 替换。
演示:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Main {
public static void main(String[] args) throws ParseException {
String strDateTime = "09-Jun-21 04.40.45.898000 PM";
strDateTime = strDateTime.replaceAll("(.*(?<=\\.)\\d{3})(\\d+)(\\s[AP]M)","$1$3");
SimpleDateFormat sdf = new SimpleDateFormat("d-MMM-y h.m.s.SSS a",Locale.ENGLISH);
Date date = sdf.parse(strDateTime);
System.out.println(date);
}
}
在我的时区输出:
Wed Jun 09 16:40:45 BST 2021
* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。