Java时区处理初学者指南

基本时间观念

大多数Web应用程序必须支持不同的时区,而正确处理时区绝非易事。 更糟糕的是,您必须确保各种编程语言(例如,前端JavaScript,中间件中的Java和作为数据存储库的MongoDB)之间的时间戳是一致的。 这篇文章旨在解释绝对时间和相对时间的基本概念。

时代

纪元是绝对时间基准。 大多数编程语言(例如Java,JavaScript,Python)使用Unix纪元(1970年1月1日午夜)来表示给定的时间戳,即自固定时间点引用以来经过的毫秒数。

相对数字时间戳

相对数字时间戳表示为从纪元以来经过的毫秒数。

时区

协调世界时(UTC)是最常见的时间标准。 UTC时区(相当于GMT )表示所有其他时区涉及的时间参考(通过正/负偏移量)。

UTC时区通常称为Zulu时间(Z)或UTC + 0。 日本时区为UTC + 9,而檀香山时区为UTC-10。 在Unix时代(1970年1月1日UTC时区),东京为1970年1月1日,檀香山为1969年12月31日14:00。

ISO 8601

ISO 8601是最广泛的日期/时间表示标准,它使用以下日期/时间格式:

时区 符号
世界标准时间 1970-01-01T00:00:00.000 + 00:00
UTC祖鲁时间 1970-01-01T00:00:00.000 + Z
时雄 1970-01-01T00:00:00.000 + 09:00
火奴鲁鲁 1969-12-31T14:00:00.000-10:00

Java时间基础

java.util.Date

java.util.Date绝对是最常见的时间相关类。 它表示一个固定的时间点,表示为自历元以来经过的相对毫秒数。 java.util.Date是与时区无关的 ,除了toString方法使用本地时区生成String表示形式。

java.util.Calendar

java.util.Calendar既是日期/时间工厂,也是时区感知定时实例。 它是最不友好的Java API类之一,我们可以在以下示例中进行演示:

@Test
public void testTimeZonesWithCalendar() throws ParseException {assertEquals(0L, newCalendarInstanceMillis("GMT").getTimeInMillis());assertEquals(TimeUnit.HOURS.toMillis(-9), newCalendarInstanceMillis("Japan").getTimeInMillis());assertEquals(TimeUnit.HOURS.toMillis(10), newCalendarInstanceMillis("Pacific/Honolulu").getTimeInMillis());Calendar epoch = newCalendarInstanceMillis("GMT");epoch.setTimeZone(TimeZone.getTimeZone("Japan"));assertEquals(TimeUnit.HOURS.toMillis(-9), epoch.getTimeInMillis());
}private Calendar newCalendarInstance(String timeZoneId) {Calendar calendar = new GregorianCalendar();calendar.set(Calendar.YEAR, 1970);calendar.set(Calendar.MONTH, 0);calendar.set(Calendar.DAY_OF_MONTH, 1);calendar.set(Calendar.HOUR_OF_DAY, 0);calendar.set(Calendar.MINUTE, 0);calendar.set(Calendar.SECOND, 0);calendar.set(Calendar.MILLISECOND, 0);calendar.setTimeZone(TimeZone.getTimeZone(timeZoneId));return calendar;
}

在Unix时代(UTC时区),东京时间提前了9个小时,而檀香山却落后了10个小时。

更改日历时区会在偏移时区偏移时保留实际时间。 相对时间戳随日历时区偏移量而变化。

Joda-Time和Java 8 Date Time API只是使java.util.Calandar过时,因此您不必再使用此古怪的API。

org.joda.time.DateTime

Joda-Time旨在通过提供以下服务来修复旧版Date / Time API:

  • 不变和可变的日期结构
  • 流利的API
  • 更好地支持ISO 8601标准

使用Joda-Time,这就是我们之前的测试用例的样子:

@Test
public void testTimeZonesWithDateTime() throws ParseException {assertEquals(0L, newDateTimeMillis("GMT").toDate().getTime());assertEquals(TimeUnit.HOURS.toMillis(-9), newDateTimeMillis("Japan").toDate().getTime());assertEquals(TimeUnit.HOURS.toMillis(10), newDateTimeMillis("Pacific/Honolulu").toDate().getTime());DateTime epoch = newDateTimeMillis("GMT");assertEquals("1970-01-01T00:00:00.000Z", epoch.toString());epoch = epoch.toDateTime(DateTimeZone.forID("Japan"));assertEquals(0, epoch.toDate().getTime());assertEquals("1970-01-01T09:00:00.000+09:00", epoch.toString());MutableDateTime mutableDateTime = epoch.toMutableDateTime();mutableDateTime.setChronology(ISOChronology.getInstance().withZone(DateTimeZone.forID("Japan")));assertEquals("1970-01-01T09:00:00.000+09:00", epoch.toString());
}private DateTime newDateTimeMillis(String timeZoneId) {return new DateTime(DateTimeZone.forID(timeZoneId)).withYear(1970).withMonthOfYear(1).withDayOfMonth(1).withTimeAtStartOfDay();
}

