热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java–从SimpleTimeZone获取ZoneId

使用Java我有一个带有GMT偏移量的SimpleTimeZone实例和来自遗留系统的夏令时信息.我想检索ZoneId以便能够使用Java8时间API.实际上,toZoneId返回

使用Java我有一个带有GMT偏移量的SimpleTimeZone实例和来自遗留系统的夏令时信息.

我想检索ZoneId以便能够使用Java 8时间API.

实际上,toZoneId返回没有夏令时信息的ZoneId

SimpleTimeZone stz = new SimpleTimeZone( 2 * 60 * 60 * 1000, "GMT", Calendar.JANUARY,1,1,1, Calendar.FEBRUARY,1,1,1, 1 * 60 * 60 * 1000);
stz.toZoneId();

解决方法:

首先,当你这样做时:

SimpleTimeZone stz = new SimpleTimeZone(2 * 60 * 60 * 1000, "GMT", Calendar.JANUARY, 1, 1, 1, Calendar.FEBRUARY, 1, 1, 1, 1 * 60 * 60 * 1000);

您正在创建ID等于“GMT”的时区.当你调用ZoneId()时,它只调用ZoneId.of(“GMT”)(它使用与参数相同的ID,如@Ole V.V.’s answer中已经说明的那样).然后,ZoneId类加载在JVM中配置的任何夏令时信息(它不保留与原始SimpleTimeZone对象相同的规则).

并且根据ZoneId javadoc:如果区域ID等于’GMT’,’UTC’或’UT’,则结果是具有相同ID的ZoneId,并且规则等同于ZoneOffset.UTC.而ZoneOffset.UTC根本没有DST规则.

因此,如果你想拥有一个具有相同DST规则的ZoneId实例,你必须手工创建它们(我不知道它是可能的,但实际上是,请查看下面的内容).

您的DST规则

查看SimpleTimeZone javadoc,您创建的实例具有以下规则(根据我的测试):

>标准偏差为02:00(提前2小时UTC / GMT)
> DST从1月的第一个星期日开始(请查看javadoc以获取更多详细信息),午夜后1毫秒(您将1作为开始和结束时间)
>在DST时,偏移更改为03:00
> DST在2月的第一个星期日结束,午夜后1毫秒(然后偏移回到02:00)

实际上,根据javadoc,你应该在dayOfWeek参数中传递一个负数以这种方式工作,所以应该像这样创建时区:

stz = new SimpleTimeZone(2 * 60 * 60 * 1000, "GMT", Calendar.JANUARY, 1, -Calendar.SUNDAY, 1, Calendar.FEBRUARY, 1, -Calendar.SUNDAY, 1, 1 * 60 * 60 * 1000);

但在我的测试中,两者都以相同的方式工作(也许它修复了非负值).无论如何,我做了一些测试只是为了检查这些规则.首先,我使用您的自定义时区创建了一个SimpleDateFormat:

TimeZone t = TimeZone.getTimeZone("America/Sao_Paulo");
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss Z");
sdf.setTimeZone(t);

然后我测试了边界日期(在夏令时开始和结束之前):

// starts at 01/01/2017 (first Sunday of January)
ZonedDateTime z = ZonedDateTime.of(2017, 1, 1, 0, 0, 0, 0, ZoneOffset.ofHours(2));
// 01/01/2017 00:00:00 +0200 (not in DST yet, offset is still +02)
System.out.println(sdf.format(new Date(z.toInstant().toEpochMilli())));
// 01/01/2017 01:01:00 +0300 (DST starts, offset changed to +03)
System.out.println(sdf.format(new Date(z.plusMinutes(1).toInstant().toEpochMilli())));
// ends at 05/02/2017 (first Sunday of February)
z = ZonedDateTime.of(2017, 2, 5, 0, 0, 0, 0, ZoneOffset.ofHours(3));
// 05/02/2017 00:00:00 +0300 (in DST yet, offset is still +03)
System.out.println(sdf.format(new Date(z.toInstant().toEpochMilli())));
// 04/02/2017 23:01:00 +0200 (DST ends, offset changed to +02 - clock moves back 1 hour: from midnight to 11 PM of previous day)
System.out.println(sdf.format(new Date(z.plusMinutes(1).toInstant().toEpochMilli())));

输出是:


01/01/2017 00:00:00 +0200

01/01/2017 01:01:00 +0300

05/02/2017 00:00:00 +0300

04/02/2017 23:01:00 +0200


