java.text.ParseException: 日期无法解析:java.text.DateFormat.parse(DateFormat.java:579)

3

我在使用SimpleDateFormat时遇到了问题。

SimpleDateFormat dtfmt=new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.getDefault());
Date dt=dtfmt.parse(deptdt);

在Android模拟器上运行正常,但在手机上出现以下错误:

W/System.err: java.text.ParseException: 无法解析的日期:"2016年10月24日下午7:31"(偏移量为3) W/System.err: at java.text.DateFormat.parse(DateFormat.java:579)

有什么解决方法吗?

3个回答

6

永远不要使用DateTimeFormatter来自定义格式,也不要使用没有LocaleSimpleDateFormat

由于给定的日期时间是英文的,您应该在日期时间解析器中使用Locale.ENGLISH;否则,在使用非英语类型的区域设置的系统(计算机、手机等)上,解析将失败。

此外,请注意,java.util的日期时间API及其格式化API SimpleDateFormat已经过时且容易出错。建议完全停止使用它们,并切换到现代日期时间API

演示:

public class Main {
    public static void main(String[] args) {
        final String strDateTime = "24 Oct 2016 7:31 pm";
        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()             // For case-insensitive (e.g. am, Am, AM) parsing 
                .appendPattern("d MMM uuuu h:m a")  // Pattern conforming to the date-time string
                .toFormatter(Locale.ENGLISH);       // Locale
        LocalDateTime ldt = LocalDateTime.parse(strDateTime, dtf);
        System.out.println(ldt);
    }
}

输出:

2016-10-24T19:31

在线演示

默认情况下,DateTimeFormatter#ofPattern 使用JVM在启动时根据主机环境设置的默认格式区域设置SimpleDateFormat也是如此。我尝试通过以下演示来说明这个问题:

public class Main {
    public static void main(String[] args) {
        final String strDateTime = "24 Oct 2016 7:31 pm";           
        DateTimeFormatter dtfWithDefaultLocale = null;          

        System.out.println("JVM's Locale: " + Locale.getDefault());
        // Using DateTimeFormatter with the default Locale
        dtfWithDefaultLocale = getDateTimeFormatterWithDefaultLocale();
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out.println(
                "Parsed with JVM's default locale: " + LocalDateTime.parse(strDateTime, dtfWithDefaultLocale));

        // Setting the JVM's default locale to Locale.FRANCE
        Locale.setDefault(Locale.FRANCE);
        
        // Using DateTimeFormatter with Locale.ENGLISH explicitly (recommended)
        DateTimeFormatter dtfWithEnglishLocale = getDateTimeFormatterWithEnglishLocale();
        System.out.println("JVM's Locale: " + Locale.getDefault());
        System.out.println("DateTimeFormatter's Locale: " + dtfWithEnglishLocale.getLocale());
        LocalDateTime zdt = LocalDateTime.parse(strDateTime, dtfWithEnglishLocale);
        System.out.println("Parsed with Locale.ENGLISH: " + zdt);

        
        System.out.println("JVM's Locale: " + Locale.getDefault());
        // Using DateTimeFormatter with the default Locale
        dtfWithDefaultLocale = getDateTimeFormatterWithDefaultLocale();
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out.println(
                "Parsed with JVM's default locale: " + LocalDateTime.parse(strDateTime, dtfWithDefaultLocale));
    }
    
    static DateTimeFormatter getDateTimeFormatterWithDefaultLocale() {
        return new DateTimeFormatterBuilder()
                .parseCaseInsensitive()             
                .appendPattern("d MMM uuuu h:m a") 
                .toFormatter(); // Using default Locale
    }
    
    static DateTimeFormatter getDateTimeFormatterWithEnglishLocale() {
        return new DateTimeFormatterBuilder()
                .parseCaseInsensitive()             
                .appendPattern("d MMM uuuu h:m a") 
                .toFormatter(Locale.ENGLISH); // Using Locale.ENGLISH
    }
}

输出:

