0. 前言:

时间格式:

//世界标准时间,其中T表示时分秒的开始(或者日期与时间的间隔),Z表示这是一个世界标准时间
2017-12-13T01:47:07.081Z//本地时间,也叫不含时区信息的时间,末尾没有Z
2017-12-13T09:47:07.153//含有时区信息的时间,+08:00表示该时间是由世界标准时间加了8个小时得到的,[Asia/Shanghai]表示时区
2017-12-13T09:47:07.153+08:00[Asia/Shanghai]

其中最难理解的是本地时间,2017-12-13T09:47:07.153时间本身是不含有时区信息的,但是“本地”这两个字含有时间信息。所以我认为这个翻译并不好,不应该叫做“本地时间”,应该直接翻译为“不含时区信息的时间”。

协调世界时,又称世界统一时间、世界标准时间、国际协调时间。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
世界时UT即格林尼治平太阳时间,是指格林尼治所在地的标准时间,也是表示地球自转速率的一种形式。以地球自转为基础的时间计量系统。

1. 先来看Java8:

表示时间的主要有4类String、Instant、LocalDateTime、ZonedDateTime

String是格式化的时间,Instant是时间戳,LocalDateTime是不含时区信息的时间,ZonedDateTime是含有时区信息的时间。

1.1 它们之间的关系是:

1.1.1 String与LocalDateTime是等价的

符合格式的String可以直接解析为LocalDateTime,如下:

System.out.println(LocalDateTime.parse("2017-12-13 10:10:10",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));输出:
2017-12-13T10:10:10

辨析LocalDateTime最好的办法就是不要把它当成“本地时间”,它就是“不含时区信息的时间”。它只是存储了年月日时分秒,没有存储任何时区信息,具体表示哪里的时间全靠输入和输出时进行解释。与String完全等价,本质上是对String的解析,只是年月日时分秒格式化的存储到了对象当中,方便取用。

1.1.2 Instant与ZonedDateTime是等价的

Instant是时间戳,是指世界标准时格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,Instant本身实际上就指明时区了,0时区。
ZonedDateTime是含有时区信息的时间,本质上是根据时区对Instant的格式化显示。

ZonedDateTime ztime1=ZonedDateTime.ofInstant(Instant.now(),ZoneId.systemDefault());
System.out.println(ztime1);
System.out.println(ztime1.toInstant()); //1
System.out.println(ztime1.toLocalDateTime()); //3
ZonedDateTime ztime2=ZonedDateTime.ofInstant(Instant.now(),ZoneId.of("Australia/Darwin"));
System.out.println(ztime2);
System.out.println(ztime2.toInstant()); //2
System.out.println(ztime2.toLocalDateTime()); //4输出:
2017-12-13T13:24:55.932+08:00[Asia/Shanghai]
2017-12-13T05:24:55.932Z
2017-12-13T13:24:55.932
2017-12-13T14:54:55.933+09:30[Australia/Darwin]
2017-12-13T05:24:55.933Z
2017-12-13T14:54:55.933

注释1、2输出相同,说明ZonedDateTime的存储本质是Instant;
注释3、4输出不同,说明ZonedDateTime会根据创建ZonedDateTime对象时传入的时区,进行格式化显示。

相同的Instant,在不同的时区有不同的展示时间,所以在用Instant构造ZonedDateTime的时候需要传入时区;ZonedDateTime可以直接转化为Instant,并且不同的ZonedDateTime可能会生成同样的Instant。

1.2 如何构造时间对象:

1.2.1 直接定义

System.out.println(Instant.ofEpochMilli(System.currentTimeMillis()));
System.out.println(LocalDateTime.of(2017,12,13,10,0,0,0));
System.out.println(ZonedDateTime.of(2017,12,13,10,0,0,0,ZoneId.systemDefault()));输出:
2017-12-13T06:22:06.581Z
2017-12-13T10:00
2017-12-13T10:00+08:00[Asia/Shanghai]

1.2.2 获取系统当前时间now()

System.out.println(Instant.now()); //世界标准时间
System.out.println(LocalDateTime.now()); //会把世界标准时间转换为本时区的时间,但是时区信息会被丢弃
System.out.println(ZonedDateTime.now()); //会把世界标准时间转换为本时区的时间,但是时区信息会被保留System.out.println(LocalDateTime.now(ZoneId.of("+00:00"))); //0时区的现在时间
System.out.println(ZonedDateTime.now(ZoneId.of("+00:00"))); //0时区的现在时间输出:
2017-12-14T02:53:05.830Z
2017-12-14T10:53:05.904
2017-12-14T10:53:05.906+08:00[Asia/Shanghai]
2017-12-14T02:53:05.906
2017-12-14T02:53:05.906Z

