java.time 中的 ZoneOffset 和 OffsetDateTime
考虑为您的日期和时间工作使用现代 Java 日期和时间 API java.time。航海(或军事)时区只是与 UTC 整小时的偏移量,因此我们不需要考虑夏令时 (DST) 和其他异常情况的任何花哨的时区规则,因为没有任何时区规则。一个平原ZoneOffset
就可以了。
时区名称不是内置的。我们需要自己编写转换代码。一种方法是通过一个数组,我们可以在其中查找时区名称并获取偏移量:
private static final int[] offsetPerNauticalTimeZone = new int['Z' + 1];
private static final int invalid = -100;
static {
Arrays.fill(offsetPerNauticalTimeZone, invalid);
// The letter "Z" ("Zulu") indicates Coordinated Universal Time (UTC).
offsetPerNauticalTimeZone['Z'] = 0;
// A through M denote positive offsets, but J is skipped
for (int offset = 1; offset <= 9; offset++) {
offsetPerNauticalTimeZone['A' - 1 + offset] = offset;
}
for (int offset = 10; offset <= 12; offset++) {
offsetPerNauticalTimeZone['K' - 10 + offset] = offset;
}
// N through Y are the negative offsets
for (int negatatedOffset = 1; negatatedOffset <= 12; negatatedOffset++) {
offsetPerNauticalTimeZone['N' - 1 + negatatedOffset] = -negatatedOffset;
}
}
示例使用:
char nauticalTimeZone = 'A'; // Set desired letter here
int offsetHours = offsetPerNauticalTimeZone[nauticalTimeZone];
if (offsetHours == invalid) {
System.out.println("Invalid nautical time zone " + nauticalTimeZone);
} else {
ZoneOffset offset = ZoneOffset.ofHours(offsetHours);
OffsetDateTime runDateTime = OffsetDateTime.now(offset);
System.out.println(runDateTime);
}
当我刚才运行这个片段时,输出是:
2020-09-23T20:05:52.451+01:00
您注意到与航海时区 A 相对应的偏移量为 +01:00。请也尝试其他时区。
ArrayIndexOutOfBounsException
就代码而言,如果 char 超出大写字母,它将抛出一个Z
。我把它留给你进行必要的检查。
编辑:
好吧,我使用的日历代码确实接受时区名称。我只需要一份完整的清单。
如果是我,我会借此机会从过时的Calendar
类切换到 java.time。还因为,正如我试图指出的那样,当您使用老式TimeZone
课程时,您随身携带了一般时区所需的一切,而您所需要的只是偏移量。如果您坚持使用Calendar
并喂它 a TimeZone
,那么转换就很容易了。假设您使用的是 ThreeTenABP(见下文):
TimeZone oldfashionedTimeZone = DateTimeUtils.toTimeZone(offset);
System.out.println(oldfashionedTimeZone.getID());
格林威治标准时间+01:00
这是您要求的时区 ID。其他类似,你可以自己构建。如果有疑问,请从那里运行我的代码。当然Calendar
,您不需要 ID,因为您已经获得了TimeZone
对象。
仍然好一点(或者不是那么糟糕),即使您需要一个老式Calendar
的遗留代码,您也不需要处理令人困惑且设计不佳的TimeZone
类。你可以Calendar
从这段代码中得到你的:
Calendar runDate = DateTimeUtils.toGregorianCalendar(
runDateTime.atZoneSameInstant(offset));
如果使用 Java 8(我想也可以通过脱糖使用 Java 8),转换时间会短一些,TimeZone.getTimeZone(offset)
和GregorianCalendar.from(runDateTime.atZoneSameInstant(offset))
,分别。
问题:java.time 不需要 Android API 级别 26 吗?
java.time 在较旧和较新的 Android 设备上都能很好地工作。它只需要至少Java 6。
- 在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 开始)中,现代 API 是内置的。
- 在非 Android Java 6 和 7 中获得 ThreeTen Backport,现代类的后向端口(ThreeTen 用于 JSR 310;请参阅底部的链接)。
- 在较旧的 Android 上,要么使用脱糖,要么使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。在后一种情况下,请确保您从
org.threeten.bp
子包中导入日期和时间类。
链接