0

在我的 Android 应用程序中,我使用不同时区的日历。这样我就可以根据当地情况调整应用程序的运行。

runDate = Calendar.getInstance(); // will display tides for this date/time
useThisTZ = TimeZone.getTimeZone("US/Pacific");
runDate.setTimeZone(useThisTZ);

我需要适应航海或军事时区。这些是地理的而不是民事的。我需要确定可以将哪些标识符传递给 getTimeZone 函数来执行此操作。我尝试了标识 Alpha 时区 (UTC + 1) 的时区代码“A”。然而,这将日历置于通常称为 Zulu 的 GMT 区域。

有谁知道这些标识符是否可用以及它们可能是什么。

4

3 回答 3

1

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子包中导入日期和时间类。

链接

于 2020-09-23T19:06:58.287 回答
0

我会使用Ole VV 建议的现代日期时间 API 。但是,您似乎想使用旧的日期时间 API 来执行此操作。我想到的一个解决方案是建立一个Map映射到民用时区的军事时区,例如这里提到的。之后,您可以使用军用时区字母(例如ABC)来显示该时区的日期时间。

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
于 2020-09-23T21:38:26.957 回答
0

遵循 Ole VV 指导,此 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);
于 2020-09-25T11:42:10.207 回答