在 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 .
我正在重构一些代码,我正在尝试区分要解决的问题。 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