DateTime流利的API比java.util.Calendar#set易于使用。 DateTime是不可变的,但如果适合当前的用例,我们可以轻松地切换到MutableDateTime 。

与我们的Calendar测试用例相比,当更改时区时,相对时间戳不会改变,因此保留了相同的原始时间点。

只是人类的时间感知发生了变化( 1970-01-01T00:00:00.000Z1970-01-01T09:00:00.000 + 09:00指向相同的绝对时间)。

相对时间与绝对时间实例

当支持时区时,基本上有两个主要选择:相对时间戳和绝对时间信息。

相对时间戳

时间戳的数字表示形式(自纪元以来的毫秒数)是相对信息。 该值是针对UTC时代给出的,但是您仍然需要一个时区来正确表示特定区域上的实际时间。

作为一个长值,它是最紧凑的时间表示形式,是交换大量数据时的理想选择。

如果您不知道原始事件的时区,则可能会显示与当前本地时区相对的时间戳,这并不总是可取的。

绝对时间戳

绝对时间戳包含相对时间以及时区信息。 在其ISO 8601字符串表示中表示时间戳是很常见的。

与数字形式(64位长)相比,字符串表示的紧凑性较低,它最多可包含25个字符(UTF-8编码为200位)。

ISO 8601在XML文件中非常常见,因为XML模式使用的是受ISO 8601标准启发的词汇格式 。

当我们想针对原始时区重构时间实例时,绝对时间表示会更加方便。 电子邮件客户端可能希望使用发件人的时区显示电子邮件创建日期,而这只能使用绝对时间戳来实现。

谜题

以下练习旨在说明使用古老的java.text.DateFormat实用程序正确处理符合ISO 8601的日期/时间结构有多么困难。

java.text.SimpleDateFormat

首先,我们将使用以下测试逻辑来测试java.text.SimpleDateFormat解析功能:

/*** DateFormat parsing utility* @param pattern date/time pattern* @param dateTimeString date/time string value* @param expectedNumericTimestamp expected millis since epoch */
private void dateFormatParse(String pattern, String dateTimeString, long expectedNumericTimestamp) {try {Date utcDate = new SimpleDateFormat(pattern).parse(dateTimeString);if(expectedNumericTimestamp != utcDate.getTime()) {LOGGER.warn("Pattern: {}, date: {} actual epoch {} while expected epoch: {}", new Object[]{pattern, dateTimeString, utcDate.getTime(), expectedNumericTimestamp});}} catch (ParseException e) {LOGGER.warn("Pattern: {}, date: {} threw {}", new Object[]{pattern, dateTimeString, e.getClass().getSimpleName()});}
}

用例1

让我们看看各种ISO 8601模式如何针对第一个解析器表现:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "1970-01-01T00:00:00.200Z", 200L);

产生以下结果:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSS'Z', date: 1970-01-01T00:00:00.200Z actual epoch -7199800 while expected epoch: 200

此模式不符合ISO 8601。 单引号字符是一个转义序列,因此最后的“ Z”符号不会被视为时间指令(例如Zulu时间)。 解析后,我们将仅获取本地时区的Date参考。

该测试是使用我当前的系统默认欧洲/雅典时区运行的,截至撰写本文时,它比UTC提前两个小时。

用例2

根据java.util.SimpleDateFormat文档,以下模式: yyyy-MM-dd'T'HH:mm:ss.SSSZ应该匹配ISO 8601日期/时间字符串值:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200Z", 200L);

但是相反,我们得到了以下异常:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSSZ, date: 1970-01-01T00:00:00.200Z threw ParseException

因此,此模式似乎无法解析Zulu时间UTC字符串值。

用例3

以下模式对于显式偏移量非常适用:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200+0000", 200L);

用例4

此模式还与其他时区偏移量兼容:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "1970-01-01T00:00:00.200+0100", 200L - 1000 * 60 * 60);

用例5

为了匹配祖鲁语时间符号,我们需要使用以下模式:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "1970-01-01T00:00:00.200Z", 200L);

用例6

不幸的是,最后一个模式与明确的时区偏移量不兼容:

