出现错误 java.text.ParseException: Unparseable date: (at offset 0) 即使简单日期格式和字符串值相同

Posted

技术标签:

【中文标题】出现错误 java.text.ParseException: Unparseable date: (at offset 0) 即使简单日期格式和字符串值相同【英文标题】:Getting error java.text.ParseException: Unparseable date: (at offset 0) even if the Simple date format and string value are identical 【发布时间】:2018-02-27 08:24:26 【问题描述】:

即使要检查的格式和字符串值相同,我也总是遇到解析异常。 代码如下:

String format = "EEE MMM dd HH:mm:ss z yyyy";
String value = "Mon Sep 18 10:30:06 MST 2017";
public static boolean isValidFormat(String format, String value) 
    Date date = null;
    try 
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        date = sdf.parse(value); // here it breaks
        if (!value.equals(sdf.format(date))) 
            date = null;
        
     catch (ParseException ex) 
        ex.printStackTrace(); //java.text.ParseException: Unparseable date: 
                                "Mon Sep 18 10:30:06 MST 2017" (at offset 0)
    
    return date != null;

【问题讨论】:

你想做什么?告诉我然后我会看看如何给出答案的解决方案 实际上是我从数据库中获取的上述日期“值”,我需要将上述日期值转换为不同的格式。所以首先我将上面的字符串值更改为日期,然后使用另一个 sdf 将获得的日期转换为我需要的格式 要获取dateFormatter()的代码吗? 我认为问题出在时区上,当我在没有 z 和 MST 的情况下运行您的代码时,它返回 true,也许 MST 它不是 SimpleDateFormatter 所期望的方式。 我给出了日期格式化程序的代码,请检查它是否适合您。如果有效,请不要忘记将其标记为正确答案。 【参考方案1】:

它表示您的日期时间字符串在索引 0 处不可解析。索引 0 是它表示 Mon 的位置,因此三个字母的时区缩写不是第一个嫌疑人。语言环境是。 “星期一”在英语中是星期一的缩写,但在许多其他语言中却不是。因此,如果您的设备具有非英语语言设置(甚至可能最近已更改),这将充分解释您的观察结果。

短视的解决办法是

        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.ROOT);

我使用Locale.ROOT 表示不应进行任何语言特定的处理。如果您的字符串是英语,因为英语通常是全球计算中使用的语言,我会认为这个选择是合适的。另一方面,如果它是英文的,因为它来自说英语的区域设置,那么该区域设置将是正确的使用。

通过此更改,您的代码在我的计算机上将您的日期格式化为Mon Sep 18 11:30:06 MDT 2017,正如您所见,它与我们开始时的值不同,因此您的方法返回 false。我的 JVM 将 MST 理解为山区标准时间,然后假定夏令时 (DST) 为 9 月并相应地格式化了字符串。

ThreeTenABP

也就是说,DateSimpleDateFormat 是早已过时的课程。您应该考虑摆脱它们并改用现代 Java 日期和时间 API。在 android 上,您可以在 ThreeTenABP 中获得它,请参阅 this question: How to use ThreeTenABP in Android Project。现在你可以这样做了:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format, Locale.ROOT);
    try 
        return ZonedDateTime.parse(value, dtf).format(dtf).equals(value); 
     catch (DateTimeParseException dtpe) 
        dtpe.printStackTrace();
        return false;
    

这与上面的行为相同。

三个字母的时区缩写

您应该尽可能避免使用三个和四个字母的时区缩写。它们不是标准化的并且通常是模棱两可的。例如,MST 可能表示马来西亚标准时间或山地标准时间。后者甚至不是一个完整的时区,因为 MDT 用于一年中的大部分时间,这导致了我在上面所说的观察到的问题。

相反,看看您是否可以获得 ISO 8601 格式的字符串,例如 2017-09-18T10:30:06+08:00。第二好的,只是得到一些明确的东西。一种方法是包含与 UTC 的偏移量,而不是时区 ID(或两者兼有)。

【讨论】:

关于 MST 与 MDT,我进行了一些测试,在解析时,MST 默认为 America/Denver(有 DST) - 您可以在解析后调用 sdf.getTimeZone().getID() 进行检查(是的,已解析的时区在格式化程序中设置,为什么?)。但也许 OP 的默认时区是没有 DST 的(例如 America/Phoenix),这可以解释为什么输入在 9 月有 MST。 @Ole V.V 是的,客户报告他将设备语言更改为西班牙语,这导致了错误。当他再次将设备更改为英语时,它起作用了。【参考方案2】:

这是 dateformatter 的代码,它将帮助您将日期转换为任何时间格式。

public void setDate(String date) 
            dateInput = (TextView) itemView.findViewById(R.id.dateText);
            DateFormat inputFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
            try 
                dateData = inputFormat.parse(date);
             catch (ParseException e) 
                e.printStackTrace();
            
            DateFormat outputFormat = new SimpleDateFormat("pur your desirable format");
            String outputString = outputFormat.format(dateData);
            dateInput.setText(outputString);
        

