从字符串创建 Joda 日期时间 UTC

Create Joda datetime UTC from String

我有以下 String 我想更改为 UTC:

Thu Aug 24 07:38:32 GMT+01:00 2017

我正在使用 Joda-Time 库。

我知道如何创建一个新的 Datetime 例如 new dateTime(DateTimeZone.UTC) 但是我如何从上面的 String 创建一个 DateTime 对象?

我尝试了以下但出现异常。当然必须有另一种方法来创建 DT 对象而不砍掉原来的 String 吗?如果外部 API 改变了它向我的应用程序发送原始 String 的方式,我的 String 操作代码将会失败。

DateTimeFormatter df = DateTimeFormat.forPattern("dd-MMM-YYYY HH:mm");
        String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
        DateTime dt = DateTime.parse(strOrigTime, df);

        Log.e(TAG, "dt after parse = " + dt.toString());

错误:

Caused by: java.lang.IllegalArgumentException: Invalid format: "Thu Aug 24 07:38:32 GMT+01:00 2017"
    at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)
    at org.joda.time.DateTime.parse(DateTime.java:144)

使用的格式(dd-MMM-YYYY HH:mm)表示:日(dd)后跟-,后跟月(MMM),后跟-,然后是年份 (YYYY) 等等(check the javadoc 了解更多详情)。

此格式与输入字符串不匹配(星期几后跟月份,后跟日,然后是 hour/minute/second,等等)。所以第一件事是使用与输入匹配的格式,否则你总是会得到 "Invalid format" 错误。

另一个细节是星期几和月份名称是英文的,因此您还必须使用 java.util.Locale 来指定您用来解析输入的语言。如果您不使用区域设置,将使用系统默认设置,并且不能保证它始终是英语(并且它也可以更改,即使在运行时,所以最好指定一个)。

我还必须添加 "GMT" 作为文字并调用 withOffsetParsed() 以使其在解析的对象中包含偏移量 (+01:00):

DateTimeFormatter df = DateTimeFormat
    // use a pattern that matches input
    .forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
    // use English locale for day of week and month
    .withLocale(Locale.ENGLISH)
    // include the offset (+01:00) in the parsed object
    .withOffsetParsed();
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
System.out.println(dt.toString());

输出为:

2017-08-24T07:38:32.000+01:00

然后,您可以为这个对象设置UTC时区:

dt = dt.withZone(DateTimeZone.UTC);
System.out.println(dt.toString());

输出将是:

2017-08-24T06:38:32.000Z

请注意,withZone 方法保留了相同的时刻(两个日期代表相同的时间点),只是输出中使用的时区发生了变化。但是两个日期是等效的(它们代表相同的时刻,因为偏移量 +01:00 中的 07:38 与 UTC 中的 06:38 相同)。

如果您希望所有日期都转换为UTC,您也可以在格式化程序中设置:

// set UTC to the formatter
df = df.withZone(DateTimeZone.UTC);

那么你不需要在 DateTime 对象中调用 withZone:所有解析的日期都将转换为 UTC。


你也说过"if the external API changes how it sends my app the orignal String, my String manipulation code would fail".

好吧,如果输入 String 发生变化,您也必须更改格式 - 没有其他办法,Joda-Time 不能只猜测格式是什么,您必须告诉它.

如果你想解析不止一种格式,有一种方法可以创建一个使用许多不同模式的格式化程序,并尝试解析每一个,直到其中一个有效(或者抛出异常,如果 none作品)。你可以这样做:

// format 1
DateTimeFormatter f1 = DateTimeFormat
    // use a pattern that matches input
    .forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
    // use English locale for day of week and month
    .withLocale(Locale.ENGLISH)
    // include the offset (+01:00) in the parsed object
    .withOffsetParsed();

// format 2
DateTimeFormatter f2 = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss Z");
// array of all possible formats
DateTimeParser[] parsers = { f1.getParser(), f2.getParser() };
// formatter that uses all the possible formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // append array of possible formats
    .append(null, parsers)
    // create formatter
    .toFormatter().withLocale(Locale.ENGLISH).withOffsetParsed()
    // set all parsed objects to UTC
    .withZone(DateTimeZone.UTC);

// parse first format
System.out.println(DateTime.parse("Thu Aug 24 07:38:32 GMT+01:00 2017", formatter));
// parse second format
System.out.println(DateTime.parse("24/08/2017 07:38:32 +01:00", formatter));

两个日期都将被解析为:

2017-08-24T06:38:32.000Z

然后您可以根据需要向数组添加新格式。


Java新Date/TimeAPI

Joda-Time 处于维护模式,正在被新的 APIs 取代,所以我不建议用它开始一个新项目。即使在 joda's website 它说:"Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".

如果您不能(或不想)从 Joda-Time 迁移到新的 API,您可以忽略此部分。

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是包名称(在 Java 8 中是 java.time,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 classes 和方法 names 相同。

解析输入的代码非常相似,格式略有变化。 我正在使用 Instant class,因为你想要 UTC 的输出,而 Instant 代表一个 UTC 时刻:

// format 1
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy", Locale.ENGLISH);
// format 2
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss XXX");
// formatter with both formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // add format 1
    .appendOptional(f1)
    // add format 2
    .appendOptional(f2)
    // create formatter
    .toFormatter(Locale.ENGLISH);
// parse first format
System.out.println(Instant.from(formatter.parse("Thu Aug 24 07:38:32 GMT+01:00 2017")));
// parse second format
System.out.println(Instant.from(formatter.parse("24/08/2017 07:38:32 +01:00")));

这将输出:

2017-08-24T06:38:32Z
2017-08-24T06:38:32Z