1.2.3 解析String

System.out.println(Instant.parse("2007-12-03T10:15:30Z")); //只能解析这种格式,不能自己指定
System.out.println(LocalDateTime.parse("2017-12-13 11:51:12.083", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
System.out.println(ZonedDateTime.parse("2017-12-13 11:51:12.083 +04:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS ZZZZZ")));输出:
2007-12-03T10:15:30Z
2017-12-13T11:51:12.083
2017-12-13T11:51:12.083+04:30

1.3 时间对象之间的转换:

1.3.1 Instant与LocalDateTime、ZonedDateTime之间的转换

Instant instant=Instant.now();
LocalDateTime localDateTime=LocalDateTime.ofInstant(instant,ZoneId.systemDefault());
ZonedDateTime zonedDateTime=ZonedDateTime.ofInstant(instant,ZoneId.systemDefault());System.out.println(instant);
System.out.println(localDateTime);
System.out.println(zonedDateTime);System.out.println(ZoneOffset.systemDefault());
System.out.println(ZoneOffset.UTC);
System.out.println(ZoneOffset.MIN);
System.out.println(ZoneOffset.of("+08:00"));
System.out.println(localDateTime.toInstant(ZoneOffset.UTC)); //在把LocalDateTime转换为Instant时,需要明确指定当前这个时间指的是那个时区的时间
System.out.println(localDateTime.toInstant(ZoneOffset.of("+08:00")));
System.out.println(zonedDateTime.toInstant());输出:
2017-12-14T01:50:26.098Z
2017-12-14T09:50:26.098
2017-12-14T09:50:26.098+08:00[Asia/Shanghai]
Asia/Shanghai
Z
-18:00
+08:00
2017-12-14T09:50:26.098Z
2017-12-14T01:50:26.098Z
2017-12-14T01:50:26.098Z

1.3.2 LocalDateTime、ZonedDateTime之间的转换

Instant instant=Instant.now();
LocalDateTime localDateTime=LocalDateTime.ofInstant(instant,ZoneId.systemDefault());
ZonedDateTime zonedDateTime=ZonedDateTime.ofInstant(instant,ZoneId.systemDefault());System.out.println(instant);
System.out.println(localDateTime);
System.out.println(zonedDateTime);System.out.println(ZonedDateTime.of(localDateTime,ZoneId.systemDefault())); //LocalDateTime转ZonedDateTime
System.out.println(zonedDateTime.toLocalDateTime()); //ZonedDateTime转LocalDateTime输出:
2017-12-14T02:01:45.145Z
2017-12-14T10:01:45.145
2017-12-14T10:01:45.145+08:00[Asia/Shanghai]
2017-12-14T10:01:45.145+08:00[Asia/Shanghai]
2017-12-14T10:01:45.145

1.4 时区之间的转换

时区转换时要特别注意的是:用户输入的String类型的时间是没有时区信息的,需要人为指定解析。
解析的步骤分2步:

  1. 先结合语境,分析用户时区,把用户输入的时间转化为世界标准时间;
  2. 再把世界标准时间转为需要的时区。

1.5 关于时间的陷阱

1.5.1 问题

因为存在时区的概念,所以会造成2个问题:

  1. 不同时区的用户,对时间的理解不同,不同时区的同一个时间String不是同一个时间戳;
  2. 不同时区的服务器,对时间的理解也不同,比如,同一份程序运行在不同时区的服务器上,当这些程序同时调用LocalDateTime.now()时,返回的结果并不同,如果操作不当很容易出现问题;
  3. 如果前台和后台程序分别部署在不同时区的服务器上,情况会更加复杂。如果用户、前台和后台程序都不在相同时区,……。

1.5.2 解决办法

  1. 建议在系统当中统一使用时间戳,包括前后台传输和数据库存储,只有在展示的时候再转化为字符串;
  2. 如果为了处理方便建议把所有的时间都转化到0时区进行处理。

2. Java8以前的时间API

JAVA API系列----日期和时间相关的类
【总结】java.util.Date vs. java.sql.Date
java.util.Date、java.sql.Date、java.sql.Time、java.sql.Timestamp区别和总结
时区转换:java new Date() 变成GMT&& GMT时间与CST时间转换

2.1 Date与时区

Date对象中,有一个默认时区,取得是操作系统的默认时区。可以通过下面的代码进行修改:

TimeZone.setDefault(TimeZone.getTimeZone("GMT")); // 将默认时区转化为GMT时区
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));// 将默认时区转化为东八区时区