【讨论】:

感谢您的快速回复。但我看到我的代码和你的一样。你觉得我的代码有问题吗? 尽量不要将 DateFormat 放在您的 try 和 catch 块中,并请上面的代码,因为它有区别。你自己看我用过这个DateFormat inputFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy"); 唯一的变化是您将 SDF 更改为 DateFormat。我尝试使用相同的代码仍然会引发错误【参考方案3】:

我使用的代码几乎与您使用的代码相同,只是在 SimpleDateFormat 实例化方面略有不同。

public static final String DATE_FORMAT = "EEE MMM d yyyy z HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.ROOT);
simpleDateFormat.format(date);

它返回 2017 年 9 月 18 日星期一 GMT+03:00 23:04:10。

【讨论】:

【参考方案4】:

切勿在没有Locale 的情况下使用SimpleDateFormatDateTimeFormatter

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

另外,请注意 java.util 的日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,转用modern date-time API。

无论出于何种原因,如果您必须坚持使用 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。

演示:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main 
    public static void main(String[] args) 
        final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
        System.out.println(zdt);
    

输出:

2017-09-18T10:30:06-06:00[America/Denver]

在我们继续之前关于时区的重要说明:

避免使用 3 个字母的缩写指定时区。时区应以 Region/City 格式指定名称,例如ZoneId.of("Europe/London")。使用此约定,UTCZoneId 可以用ZoneId.of("Etc/UTC") 指定。以UTC[+/-]Offset 指定的时区可以指定为Etc/GMT[+/-]Offset,例如ZoneId.of("Etc/GMT+1")ZoneId.of("Etc/GMT+1")

也有一些例外情况,例如要指定Turkey 的时区,我们使用

ZoneId.of("Turkey")

以下代码将为您提供所有可用的ZoneIds:

// Get the set of all time zone IDs.
Set<String> allZones = ZoneId.getAvailableZoneIds();

您应该要求您的服务器应用程序使用此约定为您提供日期时间,例如

Mon Sep 18 10:30:06 America/Denver 2017

上面的代码,没有任何改变,将适用于这个日期时间字符串。

回到原来的话题:

默认情况下,DateTimeFormatter#ofPattern 使用 JVM 在启动时根据宿主环境设置的 default FORMAT locale。 SimpleDateFormat 也是如此。我试图通过下面的演示来说明这个问题:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main 
    public static void main(String[] args) 
        final String strDateTime = "Mon Sep 18 10:30:06 America/Denver 2017";
        DateTimeFormatter dtfWithDefaultLocale = null;

        System.out.println("JVM's Locale: " + Locale.getDefault());
        // Using DateTimeFormatter with the default Locale
        dtfWithDefaultLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out
                .println("Parsed with JVM's default locale: " + ZonedDateTime.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 = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
        System.out.println("JVM's Locale: " + Locale.getDefault());
        System.out.println("DateTimeFormatter's Locale: " + dtfWithEnglishLocale.getLocale());
        ZonedDateTime zdt = ZonedDateTime.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 = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out
                .println("Parsed with JVM's default locale: " + ZonedDateTime.parse(strDateTime, dtfWithDefaultLocale));
    

输出:

JVM's Locale: en_GB
DateTimeFormatter's Locale: en_GB
Parsed with JVM's default locale: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: en
Parsed with Locale.ENGLISH: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: fr_FR
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Mon Sep 18 10:30:06 America/Denver 2017' could not be parsed at index 0
    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.ZonedDateTime.parse(ZonedDateTime.java:598)
    at Main.main(Main.java:32)

以下演示,使用SimpleDateFormat,只是为了完整起见:

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 
        final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM d H:m:s z yyyy", Locale.ENGLISH);
        Date date = sdf.parse(strDateTime);
        System.out.println(date);
    

输出:

Mon Sep 18 18:30:06 BST 2017

注意:java.util.Date 对象不像modern date-time types 那样是真正的日期时间对象;相反,它表示距离Epoch of January 1, 1970 的毫秒数。当你打印一个java.util.Date 的对象时,它的toString 方法返回从这个毫秒值计算的日期时间。由于java.util.Date 没有时区信息,它会应用您的JVM 的时区并显示相同的信息。如果您需要在不同的时区打印日期时间,您需要将时区设置为SimpleDateFomrat 并从中获取格式化字符串。

【讨论】:

以上是关于出现错误 java.text.ParseException: Unparseable date: (at offset 0) 即使简单日期格式和字符串值相同的主要内容,如果未能解决你的问题,请参考以下文章

为啥Debug无错误Release出现错误?

WIN2008 R2 组策略出现错误

在Ubuntu下,改为国内软件源,出现错误如图?

浏览器出现错误怎么办

电脑上网有个网站进去后出现错误523怎么弄?

SQL登陆出现错误,用SQL身份登陆没问题,用windows身份登陆就出现下面的错误。求解!!!