JVM's Locale: en_GB
DateTimeFormatter's Locale: en_GB
Parsed with JVM's default locale: 2016-10-24T19:31
JVM's Locale: fr_FR
DateTimeFormatter's Locale: en
Parsed with Locale.ENGLISH: 2016-10-24T19:31
JVM's Locale: fr_FR
DateTimeFormatter's Locale: fr_FR
Exception in thread "main" java.time.format.DateTimeParseException: Text '24 Oct 2016 7:31 pm' could not be parsed at index 3
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
    at Main.main(Main.java:34)

在线演示


以下示例仅为完整性而提供,使用SimpleDateFormat。
public class Main {
    public static void main(String[] args) throws ParseException {
        final String strDateTime = "24 Oct 2016 7:31 pm";
        SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy h:m a", Locale.ENGLISH);
        Date date = sdf.parse(strDateTime);
        System.out.println(date);
    }
}

输出:

Mon Oct 24 19:31:00 BST 2016

在线演示


2

您的 deptdt 包含看起来像是英文月份的 Oct。 但是您的 Locale.getDefault() 可能会返回非英语语言环境。 请将其替换为 Locale.ENGLISHLocale.US

SimpleDateFormat dtfmt=new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.ENGLISH);
Date dt=dtfmt.parse(deptdt);

2

这可能是因为手机的默认语言不是英语,而您输入的月份名称是 (Oct)。

解决方法是明确使用英语语言环境:

SimpleDateFormat dtfmt = new SimpleDateFormat("dd MMM yyyy hh:mm a", Locale.ENGLISH);
Date dt = dtfmt.parse("24 Oct 2016 7:31 pm");

不要直接使用SimpleDateFormat(因为这个旧API存在很多问题设计问题),您可以使用ThreeTen Backport,这是Java 8新日期/时间类的一个很好的后移。要在Android中使用它,您还需要ThreeTenABP(有关如何使用它的更多信息在这里)。

要使用的主要类是org.threeten.bp.LocalDateTime(似乎是最好的选择,因为输入中有日期和时间字段)和org.threeten.bp.format.DateTimeFormatter(用于解析输入)。我还使用java.util.Locale类来确保它以英语解析月份名称,并使用org.threeten.bp.format.DateTimeFormatterBuilder以确保它解析pm(将其设置为不区分大小写,因为默认值为PM):

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // case insensitive to parse "pm"
    .parseCaseInsensitive()
    // pattern
    .appendPattern("dd MMM yyyy h:mm a")
    // use English locale to parse month name (Oct)
    .toFormatter(Locale.ENGLISH);
// parse input
LocalDateTime dt = LocalDateTime.parse("24 Oct 2016 7:31 pm", fmt);
System.out.println(dt); // 2016-10-24T19:31

输出结果将是:

2016-10-24T19:31

如果您需要将其转换为java.util.Date,可以使用org.threeten.bp.DateTimeUtils类。但您还需要知道将用于转换的时区。在下面的示例中,我使用的是“UTC”:
Date date = DateTimeUtils.toDate(dt.atZone(ZoneOffset.UTC).toInstant());

要切换到不同的时区,可以执行以下操作:

Date date = DateTimeUtils.toDate(dt.atZone(ZoneId.of("Europe/London")).toInstant());

请注意,API使用IANA时区名称(始终采用格式Continent/City,如America/Sao_PauloEurope/Berlin)。避免使用三个字母的缩写(如CSTPST),因为它们是模糊和不标准的。要找到最适合每个地区的时区,请使用ZoneId.getAvailableZoneIds()方法并检查哪个最适合您的用例。
附注:上述最后一个示例(dt.atZone(ZoneId.of("Europe/London")))将在伦敦时区创建日期/时间2016-10-24T19:31。但如果您想在UTC中获得2016-10-24T19:31,然后将其转换到另一个时区,那么应该执行以下操作:
Date date = DateTimeUtils.toDate(dt
    // first convert it to UTC
    .toInstant(ZoneOffset.UTC)
    // then convert to LondonTimezone
    .atZone(ZoneId.of("Europe/London")).toInstant());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接