3. 新旧时间API的转换

新旧时间API连接的桥梁是Date类和Instant类,这两个都是世界标准时间,但是Date打印的时候会转化为本地时间。

Date date=Date.from(Instant.now());
Instant instant=date.toInstant();
System.out.println(date);
System.out.println(date.getTime()); //1
System.out.println(instant);
System.out.println(instant.toEpochMilli()); //2输出:
Thu Dec 14 11:45:58 CST 2017
1513223158588
2017-12-14T03:45:58.588Z
1513223158588

注释1、2输出相同,说明Date类和Instant类是等价的;


4. 追加:Java8中计算日期时间差

请参考此文:Java8中计算日期时间差,为查阅方便整理记录如下。
在Java8中,我们可以使用以下类来计算日期时间差异:

4.1 Period

内部使用年、月、日进行存储,只支持用年、月、日来初始化,也只支持返回年、月、日这三种单位。

public final class Periodimplements ChronoPeriod, Serializable {private final int years;private final int months;private final int days;//-----------------------------------------------------------------------public int getYears() {return years;}public int getMonths() {return months;}public int getDays() {return days;}}

4.2 Duration

内部使用秒、纳秒进行存储,支持用天、小时、分钟、秒、毫秒、纳秒来初始化,也支持返回天、小时、分钟、秒、毫秒、纳秒这几种单位。

public final class Durationimplements TemporalAmount, Comparable<Duration>, Serializable {private final long seconds;private final int nanos;//-----------------------------------------------------------------------public long getSeconds() {return seconds;}public int getNano() {return nanos;}//-----------------------------------------------------------------------public long toDays() {return seconds / SECONDS_PER_DAY;}public long toHours() {return seconds / SECONDS_PER_HOUR;}public long toMinutes() {return seconds / SECONDS_PER_MINUTE;}public long toMillis() {long millis = Math.multiplyExact(seconds, 1000);millis = Math.addExact(millis, nanos / 1000_000);return millis;}public long toNanos() {long totalNanos = Math.multiplyExact(seconds, NANOS_PER_SECOND);totalNanos = Math.addExact(totalNanos, nanos);return totalNanos;}}

演示:

public static void main(String[] args) {long l = Duration.ofDays(3).toNanos() + 5;Duration duration = Duration.ofNanos(l);System.out.println(l); // 259200000000005System.out.println(duration.toDays()); // 3System.out.println(duration.toHours()); // 72System.out.println(duration.toMinutes()); // 4320System.out.println(duration.getSeconds()); // 259200System.out.println(duration.toMillis()); // 259200000System.out.println(duration.getNano()); // 5 (注意:这一行与下一行的区别,getNano表示获取nanos变量存的值)System.out.println(duration.toNanos()); // 259200000000005 (toNanos表示将区间转换为纳秒单位)}

4.3 ChronoUnit:

本职工作是提供时间单位,但是其提供了一个between方法,所以也可以用来测量时间段(并且可以指定单位)。
如,间隔几小时:

     Instant a = Instant.now();Instant b = Instant.now().plus(1, ChronoUnit.HOURS);System.out.println(ChronoUnit.HOURS.between(a, b));

实际上是包装了until函数:

 public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {return temporal1Inclusive.until(temporal2Exclusive, this);}

4.4 until:

如果已经有了两个时间点,建议直接使用时间点的until函数。

     Instant a = Instant.now();Instant b = Instant.now().plus(1, ChronoUnit.HOURS);System.out.println(a.until(b, ChronoUnit.HOURS));

5. 后台接口中的时间参数

5.1 无配置接收

  1. yyyy-[m]m-[d]d hh:mm:ss[.f…]
    可以使用java.sql.Timestamp接收
  2. yyyy-[m]m-[d]d
    可以使用java.sql.Date接收
  3. Sat, 12 Aug 1995 13:30:00 GMT
    可以使用java.util.Date接收

5.2 有配置接收

自己编写convert类,并注册到spring框架中
参见:
[1] springboot Date解析String类型的参数
[2] @DateTimeFormat格式化JSON日期时间(Date或timestamp)无效的原因 / Spring格式化json日期时间(Date或timestamp)的方法

5.3 自定义时间类

自己编写可以作为参数的时间类
参见:Annotation Type QueryParam

被@QueryParam注解的参数,必须满足一下条件中的一个:

  1. 原始类型
  2. 有一个构造函数接受一个String参数
  3. 有一个名为valueOf或fromString的静态方法,它接受一个String参数(例如,参见Integer.valueOf(String))
  4. 列表< T>,< T>或SortedSet T,其中T满足上述2或3。生成的集合是只读的。

Java中的时间与时区相关推荐

  1. Java中的时间与时区__java

         转:https://yq.aliyun.com/ziliao/245667      摘要: 本文讲的是Java中的时间与时区__java, 0. 前言: 时间格式: //世界标准时间,其中 ...

  2. Java中的时间、时区和夏令时

    相关概念 时区 时区是地球上的区域使用同一个时间定义.以前,人们通过观察太阳的位置(时角)决定时间,这就使得不同经度的地方的时间有所不同(地方时).1863年,首次使用时区的概念.时区通过设立一个区域 ...

  3. Java中的时间和日期(上)

    自从JDK 1.0开始,Java就提供了Date来处理时间和日期,作为老古董自然有很多东西是过时的.然后出现了Calendar来解决了很多问题,但是Calendar使用比较复杂,并且有些反人类的地方. ...

  4. Java中的时间和日期(下)

    转载请注明出处:http://blog.csdn.net/wl9739/article/details/51882913 在上篇文章Java中的时间和日期(上)里面,简单介绍了Java中的Date类, ...

  5. java中各种时间格式的转化

    http://www.chinaitpower.com/A/2005-01-14/104881.html 使用java.util.Calendar返回间隔天数         static int g ...

  6. java设置北京时间的时区

    java设置北京时间的时区 解决方法: 设置北京时间的时区,消除时间差. TimeZone timeZone = TimeZone.getTimeZone("GMT+8"); Ti ...

  7. JAVA中的时间大小比较

    原文地址为: JAVA中的时间大小比较 1.时间的比较 import java.text.DateFormat; import java.text.ParseException; import jav ...

  8. java中用时分秒去现实时差_Java中的时间与时区

    0. 前言: 时间格式: //世界标准时间,其中T表示时分秒的开始(或者日期与时间的间隔),Z表示这是一个世界标准时间 2017-12-13T01:47:07.081Z //本地时间,也叫不含时区信息 ...

  9. 在java中原始时间_Java 日期时间

    Java 日期时间 java.util包提供了Date类来封装当前的日期和时间. Date类提供两个构造函数来实例化Date对象. 第一个构造函数使用当前日期和时间来初始化对象. Date( ) 第二 ...

最新文章

  1. R语言gganimate包创建可视化gif动图、可视化动图:创建动态散点图动画基于transition_time函数、使用shadow_wake函数配置动画的渐变效果(gradual falloff)
  2. .NET 框架与多线程 (转载)
  3. pycharm同一目录下无法import其他文件
  4. docker镜像内容如何查看_如何快速打通 Docker 镜像发布流程?
  5. springCloud - 第12篇 - 服务监控 Hystrix 面板
  6. 2011年华科计算机考研复试机试题真题
  7. ARM, MIPS, Power PC
  8. java接口回调学习
  9. Matlab for循环subplot画图加标题
  10. 寻找电路布线最短路径算法BFS
  11. .NET程序员面试指南:设计窗口程序演示八皇后问题
  12. 【ARM】2410裸机系列-中断处理
  13. ubuntu 终端透明
  14. 词典GoldenDict
  15. 制作旅行英语图书封面
  16. 亲身经历:一次sql缺少where条件的惨案…
  17. i春秋CTF训练 Web 破译
  18. 用python进行股票数据分析_用 Python 做股市数据分析(2)
  19. 【ESXi 7.x 升 8.x】ESXi 升级 —— 使用 ESXCLI 升级 ESXi(Offline Bundle ZIP)
  20. C++实现pi/π/圆周率的计算方法

热门文章

  1. 大数据系统架构-MPP数据库架构
  2. 在Apple Watch之前,智能手表凭什么生存?
  3. python在服务器上运行慢_Windows监控进程服务器IIS进程状态解决CPU暴满造成服务器运行缓慢 Python版...
  4. sqlite3介绍、安装及基本语法
  5. 对称加密AES代码实现,实现文件加密与解密
  6. 故障:登录时不能选择 Administrator 账户登录
  7. 【原创工具】TE 中文编辑器 / c、c++、rust编辑器 / 轻量化数据清洗工具 for Windows
  8. Dockerfile ENV指令 语法解析
  9. vmware资源管理-2
  10. 没有好的文案,你的短信营销注定石沉大海