日期对象SimpleDateFormat在Java(Android)环境中无法正确解析时间戳字符串

时间:2011-04-12 14:03:28

标签: java android parsing date simpledateformat

我正在使用SimpleDateFormat对象和Date对象,如下所示。问题是Date对象显示错误的日期,这与原始字符串相距几分钟。 Date对象似乎在调试器中以总毫秒数存储时间。

关于这个问题的任何想法?

import java.text.SimpleDateFormat;

import java.util.Date;

Date played_at_local; 

dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");

played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00"); 

//played_at_local shows "Mon Apr 11 22:35:29 America/Chicago 2011" in debugger

7 个答案:

答案 0 :(得分:15)

尝试从格式字符串中删除小数秒。我刚遇到同样的问题,但格式略有不同。我的输入格式不是ISO格式(没有“T”,没有“Z”),但症状是相同的 - 时间被一些随机数分钟和秒关闭,但其他一切都很好。这就是我的日志结果:

使用小数秒格式时:

SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");

# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:15:46 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:22:07 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:08:57 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:56:14 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:39:25 EDT 2011

我通过从格式中删除小数秒来修复它。

SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:11:15 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:09:37 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:05:55 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:55:05 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:38:35 EDT 2011

我认为发生的是输入字符串的“小数秒”部分太长(在OP示例中也是如此)。它似乎只期待三个小数位。如果你做数学运算(参加第一个例子):

  • 小秒数= 0.271816秒
  • DateFormat看到的是第二个
  • 271816 / 1000
  • 271816/1000 == 271秒
  • 271/60 = 4分钟
  • 271%60 = 31秒
  • 17:11:15至17:15:46正好是4分31秒。

答案 1 :(得分:1)

试试这个,为我工作 Z 应该在日期使用,或从格式字符串中删除

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'");

played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726Z-05:00");

答案 2 :(得分:1)

您的代码中存在三个主要问题:

  1. 您已将 .SSSSSS 用于几分之一秒,而 SimpleDateFormat 不支持超过毫秒 (.SSS) 的精度。这也意味着您需要将几分之一秒的数字限制为三。
  2. 您已使用 Z 来解析时区偏移量 -05:00 而用于此的 correct patternXXX
  3. 您曾使用 hh 以 24 小时制格式,而正确的模式是 HH。符号 hh 用于 12 小时制(即上午/下午)格式的时间。

除此之外,我建议您始终将 Locale 与日期解析/格式化 API 一起使用,因为日期时间字符串的部分在不同的 Locale 中以不同的方式表示。

演示:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
        Date date = sdf.parse("2011-04-11T22:27:18.491-05:00");

        // Print the default string i.e. Date#toString
        System.out.println(date);

        // Print the date-time in a custom format
        sdf.setTimeZone(TimeZone.getTimeZone("GMT-05:00"));
        System.out.println(sdf.format(date));
    }
}

输出:

Tue Apr 12 04:27:18 BST 2011
2011-04-11T22:27:18.491-05:00

关于旧日期时间 API 的一些事实:

  1. java.util.Date 对象不是像 modern date-time types 那样的真实日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即 January 1, 1970, 00:00:00 GMT(或 UTC)。当您打印 java.util.Date 的对象时,它的 toString 方法返回 JVM 时区中的日期时间,从这个毫秒值计算出来。如果您需要在不同的时区打印日期时间,则需要将时区设置为 SimpleDateFormat 并从中获取格式化的字符串。
  2. java.util 日期时间 API 及其格式 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API*

使用现代日期时间 API:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime odt = OffsetDateTime.parse("2011-04-11T22:27:18.491726-05:00");

        // Print the default string i.e. OffsetDateTime#toString
        System.out.println(odt);

        // Print the date-time in a custom format. Note: OffsetDateTime#toString drops
        // seconds if it is zero
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSXXX");
        System.out.println(dtf.format(odt));
    }
}

输出:

2011-04-11T22:27:18.491726-05:00
2011-04-11T22:27:18.491726-05:00

注意:对于符号 DateTimeFormatteru 表示,而符号 y 表示年时代。在 [AD][2] 时代,它对一年没有任何影响,但在 BC 时代,它对一年很重要。查看 this answer 以了解更多信息。

Trail: Date Time 了解有关现代日期时间 API 的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

答案 3 :(得分:0)

05:00 -->> 0500

hh --> HH // error not because of this ,but date is in 24hr format.

played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00"); 

played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-0500"); 

答案 4 :(得分:0)

您可以尝试以下方法: http://docs.oracle.com/javase/6/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String)

关键是小数位是可选的,您可以使用其中的可变数字。但是,这似乎并未考虑时区。

来自文档:

valueOf

public static Timestamp valueOf(String s)
Converts a String object in JDBC timestamp escape format to a Timestamp value.
Parameters:
s - timestamp in format yyyy-mm-dd hh:mm:ss[.f...]. The fractional seconds may be omitted.
Returns:
corresponding Timestamp value
Throws:
IllegalArgumentException - if the given argument does not have the format yyyy-mm-dd hh:mm:ss[.f...]

答案 5 :(得分:0)

试试这个:

dTime  =  new  SimpleDateFormat("HH:mm:ss:SS");
String  sTime =  (dTime.format(new java.util.Date())).toString();

希望这个帮助

答案 6 :(得分:0)

java.time和ThreeTenABP

SimpleDateFormat无法正确解析日期时间字符串。另一方面,现代的Java日期和时间API java.time支持开箱即用的格式。

import org.threeten.bp.OffsetDateTime;

    String dateTimeString = "2011-04-11T22:27:18.491726-05:00";
    OffsetDateTime playedAtLocal = OffsetDateTime.parse(dateTimeString);
    System.out.println("Parsed into " + playedAtLocal);

输出为:

  

解析为2011-04-11T22:27:18.491726-05:00

SimpleDateFormat仅支持毫秒,仅精确到秒的三位小数,而不是2,不是4,不是6(诚然,我不确定某些Android版本的SimpleDateFormat版本是否可以做到更好,但您的问题表明您的版本不能)。 SimpleDateFormat也是非常麻烦且长期过时的,因此您还是不想使用它。

java.time更好用。您会注意到,我们甚至不需要显式的格式化程序,因此不需要编写格式模式字符串,这始终是容易出错的任务。您的日期时间字符串为ISO 8601格式,并且java.time类将ISO 8601解析为其默认值。

问题:我可以在Android上使用java.time吗?

是的,java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6

  • 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
  • 在Java 6和7中,获得了ThreeTen反向端口,这是现代类的反向端口(JSR 310的ThreeTen;请参见底部的链接)。
  • 在(较旧的)Android上,使用Android版本的ThreeTen Backport。叫做ThreeTenABP。并确保您使用子包从org.threeten.bp导入日期和时间类。

链接