在 SimpleDateFormat 上使用 DateTimeFormatter

Using DateTimeFormatter over SimpleDateFormat

我正在重构一些代码,我正在尝试区分要解决的问题。 formatDateRfc1123的作用很明显,但是添加代码的附加值是什么?

在第一次提交中,这是函数,它可能会因为我不知道的原因而崩溃:

public static String formatDateRfc1123(final Date timestamp) {
    SimpleDateFormat formatRf1123 = new SimpleDateFormat(RFC_1123);
    formatRf1123.setTimeZone(GMT_ZONE);
    return formatRfc1123.format(timestamp);
} 

后来,对它进行了重构以解决崩溃问题:

public static String formatDateRfc1123(final Date timestamp, final int buildVersion) {
    if (buildVersion >= Build.VERSION_CODE_O) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(RFC_1123, US);
        Instant instant = Instant.ofEpochSecond(TimeUnit.MILLISECONDS.toSeconds(timestamp.getTime());
        ZoneId zoneId = ZoneId.of(GMT_ZONE.getID());
        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);
        return dateTimeFormatter.format(zonedDateTime);
    } else {
        SimpleDateFormat formatRf1123 = new SimpleDateFormat(RFC_1123);
        formatRf1123.setTimeZone(GMT_ZONE);
        return formatRfc1123.format(timestamp);
    }
} 

有一个旧的用户故事链接到添加的代码,但它只链接到已删除的 Fabric 崩溃日志。所以,我只知道它会崩溃,不知道什么值抛出了什么异常。

什么值和SDK版本容易崩溃?

您使用 java.time 的代码的前三行过于复杂,导致计算容易出错。我建议您使用开箱即用的常量来避免任何歧义和错误。以下是推荐的方法:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(formatDateRfc1123(new Date()));
    }

    public static String formatDateRfc1123(final Date timestamp) {
        // Use the out-of-the-box formatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(Locale.US);

        // Use Date#toInstant to convert it into Instant
        Instant instant = timestamp.toInstant();

        // Use the out-of-the-box constant for UTC (or GMT)
        ZoneId zoneId = ZoneOffset.UTC;

        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);
        // ZonedDateTime zonedDateTime = instant.atZone(zoneId); //This is simpler

        return dateTimeFormatter.format(zonedDateTime);
    }
}

请注意,表示时区的三字母字符串容易出错。您应该始终使用时区的全名,即 Continent/City。下面给出一个例子来演示它。

import java.time.ZoneId;

public class Main {
    public static void main(String[] args) {
        ZoneId zoneId = ZoneId.of("EDT");
        // ...
    }
}

输出:

Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: EDT
    at java.base/java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:279)
    at java.base/java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:234)
    at java.base/java.time.ZoneRegion.ofId(ZoneRegion.java:120)
    at java.base/java.time.ZoneId.of(ZoneId.java:408)
    at java.base/java.time.ZoneId.of(ZoneId.java:356)
    at Main.main(Main.java:5)

SimpleDateFormat 甚至不会抛出异常,并且会以 UTC(或 GMT)静默格式化日期时间。好危险啊!

不正确:

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

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        sdf.setTimeZone(TimeZone.getTimeZone("EDT"));
        System.out.println(sdf.format(new Date()));
    }
}

输出:

2020-12-05T01:36:48Z

正确:

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

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        System.out.println(sdf.format(new Date()));
    }
}

输出:

2020-12-04T20:36:05-05:00

请注意 java.util 的日期时间 API 及其格式 API、SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API. Learn more about the modern date-time API at Trail: Date Time.

注意:如果您正在为Android项目工作并且您的AndroidAPI级别仍然不符合Java-8,勾选Java 8+ APIs available through desugaring and .