18

我有一个来自电子邮件标题的字符串,例如Date: Mon, 27 Oct 2008 08:33:29 -0700. 我需要的是一个 GregorianCalendar 的实例,它将代表同一时刻。就这么简单——我该怎么做?

而对于最快的——这将无法正常工作:

SimpleDateFormat format = ... // whatever you want
Date date = format.parse(myString)
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date)

因为它将时区标准化为 UTC(或您的本地机器时间,取决于 Java 版本)。我需要的是 calendar.getTimeZone().getRawOffset() 来返回-7 * milisInAnHour

4

3 回答 3

15

如果可以的话,我建议您查看 Joda Time 库。在核心平台提供类似功能的情况下,我通常反对使用第三方库,但我将其作为一个例外,因为 Joda Time 的作者也在 JSR310 背后,而 Joda Time 基本上最终会滚入 Java 7。

http://joda-time.sourceforge.net/

所以无论如何,如果 Joda Time 是一个选项,这样的事情应该可以工作:

DateTimeFormatter formatter =
    DateTimeFormat.forPattern("your pattern").withOffsetParsed();
DateTime dateTime = formatter.parseDateTime("your input");
GregorianCalendar cal = dateTime.toGregorianCalendar();

我希望这有帮助。

于 2008-10-27T16:43:50.627 回答
6

对于最快的 - 这不会正常工作......因为它会将时区标准化为 UTC(或您的本地机器时间,取决于 Java 版本)。我需要的是 calendar.getTimeZone().getRawOffset() 返回 -7 * milisInAnHour。

从技术上讲,这确实有效,因为虽然它会返回一个 TimeZone 等于当前系统 TimeZone 的对象,但时间将被修改以考虑偏移量。

这段代码:

String dateString = "Mon, 27 Oct 2008 08:33:29 -0700";
DateFormat df = new SimpleDateFormat("E, dd MMM yyyy hh:mm:ss Z");
Date parsed = df.parse(dateString);
System.out.println("parsed date: " + parsed);

Calendar newCalendar = Calendar.getInstance();
newCalendar.setTime(parsed);

输出:

解析日期:2008 年 10 月 27 日星期一 11:33:29 EDT

这在技术上是正确的,因为我的系统时区是 EDT / UTC 减去四个小时(比你的时间早三个小时)。如果您将时间表示为自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数(这是Date对象存储日期/时间的方式),那么这些日期/时间是相等的,只是 TimeZone 不同。

您的问题实际上是如何将日期/日历转换为我的时区?为此,请查看我对上一个问题如何使用 Java 处理日历时区的回复?

于 2008-10-27T17:29:20.440 回答
3

java.time

使用现代日期时间 APIjava.time的解决方案:

现代日期时间 API 提供OffsetDateTime了表示具有时区偏移的日期时间对象。它可以转换为Instant代表时间轴上的瞬时点的哪一个。AnInstant独立于任何时区,即它具有+00:00小时的时区偏移量,ZISO 8601标准中指定。

Instant#toEpochMilli将此瞬间转换为从 1970-01-01T00:00:00Z 开始的毫秒数。该值可以设置为一个对象,GregorianCalendar然后该对象将代表同一时刻。

演示:

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "Mon, 27 Oct 2008 08:33:29 -0700";
        OffsetDateTime odt = OffsetDateTime.parse(strDateTime, DateTimeFormatter.RFC_1123_DATE_TIME);
        System.out.println(odt);
        
        // In case you want a time zone neutral object, convert to Instant
        Instant instant = odt.toInstant();
        System.out.println(instant);
        
        // Edit: If the requirement is a GregorianCalendar having the offset from
        // the string — typically for an old API not yet upgraded to java.time: 
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, DateTimeFormatter.RFC_1123_DATE_TIME);
        GregorianCalendar gc = GregorianCalendar.from(zdt);
        
        System.out.println("As Date:      " + gc.getTime());
        System.out.println("Time zone ID: " + gc.getTimeZone().getID());
        System.out.println("Hour of day:  " + gc.get(Calendar.HOUR_OF_DAY));
        // ...
    }
}

输出:

2008-10-27T08:33:29-07:00
2008-10-27T15:33:29Z
As Date:      Mon Oct 27 15:33:29 GMT 2008
Time zone ID: GMT-07:00
Hour of day:  8

调用转换为没有时区的getTime()(另一个旧且容易出错的类),因此偏移量丢失。打印时区 ID 和时间表明偏移量和时间都保存在.GregorianCalendarDateGregorianCalendar

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


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

于 2021-05-10T14:48:17.160 回答