如何以数字格式获取当前日期和时区

3

我想以这种格式打印当前日期2017/06/05>年/月/日,并在其旁边打印当前时区的格式为+3

我使用了以下代码

String DateToday  = DateFormat.getDateInstance().format(new Date());
String TZtoday = DateFormat.getTimeInstance().getTimeZone().getDisplayName();
txt.setText(DateToday + " | "+ TZtoday );

但是,它显示的是这样的:

2017年6月5日 | 阿拉伯标准时间

我希望它像这样:

2017/06/05 | +3


1
如果偏移量不是整小时,您希望发生什么? - Ole V.V.
1
例如,假设“Asia/Kathmandu”的区域偏移量为+05:45。应该如何打印?如果答案是“不关心”,我建议在这种情况下抛出异常。 - Ole V.V.
嗯,你是对的,我想是这样的..! - Alaa AbuZarifa
实际上,我建议始终使用小时和分钟以及可选的冒号打印您与UTC偏移量。因此,应该使用+03:00而不是+3。这似乎是规范化的格式。我已经看到多个库和协议只接受该格式。此外,该格式对人类读者更易识别。 - Basil Bourque
@SamZar 但是你的API是否也支持±HH:MM的完整格式?除了我在上面的评论中提供的实用建议之外,使用完整格式的另一个原因是这是java.time中ZoneOffset类使用的默认格式。 - Basil Bourque
显示剩余3条评论
4个回答

3
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd | X");
System.out.println(sdf.format(new Date()));

gets close, 但是时区前面会打印一个零:

2017/06/05 | +03

如果需要,您可以从时区中删除前导零:

我认为这样做是可行的。
SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat zone = new SimpleDateFormat("ZZZZZ"); // = +03:00
String tz = zone.format(new Date()).split(":")[0]
    .replaceAll("^(\\+|-)0", "$1"); // = +3
System.out.println(sdf.format(new Date()) + " | " + tz);

这将会给出:

2017/06/05 | +3

@Sam 根据Android文档,它也支持Android。你在用什么? - David Conrad
我正在使用Android,但是我进行了一些挖掘,X可以被替换为ZZZZZ,但它会显示为+03:00 - Alaa AbuZarifa
1
我已经做到了,现在它运行得很好。我会编辑你的答案,展示我所做的更改以达到我想要的结果。非常感谢你的帮助。 - Alaa AbuZarifa
2
@Sam,你不应该使用StringTokenizer,它已经过时且存在问题。你应该使用String.split()来替代它。另外,你也不应该使用replaceAll("0", ""),因为时区可能是+10或者-10,所以你需要确保只替换前导的零。 - David Conrad
哦,我明白了。好的,谢谢你的提示。 - Alaa AbuZarifa
显示剩余4条评论

3
我知道你在使用Android,也知道Android内置的类如DateFormatSimpleDateFormatDateCalendar早已过时。但我仍然建议,如果你需要处理日期、时间或时区相关的内容,应该严肃考虑放弃这些类并转而使用它们的现代替代品。这需要你使用ThreeTenABP。然后,我建议你按照以下方式操作:
    ZonedDateTime date = ZonedDateTime.now(ZoneId.systemDefault());
    String tzToday = date.format(DateTimeFormatter.ofPattern("uuuu/MM/dd | x"));
    tzToday = tzToday.replaceFirst("(?<=\\+|-)0", "");

我的电脑上的结果:

2017/06/05 | +2

亚洲/加德满都时区的结果可能不完全符合您的要求:

2017/06/05 | +545

你可能认为我的代码片段并不比使用旧类更有优势。但是我可以告诉你,在许多情况下,旧类会给你带来负面的惊喜,所以你越早开始使用新类,就越好。或者你可能会在格式模式字符串中打错字,例如,注意到旧类产生了一个令人惊讶的结果,而没有任何错误的迹象,而较新的类将尝试组合一个有意义的错误消息。
另一个要注意的细节是,我只读取系统时钟和JVM的时区一次(将时区传递回now(),以确保它用于时间)。你问题中的代码首先格式化日期,然后读取时区。如果有人在两者之间更改时区,结果将不一致。这很不可能,但这也意味着很少有人能够弄清楚发生了什么。
正则表达式中的(?<=\\+|-)是一个回顾:我用空字符串替换了前面带有+-0。我徒劳地搜索了一种将区域偏移量格式化为您所需的+3的方法,因此我求助于replaceFirst方法。
更多链接:如何在Android项目中使用ThreeTenABP

2
我之前是Java开发人员,后来转向Android,所以我会确保我的下一个时间/日期实现将使用ThreeTenABP,非常感谢您的建议! - Alaa AbuZarifa

1

补充@Ole V.V.的答案,我发现使用ThreeTenABP还有另一种方法可以实现,但不需要使用正则表达式替换(虽然我认为它并不比较简单,下面会说到)。

使用DateTimeFormatterBuilder,您可以使用一个将偏移量的值映射到StringHashMap。因此,对于整个偏移小时(例如+03:00),您可以将相应的值映射到字符串+3等。

