问题描述
在我的Android应用中,我在不同时区使用日历。这样我可以调整应用程序的操作 因地制宜。
runDate = Calendar.getInstance(); // will display tides for this date/time
useThisTZ = TimeZone.getTimeZone("US/Pacific");
runDate.setTimeZone(useThisTZ);
我需要适应航海或军事时区。这些是地理的,而不是民间的。我需要确定可以将哪些标识符传递给getTimeZone函数。我尝试使用时区代码“ A”来标识Alpha时区(UTC + 1)。但这将日历放入 GMT区,通常称为祖鲁。
有人知道这些标识符是否可用以及它们可能是什么。
解决方法
java.time中的ZoneOffset和OffsetDateTime
考虑使用java.time(现代Java日期和时间API)进行日期和时间工作。航海(或军事)时区仅是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
您注意到偏移量为+01:00,对应于航海时区A。请尝试其他时区。
如代码所示,如果字符超出大写字母ArrayIndexOutOfBounsException
,它将抛出Z
。我将它留给您以进行必要的检查。
编辑:
好吧,我使用的日历代码确实接受时区名称。我只是 需要一个完整的清单。
如果是我,我将借此机会从过时的Calendar
类切换到java.time。也是因为,正如我试图指出的那样,当您使用老式的TimeZone
类时,如果您所需要的只是一个偏移量,那么您就会随身携带常规时区所需的一切。如果您坚持使用Calendar
并为其输入TimeZone
,那么转换就很容易了。假设您正在使用ThreeTenABP(请参见下文):
TimeZone oldfashionedTimeZone = DateTimeUtils.toTimeZone(offset);
System.out.println(oldfashionedTimeZone.getID());
GMT + 01:00
这是您要求的时区ID。其他的类似,您可以自己构建它们。如有疑问,请运行我的代码以从那里获取它们。要送入Calendar
,您不需要ID,当然,您已经有了TimeZone
对象。
仍然好一些(或不太糟),即使您需要老式的Calendar
作为旧代码,也不需要处理混乱而设计不当的{{1} }类。您可以从以下代码中获得TimeZone
:
Calendar
如果使用Java 8(我想也是通过废话使用Java 8),那么转换的时间要短一些,分别为 Calendar runDate = DateTimeUtils.toGregorianCalendar(
runDateTime.atZoneSameInstant(offset));
和TimeZone.getTimeZone(offset)
。
问题:java.time是否不需要Android API级别26?
java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6 。
- 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
- 在非Android Java 6和7中,获得ThreeTen Backport,这是现代类的backport(JSR 310的ThreeTen;请参见底部的链接)。
- 在较旧的Android上,请使用废除旧书或Android版本的ThreeTen Backport。称为ThreeTenABP。在后一种情况下,请确保使用子包从
GregorianCalendar.from(runDateTime.atZoneSameInstant(offset))
导入日期和时间类。
链接
- List of military time zones
- Oracle tutorial: Date Time解释如何使用java.time。
- Java Specification Request (JSR) 310,其中首先描述了
org.threeten.bp
。 - ThreeTen Backport project,即
java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。 - Java 8+ APIs available through desugaring
- ThreeTenABP,Android版本的ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project,其中有非常详尽的解释。
我将使用现代的日期时间API as suggested by Ole V.V.。但是,您似乎想使用旧的日期时间API来执行此操作。我想到的一个解决方案是建立一个Map
军事时区,映射到民用时区,例如如here所述。之后,您可以使用军事时区字母(例如A
,B
,C
等)在该时区显示日期时间。
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
Map<String,String> tzMap = new HashMap<>();
tzMap.put("A","Europe/Paris");
tzMap.put("B","Europe/Athens");
tzMap.put("C","Europe/Moscow");
// and so on...
// Tests
Calendar calendar = Calendar.getInstance();
displayDateInMilitaryTZ(calendar,tzMap.get("A"));
displayDateInMilitaryTZ(calendar,tzMap.get("B"));
displayDateInMilitaryTZ(calendar,tzMap.get("C"));
}
static void displayDateInMilitaryTZ(Calendar runDate,String tz) {
TimeZone useThisTZ = TimeZone.getTimeZone(tz);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(useThisTZ);
System.out.println(sdf.format(runDate.getTime()));
}
}
输出:
2020-09-23 23:33:16
2020-09-24 00:33:16
2020-09-24 00:33:16
,
以下Ole V.V.指导此Java代码选择并选择正确的时区。 这段代码是说明性的,效率不高,但是有效。
runDate = Calendar.getInstance();
// select timezone geographically by longitude
// degrees West = - longitude 0 to -180
// degrees East = + longitude 0 to +180
// the logic below proceeds eastward from Greenwich
if ((longitude > -7.5) && (longitude < 7.5)) //UTC+0
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+0"); tzName = "Zulu"; }
else if ((longitude > 7.5) && (longitude < 22.5)) //UTC+1
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-1"); tzName = "Alpha"; }
else if ((longitude > 22.5) && (longitude < 37.5)) //UTC+2
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-2"); tzName = "Bravo"; }
else if ((longitude > 37.5) && (longitude < 52.50)) //UTC+3
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-3"); tzName = "Charlie"; }
else if ((longitude > 52.5) && (longitude < 67.5)) //UTC+4
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-4"); tzName = "Delta"; }
else if ((longitude > 67.5) && (longitude < 82.5)) //UTC+5
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-5"); tzName = "Echo"; }
else if ((longitude > 82.5) && (longitude < 97.5)) //UTC+6
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-6"); tzName = "Foxtrot"; }
else if ((longitude > 97.5) && (longitude < 112.5)) //UTC+7
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-7"); tzName = "Golf"; }
else if ((longitude > 112.5) && (longitude < 127.5)) //UTC+8
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-8"); tzName = "Hotel"; }
else if ((longitude > 127.5) && (longitude < 142.5)) //UTC+9
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-9"); tzName = "India"; }
else if ((longitude > 142.5) && (longitude < 157.5)) //UTC+10
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-10"); tzName = "Kilo"; }
else if ((longitude > 157.5) && (longitude < 172.5)) //UTC+11
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-11"); tzName = "Lima"; }
else if ((longitude > 172.5) && (longitude < 180)) //UTC+12 7.5 degrees wide
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT-12"); tzName = "Mike"; }
else if ((longitude > -180) && (longitude < -172.5)) //UTC-12 7.5 degrees wide
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+12"); tzName = "Yankee"; }
else if ((longitude > -172.5) && (longitude < -157.5)) //UTC-11
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+11"); tzName = "X-Ray"; }
else if ((longitude > -157.5) && (longitude < -142.5)) //UTC-10
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+10"); tzName = "Whiskey"; }
else if ((longitude > -142.5) && (longitude < -127.5)) //UTC-9
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+9"); tzName = "Victor"; }
else if ((longitude > -127.5) && (longitude < -112.5)) //UTC-8
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+8"); tzName = "Uniform"; }
else if ((longitude > -112.5) && (longitude < -97.5)) //UTC-7
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+7"); tzName = "Tango"; }
else if ((longitude > -97.5) && (longitude < -82.5)) //UTC-6
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+6"); tzName = "Sierra"; }
else if ((longitude > -82.5) && (longitude < -67.5)) //UTC-5
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+5"); tzName = "Romeo"; }
else if ((longitude > -67.5) && (longitude < -52.5)) //UTC-4
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+4"); tzName = "Quebec"; }
else if ((longitude > -52.5) && (longitude < -37.5)) //UTC-3
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+3"); tzName = "Papa"; }
else if ((longitude > -37.5) && (longitude < -22.5)) //UTC-2
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+2"); tzName = "Oscar"; }
else if ((longitude > -22.5) && (longitude < -7.5)) //UTC-1
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+1"); tzName = "November"; }
else
{ useThisTZ = TimeZone.getTimeZone("Etc/GMT+0"); tzName = "Error"; }
runDate.setTimeZone(useThisTZ);