如何将Android日历设置为航海或军事时区

问题描述

在我的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))导入日期和时间类。

链接

,

我将使用现代的日期时间API as suggested by Ole V.V.。但是,您似乎想使用旧的日期时间API来执行此操作。我想到的一个解决方案是建立一个Map军事时区,映射到民用时区,例如如here所述。之后,您可以使用军事时区字母(例如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
,

以下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);