唯一的问题是,ZoneOffset 只有精度。最小和最大值分别为 UTC-18:00:00UTC+18:00:00。因此,所有可能的值都是 UTC-18:00:00UTC-17:59:59UTC-17:59:58 等等。并且格式化程序要求将所有可能的值映射(否则它会显示偏移量的秒值),因此该映射将有超过 120K 个条目!
为构建这个映射,我进行了两个循环:
  • 第一个循环将整个小时的偏移量映射到相应的数字(例如,将 +01:00 映射到 +1,将 -02:00 映射到 -2,以此类推)
  • 第二个循环映射所有其他值(它们保持不变):
    • 大于或等于 10 的整数小时(如 +10:00
    • 非整数小时(如 +05:30
创建格式化程序的代码如下:
// builds a hashmap to map all offset values
Map<Long, String> map = new HashMap<>();

// First loop: Map whole hours from 1 to 9 and from -9 to -1
// So a "+01:00" offset is displayed as "+1"
for (int i = 1; i <= 9; i++) {
    long seconds = ZoneOffset.ofHours(i).getTotalSeconds();
    // 1 hour offset maps to "+1" and so on
    map.put(seconds, "+" + i);
    // -1 hour offset maps to "-1" and so on
    map.put(-seconds, "-" + i);
}

// second loop: Need to map all other possible values for not-whole hours
// offsets like "+10:00" and "+01:30" are not changed
int minOffset = ZoneOffset.MIN.getTotalSeconds();
int maxOffset = ZoneOffset.MAX.getTotalSeconds();
for (long i = minOffset; i <= maxOffset; i++) {
    // the map already contains whole hours, don't need to overwrite the values
    if (!map.containsKey(i)) {
        // uses default String representation (like "+05:30")
        map.put(i, ZoneOffset.ofTotalSeconds((int) i).getId());
    }
}

// create the formatter
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                // year/month/day and the "|"
                .appendPattern("uuuu/MM/dd | ")
                // use the map of custom values (offset will use the values in the map)
                .appendText(ChronoField.OFFSET_SECONDS, map)
                // create formatter
                .toFormatter();

一些测试:

LocalDateTime dt = LocalDateTime.of(2017, 5, 1, 10, 0);
ZonedDateTime z = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo")); // UTC-03:00
System.out.println(formatter.format(z)); // 2017/05/01 | -3

// just picking some timezone in Arabia Stardard Time
// (there are more than one timezone in AST: https://en.wikipedia.org/wiki/UTC%2B03:00#Arabia_Standard_Time)
// I don't know in which one you are
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Qatar")); // UTC+03:00
System.out.println(formatter.format(z)); // 2017/05/01 | +3

// 2-digit offset
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Vladivostok")); // UTC+10:00
System.out.println(formatter.format(z)); // 2017/05/01 | +10:00

// not whole hour
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Kathmandu")); // UTC+05:45
System.out.println(formatter.format(z)); // 2017/05/01 | +05:45

输出结果是:

2017年5月1日 | -3
2017年5月1日 | +3
2017年5月1日 | +10:00
2017年5月1日 | +05:45


注意:

  • 我不知道使用一个包含120K条目的映射表是否比使用正则表达式更好(这取决于您的决定)。这种方法会创建一个大型映射表,但至少不需要输出后处理(不确定这是否是一个合理的权衡)。
  • 如果您想要显示整小时偏移量 >= 10 为 +10+11等,请将第一个 for 循环更改为 for (int i = 1; i <= maxOffsetYouWant; i++) - 只是提醒 maxOffsetYouWant 的最大可能值为18

1
哇,这是一个非常有趣的解决问题的方法,谢谢你,尽管对我来说理解起来更加复杂。所以现在我会坚持使用旧的Java类解决方案,因为我的应用程序的受众不会超过+3个时区..!但还是感谢你的帮助。 - Alaa AbuZarifa
实际上,我必须承认这个特定案例的解决方案比我希望的更加复杂。但是除此之外,你真的应该花些时间学习新的API。它比DateCalendar好得多,而且真的值得学习。 - user7605325
1
我一定会的 ;) - Alaa AbuZarifa

0
使用以下逻辑来获取所需的日期格式。
    public static void main(String[] args) {

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 1);
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(cal.getTime());
// Output "Wed Sep 26 14:23:28 EST 2012"

        String formatted = format1.format(cal.getTime());
        String formattedmain=formatted.replace("-","/");
        System.out.println(formatted);
        // Output "2012-09-26"
        System.out.println(formattedmain);

//Output :- 2017/06/06


    }

2
那个格式不对。OP要求斜杠而不是破折号,还有时区偏移量。 - David Conrad
等一下,我会根据需要更改斜杠,这不是什么大问题。 - Amit Gujarathi
对于偏移量,将要添加的时间量添加到此处:cal.add(Calendar.DATE, 1); 此时1表示偏移量为1天。 - Amit Gujarathi
3
为什么要使用“替换”而不是一开始就改用斜杠的格式呢?而且你还没有解决时区的问题...... - David Conrad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接