出现错误 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
也就是说,Date
和 SimpleDateFormat
是早已过时的课程。您应该考虑摆脱它们并改用现代 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
的情况下使用SimpleDateFormat
或DateTimeFormatter
由于给定的日期时间是英文的,你应该在你的日期时间解析器中使用Locale.ENGLISH
;否则解析将在使用非英语类型区域设置的系统(计算机、电话等)中失败。
另外,请注意 java.util
的日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用,转用modern date-time API。
演示:
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")
。使用此约定,UTC
的ZoneId
可以用ZoneId.of("Etc/UTC")
指定。以UTC[+/-]Offset
指定的时区可以指定为Etc/GMT[+/-]Offset
,例如ZoneId.of("Etc/GMT+1")
、ZoneId.of("Etc/GMT+1")
等
也有一些例外情况,例如要指定Turkey
的时区,我们使用
ZoneId.of("Turkey")
以下代码将为您提供所有可用的ZoneId
s:
// 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) 即使简单日期格式和字符串值相同的主要内容,如果未能解决你的问题,请参考以下文章