将整数YYYYMMDD转换为带有本地时区的java.util.Date的好方法

时间:2015-01-12 20:26:32

标签: java date jodatime date-conversion

我理解这个问题可能看起来像FAQ主题,但这里的关键问题是时区和性能。我有整数YYYYMMDD日期(例如20150131)。这是一个很好的“几乎工作”的解决方案:

import org.joda.time.DateTime;
import java.util.Date;

// ...

public Date extract(final int intDate) {
    Date result = null;

    try {
        result = new DateTime(
                intDate / 10000,
                (intDate / 100) % 100,
                intDate % 100,
                0,
                0,
                0,
                0).toDate();

    } catch (final IllegalArgumentException e) {
        // Log failure
    }
    return result;
}

'几乎'是因为我收到的时区为0x0126810d和EET(当DST时为UTC + 2 / +3):

  

java.lang.IllegalArgumentException:由于时区而导致的非法即时   抵消过渡:1930-06-20T22:00:00.000

至少使用JODA 1.6。我不能轻易切换。 但我希望它是1930-06-21T00:00:00.000+02:00而我不关心UTC表示。

  1. 是否可以(可以java.util.date存储此类日期)?
  2. 好的,任何更好的高性能方式来实现这一点(JODA只是在这里补救,而不是关键)?
  3. 是的,我知道这个时间不存在:

    roman@node4:$ zdump -v Europe/Kiev | grep 1930
    Europe/Kiev  Fri Jun 20 21:59:59 1930 UTC = Fri Jun 20 23:59:59 1930 EET isdst=0 gmtoff=7200
    Europe/Kiev  Fri Jun 20 22:00:00 1930 UTC = Sat Jun 21 01:00:00 1930 MSK isdst=0 gmtoff=10800
    

4 个答案:

答案 0 :(得分:2)

java.time和LocalDate

无论使用的是Joda-Time(如您的问题)还是现代的Java日期和时间API java.time,我都认为解决问题的方法是使用LocalDate。我建议您只是坚持这样做,不要使用org.joda.time.DateTimejava.util.Date。特别是不是后者,它总是设计不佳,现在已经过时了。

我正在向大家介绍。

    int intDate = 19300621; // 0x0126810d

    String asString = String.valueOf(intDate);
    LocalDate date = LocalDate.parse(asString, DateTimeFormatter.BASIC_ISO_DATE);

    System.out.println(date);
  

1930-06-21

我发现此代码比除法和模运算的代码更易于阅读。它的效率不高,但是在20个案例中,有19个以上的案例都不在乎。如果您喜欢这些划分,当然也可以使用java.time进行划分:

    int year = intDate / 10000;
    int monthDay = intDate % 10000;
    int month = monthDay / 100;
    int day = monthDay % 100;
    LocalDate date = LocalDate.of(year, month, day);

如果确实需要java.util.Date ,而该API尚未升级到Java。时间(或Joda-Time)进行转换(无论您使用了上述哪种转换):

    Instant asInstant = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
    Date oldfashionedDate = Date.from(asInstant);
    System.out.println(oldfashionedDate);

当我的默认时区设置为Europe / Zaporozhye时输出:

  

1930年6月21日星期六01:00:00 EET

(我们注意到,由于转换为夏令时(DST),所以获得了01:00:00。该时区的当天00:00:00的时间不存在。)

如果仍在使用Joda-Time

如果您仍在使用Joda-Time,则使用toDateTimeAtStartOfDay()自己的答案就可以了。

PS我转载了您有关Joda-Time 2.9.9的问题,我的时区设置为Europe / Zaporozhye,您的整数为19300621(不知道为什么您将其设置为十六进制0x0126810d)。我有一个与您相似的例外:org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 1930-06-21T00:00:00.000 (Europe/Zaporozhye)

链接

Oracle tutorial: Date Time解释了如何使用java.time。

答案 1 :(得分:1)

我建议如下:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month);
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
result = cal.getTime();

答案 2 :(得分:1)

好的,我认为我设法复制了你的问题:

    TimeZone.setDefault(TimeZone.getTimeZone("Europe/Kiev"));
    System.out.println( extract(0x0126810d));

(之前我用" EET"尝试过,但显然完全得到了不同的时区)

我得到了一个非法的争论异常,尽管它提到的日期有点不同。这可能是因为我的Joda版本。

Illegal instant due to time zone offset transition (daylight savings time 'gap'): 1930-06-21T00:00:00.000 (Europe/Kiev)

嗯,解决问题的方法不是在欧洲/基辅地区,至少为了Joda的转换:

public static Date extract(final int intDate) {
    Date result = null;
    DateTimeZone tz = DateTimeZone.forOffsetHours(2);

    try {
        result = new DateTime(
                intDate / 10000,
                (intDate / 100) % 100,
                intDate % 100,
                0,
                0,
                0,
                0,
                tz).toDate();

    } catch (final IllegalArgumentException e) {
        System.err.println(e.getMessage());
        return null;
    }
    return result;
}

这样可以避免错误。如果您希望在多次调用tz方法的情况下提高性能,可以将extract变量定义和初始化移动到字段。

请注意,当您使用默认日期格式打印生成的Date对象时,默认日期格式会使用默认时区(欧洲/基辅),结果将是:

Sat Jun 21 01:00:00 EET 1930

您可以使用以下方式正确打印:

SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
fmt.setTimeZone(TimeZone.getTimeZone("GMT+2"));


System.out.println(fmt.format( extract(0x0126810d)));

但也许如果您不想考虑DST,您应该只使用日期,就像它们是UTC一样。这取决于你想要用它们做什么,真的。

最后一点:用Calendar很容易达到相同的结果:

public static Date extract2(final int intDate) {
    cal.set(intDate / 10000, ( intDate / 100 ) % 100 - 1, intDate % 100);
    return cal.getTime();
}

其中cal是在字段中设置的Calendar实例,以避免重复创建和清除它:

public static final Calendar cal;
static {
    cal = Calendar.getInstance();
    cal.clear();
}

(但是,脑多线程)。

不确定您提到的性能问题,以及差异有多重要。

答案 3 :(得分:0)

好的,最后得到了以下片段,它最接近我的期望。像SDF一样但速度要快很多 - 就像没有字符串解析只是为了得到数字:

import org.joda.time.DateTime;
import org.joda.time.LocalDate;

public static Date toDateJoda(int intDate) {
    LocalDate ldt = new LocalDate(
        intDate / 10000,
        (intDate / 100) % 100,
        intDate % 100);

    DateTime dt = ldt.toDateTimeAtStartOfDay();
    return dt.toDate();
}

解析所有事情并获得像我这样的案件的下一个有效日期/时间。