因此,它遵循上述规则(在01/01/2017午夜偏移为0200,一分钟后它在DST(偏移现在是0300;相反发生在05/02(DST结束,偏移返回到0200) ).

使用上述规则创建ZoneId

不幸的是,你不能扩展ZoneId和ZoneOffset,你也不能改变它们,因为它们都是不可变的.但是可以创建自定义规则并将它们分配给新的ZoneId.

它似乎没有办法直接将规则从SimpleTimeZone导出到ZoneId,因此您必须手动创建它们.

首先,我们需要创建一个ZoneRules,这个类包含偏移何时以及如何变化的所有规则.为了创建它,我们需要构建一个包含2个类的列表:

> ZoneOffsetTransition:定义偏移更改的特定日期.必须至少有一个使其工作(使用空列表失败)
> ZoneOffsetTransitionRule:定义一般规则,不限于特定日期(例如“1月第一个星期日偏移从X变为Y”).我们必须有2个规则(一个用于DST启动,另一个用于DST结束)

那么,让我们创建它们:

// offsets (standard and DST)
ZoneOffset standardOffset = ZoneOffset.ofHours(2);
ZoneOffset dstOffset = ZoneOffset.ofHours(3);
// you need to create at least one transition (using a date in the very past to not interfere with the transition rules)
LocalDateTime startDate = LocalDateTime.MIN;
LocalDateTime endDate = LocalDateTime.MIN.plusDays(1);
// DST transitions (date when it happens, offset before and offset after) - you need to create at least one
ZoneOffsetTransition start = ZoneOffsetTransition.of(startDate, standardOffset, dstOffset);
ZoneOffsetTransition end = ZoneOffsetTransition.of(endDate, dstOffset, standardOffset);
// create list of transitions (to be used in ZoneRules creation)
List transitiOns= Arrays.asList(start, end);
// a time to represent the first millisecond after midnight
LocalTime firstMillisecOnd= LocalTime.of(0, 0, 0, 1000000);
// DST start rule: first Sunday of January, 1 millisecond after midnight
ZoneOffsetTransitionRule startRule = ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.SUNDAY, firstMillisecond, false, TimeDefinition.WALL,
standardOffset, standardOffset, dstOffset);
// DST end rule: first Sunday of February, 1 millisecond after midnight
ZoneOffsetTransitionRule endRule = ZoneOffsetTransitionRule.of(Month.FEBRUARY, 1, DayOfWeek.SUNDAY, firstMillisecond, false, TimeDefinition.WALL,
standardOffset, dstOffset, standardOffset);
// list of transition rules
List transitiOnRules= Arrays.asList(startRule, endRule);
// create the ZoneRules instance (it'll be set on the timezone)
ZoneRules rules = ZoneRules.of(start.getOffsetAfter(), end.getOffsetAfter(), transitions, transitions, transitionRules);

我无法创建一个在午夜后的第一个毫秒开始的ZoneOffsetTransition(它们实际上恰好在午夜开始),因为秒的分数必须为零(如果不是,则ZoneOffsetTransition.of()抛出异常).所以,我决定在过去设置一个日期(LocalDateTime.MIN),以免干扰规则.

但是ZoneOffsetTransitionRule实例的工作方式与预期完全一样(DST在午夜之后开始和结束1毫秒,就像SimpleTimeZone实例一样).

现在我们必须将此ZoneRules设置为时区.正如我所说,ZoneId无法扩展(构造函数不公开),ZoneOffset也不是(最终类).我最初认为设置规则的唯一方法是创建一个实例并使用反射设置它,但实际上API提供了一种通过扩展java.time.zone.ZoneRulesProvider类来创建自定义ZoneId的方法:

// new provider for my custom zone id's
public class CustomZoneRulesProvider extends ZoneRulesProvider {
@Override
protected Set provideZoneIds() {
// returns only one ID
return Collections.singleton("MyNewTimezone");
}
@Override
protected ZoneRules provideRules(String zoneId, boolean forCaching) {
// returns the ZoneRules for the custom timezone
if ("MyNewTimezone".equals(zoneId)) {
ZoneRules rules = // create the ZoneRules as above
return rules;
}
return null;
}
// returns a map with the ZoneRules, check javadoc for more details
@Override
protected NavigableMap provideVersions(String zoneId) {
TreeMap map = new TreeMap<>();
ZoneRules rules = getRules(zoneId, false);
if (rules != null) {
map.put(zoneId, rules);
}
return map;
}
}

请记住,您不应将ID设置为“GMT”,“UTC”或任何有效ID(您可以使用ZoneId.getAvailableZoneIds()检查所有现有ID). “GMT”和“UTC”是API内部使用的特殊名称,可能会导致意外行为.所以选择一个不存在的名称 – 我选择了MyNewTimezone(没有空格,否则它会失败,因为如果名称中有空格,ZoneRegion会抛出异常).

让我们来测试这个新的时区.必须使用ZoneRulesProvider.registerProvider方法注册新类:

// register the new zonerules provider
ZoneRulesProvider.registerProvider(new CustomZoneRulesProvider());
// create my custom zone
ZoneId customZOne= ZoneId.of("MyNewTimezone");
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss Z");
// starts at 01/01/2017 (first Sunday of January)
ZonedDateTime z = ZonedDateTime.of(2017, 1, 1, 0, 0, 0, 0, customZone);
// 01/01/2017 00:00:00 +0200 (not in DST yet, offset is still +02)
System.out.println(z.format(fmt));
// 01/01/2017 01:01:00 +0300 (DST starts, offset changed to +03)
System.out.println(z.plusMinutes(1).format(fmt));
// ends at 05/02/2017 (first Sunday of February)
z = ZonedDateTime.of(2017, 2, 5, 0, 0, 0, 0, customZone);
// 05/02/2017 00:00:00 +0300 (in DST yet, offset is still +03)
System.out.println(z.format(fmt));
// 04/02/2017 23:01:00 +0200 (DST ends, offset changed to +02 - clock moves back 1 hour: from midnight to 11 PM of previous day)
System.out.println(z.plusMinutes(1).format(fmt));

输出是相同的(因此规则与SimpleTimeZone使用的相同):


01/01/2017 00:00:00 +0200

01/01/2017 01:01:00 +0300

05/02/2017 00:00:00 +0300

04/02/2017 23:01:00 +0200


笔记:

> CustomZoneRulesProvider只创建一个新的ZoneId,但当然您可以扩展它以创建更多. Check the javadoc了解有关如何更正实施您自己的规则提供程序的更多详细信息.
>在创建ZoneRules之前,您必须确切地检查自定义时区的规则.一种方法是使用SimpleTimeZone.toString()方法(返回对象的内部状态)并读取javadoc以了解参数如何影响规则.
>我没有测试足够的案例来了解SimpleTimeZone和ZoneId规则是否有某种特定日期以不同的方式表现.我已经测试了不同年份的一些日期,似乎工作正常.


推荐阅读
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
author-avatar
书友58684991
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有