dateFormatParse("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "1970-01-01T00:00:00.200+0000", 200L);

最后出现以下异常:

Pattern: yyyy-MM-dd'T'HH:mm:ss.SSSXXX, date: 1970-01-01T00:00:00.200+0000 threw ParseException

org.joda.time.DateTime

java.text.SimpleDateFormat相反, Joda-Time与任何ISO 8601模式兼容。 以下测试用例将用于即将推出的测试用例:

/*** Joda-Time parsing utility* @param dateTimeString date/time string value* @param expectedNumericTimestamp expected millis since epoch*/
private void jodaTimeParse(String dateTimeString, long expectedNumericTimestamp) {Date utcDate = DateTime.parse(dateTimeString).toDate();if(expectedNumericTimestamp != utcDate.getTime()) {LOGGER.warn("date: {} actual epoch {} while expected epoch: {}", new Object[]{dateTimeString, utcDate.getTime(), expectedNumericTimestamp});}
}

Joda-Time与所有标准ISO 8601日期/时间格式兼容:

jodaTimeParse("1970-01-01T00:00:00.200Z", 200L);
jodaTimeParse("1970-01-01T00:00:00.200+0000", 200L);
jodaTimeParse("1970-01-01T00:00:00.200+0100", 200L - 1000 * 60 * 60);

结论

如您所见,古老的Java Date / Time实用程序不容易使用。 Joda-Time是更好的选择,提供更好的时间处理功能。

如果您碰巧使用Java 8,则值得切换到Java 8 Date / Time API ,该API是从头开始设计的,但深受Joda-Time启发 。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2014/11/a-beginners-guide-to-java-time-zone-handling.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android工具HierarchyViewer 代码导读(3) -- 后台代码

在上文中&#xff0c;我们讲解了如何把HierarchyViewer的项目导入到Eclipse中&#xff0c;以便更高效阅读代码。本文将讲解HierarchyViewer的后台代码&#xff0c;建议大家可以先阅读<Android工具HierarchyViewer代码导读(1) -- 功能实现演示>一文, 其中的代码演示了Hier…

Extjs 数据代理

Ext.data.proxy.Proxy 代理类的根类 客户端代理&#xff1a; 1.LocalStorageProxy&#xff1a;将数据存储在localStorage中&#xff0c;此种方式可以持久的将数据存储在客户端 要使用代理&#xff0c;我们首先要有一个数据模型类&#xff0c;我们定义一个简单的Person类&…

WildFly 8.2.0.Final版本–更改的快速概述

自从我上次在此博客上写作以来已经有一段时间了。 尽管我有一些我想写博客的主题&#xff0c;但我只是没有足够的时间来做。 在看到JBoss社区成员之一的邮件后&#xff0c;我终于决定今天写这篇文章&#xff0c;检查为什么最近这里没有任何更新&#xff08;感谢检查&#xff0c…

Ph.D Grind 阅读感想 By 张雄

#Ph.D Grind是Xin Zou老师推荐的一本书&#xff0c;邮件里本来说是要在三周内读完的&#xff0c;不料看了个Preface之后再也放不下&#xff0c;最终一口气地看完了。 #看完之后很有感触&#xff0c;之前也阅读过一本讲如何规划研究生涯的书&#xff0c;但是那是一本从教授的角度…

VUE 入门基础(3)

三&#xff0c;模板语法 Vue将模板编译成虚拟DOM渲染函数&#xff0c;结合响应系统&#xff0c;在应用状态改变时&#xff0c;vue能够智能地计算出重新渲染组件的最小代价并DOM操作上。 插值&#xff0c;文本 数据绑定常见的形式就是使用“Mustache”语法&#xff08;双大括号&…

SVG 使用

SVG即Scalable Vector Graphics可缩放矢量图形&#xff0c;使用XML格式定义图形&#xff0c; 主要优势在于可缩放的同时不会影响图片的质量。 SVG 在html 中常用的方法 1.使用<img>元素来嵌入SVG图像 <img src”http://www.w3school.com.cn/svg/rect1.svg” width”…

超越JAX-RS规范:Apache CXF搜索扩展

在今天的帖子中&#xff0c;我们将超越JAX-RS 2.0规范&#xff0c;并探索Apache CXF &#xff08;流行的JAX-RS 2.0实现之一&#xff09;为REST服务和API开发人员提供的有用扩展。 特别是&#xff0c;我们将讨论使用OData 2.0查询过滤器子集的搜索扩展。 简而言之&#xff0c;…

阿里巴巴使命、愿景、价值观、绩效管理中的六大价值观、

阿里巴巴的使命 让天下没有难做的生意 阿里巴巴的愿景 分享数据的第一平台幸福指数最高的企业“活102年”阿里巴巴的价值观 我们坚持“客户第一、员工第二、股东第三”。 阿里巴巴的六脉神剑&#xff08;绩效管理中的六大价值观&#xff09; 公司的“六脉神剑” 客户第一&#…

Angularjs基础(十)

ng-blur       描述&#xff1a;规定blur 事件的行为       实例&#xff1a;当输入框失去焦点的(onblur)时执行表达式&#xff1a;         <input ng-blur"count count 1" ng-init"count0"/>         <h1>{{co…

在命令行上操作JAR,WAR和EAR

尽管Java IDE和许多图形工具使查看和操作Java归档文件&#xff08;JAR&#xff0c;WAR和EAR&#xff09;文件的内容比以往更加容易&#xff0c;但有时我还是更喜欢使用命令行jar命令来完成这些任务。 当我必须重复做某事或作为脚本的一部分来做时&#xff0c;尤其如此。 在本文…

C#语言使用多态(接口与override) ——帮您剔除对面向对象多态性的疑惑

多态是面向对象编程中三大机制之一,其原理建立在"从父类继承而来的子类可以转换为其父类"这个规则之上,换句话说,能用父类的地方,就能用该类的子类.当从父类派生了很多子类时,由于每个子类都有其不同的代码实现,所以当用父类来引用这些子类时,同样的操作而可以表现出…

Java如何以及为什么使用Unsafe?

总览 sun.misc.Unsafe至少在Java 1.4&#xff08;2004&#xff09;中就已经存在于Java中。 在Java 9中&#xff0c;不安全将与许多其他供内部使用的类一起隐藏。 以提高JVM的可维护性。 尽管仍不确定究竟将取代Unsafe到底是什么&#xff0c;但我怀疑将取代Unsafe不仅仅是一件事…

Angularjs基础(三)

AngularJS ng-model 指令     ng-model 指令用于绑定应用程序数据到HTML 控制器&#xff08;input,select,textarea&#xff09;的值ng-model指令     ng-model指令可以将输入域的值与AngularJS 创建的变量绑定。       实例&#xff1a;         <di…

ASP.NET MVC的生命周期与网址路由

网址路由&#xff08;Routing&#xff09;在ASP.NET MVC中有两个主要用途&#xff0c;一个用途是匹配通过浏览器传来的HTTP请求&#xff0c;另一个用途则是将适当的网址返回浏览器。 首先我们来看下第一个用途&#xff0c;也就是匹配通过浏览器传来的HTTP请求。 客户端对ASP.NE…

ecshop分页类assign_pager分析和扩展

ecshop分页类assign_pager分析和扩展,我们前面的文章中介绍过ecshop ajax分页&#xff0c;他的基础都是简单单一的分页。如果我们要在ecshop分页里面传入自己的参数&#xff0c;你就必须对ecshop的assign_pager分页函数进行系统的认识和分析。 首先我们看category.php的ecshop分…

NEC css规范

CSS规范 - 分类方法 SS文件的分类和引用顺序 通常&#xff0c;一个项目我们只引用一个CSS&#xff0c;但是对于较大的项目&#xff0c;我们需要把CSS文件进行分类。 我们按照CSS的性质和用途&#xff0c;将CSS文件分成“公共型样式”、“特殊型样式”、“皮肤型样式”&#…

JDK 8中的流驱动的集合功能

这篇文章介绍了JDK 8的应用–引入了带有集合的 流 &#xff0c;以更简洁地完成通常需要的与集合相关的功能。 在此过程中&#xff0c;将演示并简要说明使用Java Streams的几个关键方面。 请注意&#xff0c;尽管JDK 8 Streams通过并行化支持提供了潜在的性能优势&#xff0c;但…

知识汇集

Oracle中锁介绍&#xff1a;http://space.itpub.net/26961876/viewspace-731300转载于:https://www.cnblogs.com/yaohonv/archive/2012/08/18/tech-col.html

jquery mobile 移动web(5)

有序列表   <div data-role"content">     <ol data-role"listview" data-theme"g">       <li><a href"#"> List 1</a></li>       <li><a href"#"> L…

cjmx:JConsole的命令行版本

当监视正在运行的Java应用程序时&#xff0c;JConsole是一个很好的工具。 但是&#xff0c;当无法使用JConsole直接连接到JVM&#xff08;例如&#xff0c;由于网络限制&#xff09;并且无法进行SSH隧道传输时&#xff0c;那么拥有命令行版本的JConsole会很棒。 jcmx是JConsol…