当前位置: 首页 > 知识库问答 >
问题:

具有夏时制的GregorianCalendar.set()的行为

壤驷鸿祯
2023-03-14

在排除故障时,此方法行为中出现了一些奇怪的东西。

一些国家通过改变时间来节省一些日光。例如,在“欧洲/巴黎”时区,每年3月底时间向前移动1小时,10月底时间向后移动1小时,两者都在凌晨2点至3点之间。例如,这导致日期10月,2016年30日凌晨02:15存在两次。

    null

但是,如果我们想使用.set()为该对象设置分钟,那么+0200偏移信息将被破坏为+0100(“同一时间”,但在时间偏移之后),是否有任何方法可以这样做,因为方法.add()实际上保留了偏移信息?

    // Instantiation
    GregorianCalendar gc1 = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris"));
    gc1.setTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss Z").parse("2016-10-30 02:15:00 +0200"));
    GregorianCalendar gc2 = (GregorianCalendar) gc1.clone();
    
    System.out.println(gc1.getTime()); // Output : Sun Oct 30 02:15:00 CEST 2016 ; OK
    System.out.println(gc2.getTime()); // Output : Sun Oct 30 02:15:00 CEST 2016 ; OK
    
    // Set/add minutes
    gc1.set(Calendar.MINUTE, 10);
    gc2.add(Calendar.MINUTE, 10);
    
    System.out.println(gc1.getTime()); // Output : Sun Oct 30 02:10:00 CET 2016 ; Unexpected
    System.out.println(gc2.getTime()); // Output : Sun Oct 30 02:25:00 CEST 2016 ; OK

共有1个答案

上官英哲
2023-03-14

一个丑陋的替代方案是在设置值后从gc1中减去1小时:

gc1.set(Calendar.MINUTE, 10);
gc1.add(Calendar.HOUR, -1);

结果将是太阳10月30日02:10:00 CEST2016

不幸的是,这似乎是CalendarAPI可用的最佳解决方案set方法的这种行为被报告为一个bug,JDK bug跟踪器中的建议是使用add来“修复”它:

旧的类(datecalendarSimpleDateFormat)有很多问题和设计问题,它们正在被新的API所取代。

如果您使用的是Java8,请考虑使用新的Java.time API。与旧的API相比,它更容易、更少的bug和更少的错误倾向。

如果您使用的是Java<=7,那么可以使用ThreeTen Backport,这是Java8新的日期/时间类的一个很好的Backport。对于Android来说,有ThreeTenABP(这里有更多关于如何使用它的信息)。

下面的代码适用于这两种情况。唯一的区别是:

  • 包名(在Java 8中是Java.time,在ThreeTen Backport(或Android的ThreeTenABP)中是org.ThreeTen.bp),但是类和方法名是相同的。
  • 从/到旧的日历API
  • 的转换

要将GregorianCalendar转换为新API,可以执行以下操作:

// Paris timezone
ZoneId zone = ZoneId.of("Europe/Paris");
// convert GregorianCalendar to ZonedDateTime
ZonedDateTime z = Instant.ofEpochMilli(gc1.getTimeInMillis()).atZone(zone);
ZonedDateTime z = gc1.toInstant().atZone(zone);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
System.out.println(fmt.format(z));

2016年10月30日太阳02:15:00

我使用java.util.locale强制将区域设置为英语,因此月份名称和星期几的格式为Correclty。如果您不指定区域设置,它将使用系统的默认值,并且不能保证始终是英语(默认值也可以在没有通知的情况下更改,即使在运行时也是如此,因此最好总是明确表示您使用的是哪一个)。

使用这个API,您可以轻松地添加或设置分钟数:

// change the minutes to 10
ZonedDateTime z2 = z.withMinute(10);
System.out.println(fmt.format(z2)); // Sun Oct 30 02:10:00 CEST 2016

// add 10 minutes
ZonedDateTime z3 = z.plusMinutes(10);
System.out.println(fmt.format(z3)); // Sun Oct 30 02:25:00 CEST 2016

若要将ZonedDateTime转换回GregorianCalendar,可以执行以下操作:

gc1.setTimeInMillis(z.toInstant().toEpochMilli());

在Java8中,您还可以执行以下操作:

gc1 = GregorianCalendar.from(z);

要解析输入2016-10-30 02:15:00+0200,必须使用其他格式化程序:

DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XX")
    .withZone(ZoneId.of("Europe/Paris"));
ZonedDateTime z = ZonedDateTime.parse("2016-10-30 02:15:00 +0200", parser);

我不得不使用withzone方法设置时区,因为仅仅偏移量+0200不足以确定它(多个时区可以使用相同的偏移量,API无法决定使用哪一个)

 类似资料:
  • 问题内容: 中欧夏令时开始于三月的最后一个星期日。我们将时钟设置为02:00到03:00。如果我在数据库请求中进行时间戳计算会发生什么?比方说,在01:59? 结果是03:00还是02:00? 如果我们将时钟设置为03:00到02:00,那结束了呢? 时间从03:00更改为02:00之后…在02:00会发生什么?是02:59还是01:59? 应该如何处理?最佳实践以及Oracle Database

  • 我有一个使用jquery FullCalendar构建的日历应用程序。用户查看他们本地时区中的事件,我的服务器将它们存储为MySQL中的UTC日期时间。(关于stackoverflow的其他问题表明,这是处理时区的最佳方法。)因此,每次用户在日历上保存或查看事件时都会进行转换。这很好,但我对如何最好地处理夏令时感到困惑。 例如,假设timezone EST(东部标准时间)中的一个用户在不是夏时制的

  • 我在我的webapp中有一些本地化功能(通过Ajax公开),使用户能够在他们自己的时区以及与数据主要相关的实体位置相关的时区中显示一些日期时间信息。 > 输入值25/11/2018 16:00 NZDT(太平洋/奥克兰)=UTC+13 产值应为25/11/2018 14:00 ADT(澳大利亚/悉尼)=UTC+11 如果我从数据库中提取源值,然后进行转换,那么计算工作正常,没有问题 如果我使用表单

  • 在美国,有诸如和这样的时区,在夏时制期间,它们是和,而当夏时制不生效时,它们是和。 那么,既然夏令时从3月的第二个星期日开始,到11月的第一个星期日结束,那么说3月到11月之间的日期是在还是是无效的吗?例如,以下日期在技术上是否不存在? 它不应该是,还是仅仅是,以避免必须指定或?

  • 问题内容: 我正在编写一个处理很多时区并跨越时区的程序。我最常处理的两件事是从“ now”创建一个datetime对象,然后本地化一个朴素的datetime对象。 要从现在开始在太平洋时区创建datetime对象,我目前正在执行此操作(python 2.7.2+) 关于DST,这是否正确?如果没有,我想应该这样做: 我的问题是为什么?谁能告诉我第一个错误而第二个秒正确的情况? 至于我的秒数问题,假

  • 问题内容: 我想检索限制为0,x的特定用户的所有行。 所以我只想问问有没有办法在不调用返回x&的count(id)的方法的情况下检索mysql中的所有行,而不会重载在查询和withour string中完全没有限制的现有函数的重载。 )功能。 因为在内部,当我们选中“显示所有”复选框时,mysql可能会使用它,然后显示taht表的所有行 问题答案: 一个简单的解决方案是将int的最大范围发送到方法