这里写目录标题

  • 声明
  • 7.5 剖析日期和时间
    • 7.5.1 基本概念
      • 1.时区
      • 2.时刻和纪元时
      • 3.年历
    • 7.5.2 日期和时间API
      • 1.Date
      • 2.TimeZone
      • 3.Locale
      • 4.Calendar
      • 5.DateFormat
      • 6.SimpleDateFormat
    • 7.5.3 局限性
      • 1.Date中的过时方法
      • 2.Calendar操作比较烦琐
      • 3.DateFormat的线程安全性
    • 参考目录

声明

本书作者的关于此章节的内容的相关时间的API和方法都为Java 8之前的API,虽然这些类与方法大多有缺陷,但对于Java中日期与时间的理解还是非常重要的,对于更多方便的Java时间API,此书作者在后续章节(26.5 Java 8的日期和时间API)专门介绍了Java8的相关时间API。

7.5 剖析日期和时间

本节,我们讨论Java中日期和时间处理相关的API。日期和时间是 一个比较复杂的概念,Java 8之前的设计有一些不足,业界有一个广泛 使用的第三方类库Joda-Time,Java 8受Joda-Time影响,重新设计了日期 和时间API,新增了一个包java.time。虽然Java 8之前的API有一些不 足,但依然是被大量使用的,本节只介绍Java 8之前的API。关于Java 8 的API,它使用了Lambda表达式,我们还没介绍,所以留待到第26章介 绍。

下面,我们先来看一些基本概念,然后再介绍Java的日期和时间 API。

7.5.1 基本概念

关于日期和时间,有一些基本概念,包括时区、时刻、纪元时、年 历等。

1.时区

我们都知道,同一时刻,世界上各个地区的时间可能是不一样的, 具体时间与时区有关。全球一共有24个时区,英国格林尼治是0时区, 北京是东八区,也就是说格林尼治凌晨1点,北京是早上9点。0时区的 时间也称为GMT+0时间,GMT是格林尼治标准时间,北京的时间就是 GMT+8:00。

2.时刻和纪元时

所有计算机系统内部都用一个整数表示时刻,这个整数是距离格林 尼治标准时间1970年1月1日0时0分0秒的毫秒数。为什么要用这个时间 呢?更多的是历史原因,本书就不介绍了。

格林尼治标准时间1970年1月1日0时0分0秒也被称为Epoch Time(纪元时)。

这个整数表示的是一个时刻,与时区无关,世界上各个地方都是同 一个时刻,但各个地区对这个时刻的解读(如年月日时分秒)可能是不 一样的。

对于1970年以前的时间,使用负数表示。

3.年历

我们都知道,中国有公历和农历之分,公历和农历都是年历,不同 的年历,一年有多少月,每月有多少天,甚至一天有多少小时,这些可 能都是不一样的。

比如,公历有闰年,闰年2月是29天,而其他年份则是28天,其他 月份,有的是30天,有的是31天。农历有闰月,比如闰7月,一年就会 有两个7月,一共13个月。

公历是世界上广泛采用的年历,除了公历,还有其他一些年历,比 如日本也有自己的年历。Java API的设计思想是支持国际化的,支持多 种年历,但没有直接支持中国的农历,本书主要讨论公历。

简单总结下,时刻是一个绝对时间,对时刻的解读,则是相对的, 与年历和时区相关。

7.5.2 日期和时间API

Java API中关于日期和时间,有三个主要的类。

·        ·Date:表示时刻,即绝对时间,与年月日无关

·Calendar:表示年历,Calendar是一个抽象类,其中表示公历的子类是Gregorian-Calendar

·DateFormat:表示格式化,能够将日期和时间与字符串进行相互转换,DateFormat也是一个抽象类,其中最常用的子类是 SimpleDateFormat。

还有两个相关的类:

·TimeZone:表示时区

·Locale:表示国家(或地区)和语言。

下面,我们来看这些类。

1.Date

Date是Java API中最早引入的关于日期的类,一开始,Date也承载 了关于年历的角色,但由于不能支持国际化,其中的很多方法都已经过时了,被标记为了@Deprecated,不再建议使用

Date表示时刻,内部主要是一个long类型的值,如下所示:

fastTime表示距离纪元时的毫秒数,此处,关于transient关键字,我们暂时忽略。

Date有两个构造方法:


        第一个构造方法是根据传入的毫秒数进行初始化第二个构造方法 是默认构造方法,它根据System.currentTimeMillis()的返回值进行初始化System.currentTimeMillis()是一个常用的方法,它返回当前时 刻距离纪元时的毫秒数

Date中的大部分方法都已经过时了,其中没有过时的主要方法有下面这些:

2.TimeZone

TimeZone表示时区它是一个抽象类有静态方法用于获取其实例。获取当前的默认时区,代码为:


        获取默认时区,并输出其ID,在笔者的计算机中,输出为:

默认时区是在哪里设置的呢?可以更改吗?Java中有一个系统属性 user.timezone,保存的就是默认时区。系统属性可以通过 System.getProperty获得,如下所示:

在笔者的计算机中,输出为:

系统属性可以在Java启动的时候传入参数进行更改,如:

TimeZone也有静态方法,可以获得任意给定时区的实例。比如,获 取美国东部时区:


        ID除了可以是名称外,还可以是GMT形式表示的时区,如:

3.Locale

Locale表示国家(或地区)和语言,它有两个主要参数一个是国家(或地区)另一个是语言,每个参数都有一个代码,不过国家(或 地区)并不是必需的。比如,中国内地的代码是CN,中国台湾地区的 代码是TW,美国的代码是US,中文语言的代码是zh,英文语言的代码 是en。

Locale类中定义了一些静态变量,表示常见的Locale,比如:

·Locale.US:表示美国英语。

·Locale.ENGLISH:表示所有英语。

·Locale.TAIWAN:表示中国台湾地区所用的中文。

·Locale.CHINESE:表示所有中文。

·Locale.SIMPLIFIED_CHINESE:表示中国内地所用的中文。

与TimeZone类似,Locale也有静态方法获取默认值,如:

        在笔者的计算机中,输出为:

4.Calendar

Calendar类是日期和时间操作中的主要类它表示与TimeZone和 Locale相关的日历信息,可以进行各种相关的运算。我们先来看下它的 内部组成,与Date类似,Calendar内部也有一个表示时刻的毫秒数,定义为:


        除此之外,Calendar内部还有一个数组,表示日历中各个字段的值,定义为:

这个数组的长度为17,保存一个日期中各个字段的值,都有哪些字 段呢?Calendar类中定义了一些静态变量,表示这些字段,主要有:

·Calendar.YEAR:表示年

·Calendar.MONTH:表示月,1月是0,Calendar同样定义了表示各 个月份的静态变量,如Calendar.JULY表示7月

·Calendar.DAY_OF_MONTH:表示日,每月的第一天是1

·Calendar.HOUR_OF_DAY:表示小时,为0~23

·Calendar.MINUTE:表示分钟,为0~59

·Calendar.SECOND:表示秒,为0~59。

·Calendar.MILLISECOND:表示毫秒,为0~999。

·Calendar.DAY_OF_WEEK:表示星期几,周日是1,周一是2,周 六是7,Calenar同样定义了表示各个星期的静态变量,如 Calendar.SUNDAY表示周日。

Calendar是抽象类,不能直接创建对象,它提供了多个静态方法, 可以获取Calendar实例,比如:

最终调用的方法都是需要TimeZone和Locale的,如果没有,则会使 用上面介绍的默认值。getInstance方法会根据TimeZone和Locale创建对 应的Calendar子类对象,在中文系统中,子类一般是表示公历的 GregorianCalendar。

getInstance方法封装了Calendar对象创建的细节。TimeZone和Locale 不同,具体的子类可能不同,但都是Calendar。这种隐藏对象创建细节的方式,是计算机程序中一种常见的设计模式,它有一个名字,叫工厂方法getInstance就是一个工厂方法,它生产对象

与new Date()类似,新创建的Calendar对象表示的也是当前时间,与Date不同的是,Calendar对象可以方便地获取年月日等日历信 息。来看代码,输出当前时间的各种信息:

具体输出与执行时的时间和默认的TimeZone以及Locale有关,比 如,在笔者的计算机中的一次输出为:

内部,Calendar会将表示时刻的毫秒数,按照TimeZone和Locale对 应的年历,计算各个日历字段的值,存放在fields数组中,Calendar.get 方法获取的就是fields数组中对应字段的值

Calendar支持根据Date或毫秒数设置时间

也支持根据年月日等日历字段设置时间,比如:


        除了直接设置,Calendar支持根据字段增加和减少时间


        amount为正数表示增加,负数表示减少

比如,如果想设置Calendar为第二天的下午2点15,代码可以为:


        Calendar的这些方法中一个比较方便和强大的地方在于,它能够自动调整相关的字段。比如,我们知道2月最多有29天,如果当前时间为1月30号,对Calendar.MONTH字段加1,即增加一月,Calendar不是简单 的只对月字段加1,那样日期是2月30号,是无效的,Calendar会自动调 整为2月最后一天,即2月28日或29日。

再如,设置的值可以超出其字段最大范围,Calendar会自动更新其他字段,如:


        相当于增加了46小时。

内部,根据字段设置或修改时间时,Calendar会更新fields数组对应字段的值,但一般不会立即更新其他相关字段或内部的毫秒数的值,不 过在获取时间或字段值的时候,Calendar会重新计算并更新相关字段。

简单总结下,Calenar做了一项非常烦琐的工作,根据TimeZone和 Locale,在绝对时间毫秒数和日历字段之间自动进行转换,且对不同日 历字段的修改进行自动同步更新

除了add方法,Calendar还有一个类似的方法:

与add方法的区别是,roll方法不影响时间范围更大的字段值。比 如:

calendar首先设置为13:59,然后分钟字段加3,执行后的calendar 时间为14:02。如果add改为roll,即:

则执行后的calendar时间会变为13:02,在分钟字段上执行roll方法 不会改变小时的值

Calendar可以方便地转换为Date或毫秒数,方法是:

与Date类似,Calendar之间也可以进行比较,也实现了Comparable 接口,相关方法有:

5.DateFormat

DateFormat类主要在Date和字符串表示之间进行相互转换,它有两 个主要的方法:

format将Date转换为字符串,parse将字符串转换为Date

Date的字符串表示与TimeZone和Locale都是相关的,除此之外,还 与两个格式化风格有关,一个是日期的格式化风格,另一个是时间的格式化风格。DateFormat定义了4个静态变量,表示4种风格:SHORT、 MEDIUM、LONG和FULL;还定义了一个静态变量DEFAULT,表示默 认风格,值为MEDIUM,不同风格输出的信息详细程度不同。

与Calendar类似,DateFormat也是抽象类,也用工厂方法创建对象,提供了多个静态方法创建DateFormat对象,有三类方法:

getDateTimeInstance方法既处理日期也处理时间,getDateInstance方 法只处理日期,get-TimeInstance方法只处理时间。看下面的代码:

输出为:


        每类工厂方法都有两个重载的方法,接受日期时间风格以及 Locale作为参数:

比如,看下面的代码:

输出为:

DateFormat的工厂方法里,我们没看到TimeZone参数,不过, DateFormat提供了一个setter方法,可以设置TimeZone

DateFormat虽然比较方便,但如果我们要对字符串格式有更精确的控制,则应该使用SimpleDateFormat这个类。

6.SimpleDateFormat

SimpleDateFormat是DateFormat的子类,相比DateFormat,它的一个 主要不同是,它可以接受一个自定义的模式(pattern)作为参数,这个 模式规定了Date的字符串形式。先看个例子:

输出为:

SimpleDateFormat有个构造方法,可以接受一个pattern作为参数, 这里pattern是:

pattern中的英文字符a~z和A~Z表示特殊含义,其他字符原样输出,这里:

·yyyy:表示4位的年。

·MM:表示月,用两位数表示。

·dd:表示日,用两位数表示。

·HH:表示24小时制的小时数,用两位数表示。

·mm:表示分钟,用两位数表示。

·ss:表示秒,用两位数表示。

·E:表示星期几。

这里需要特意提醒一下,hh也表示小时数,但表示的是12小时制的小时数,而a表示的是上午还是下午,看代码:

输出为:

更多的特殊含义可以参看SimpleDateFormat的API文档。如果想原样输出英文字符,可以将其用单引号括起来

除了将Date转换为字符串,SimpleDateFormat也可以方便地将字符串转化为Date,看代码:


        输出为:

代码将字符串解析为了一个Date对象,然后使用另外一个格式进行 了输出,这里SSS表示三位的毫秒数。需要注意的是,parse会抛出一个 受检异常,异常类型为ParseException,调用者必须进行处理。

7.5.3 局限性

至此,关于Java 8之前的日期和时间相关API的主要内容基本就介绍 完了。Date表示时刻,与年月日无关Calendar表示日历,与时区和 Locale相关,可进行各种运算,是日期时间操作的主要类DateFormat/SimpleDateFormat在Date和字符串之间进行相互转换。这些 API存在着一些局限性,下面强调一下。

1.Date中的过时方法

Date中的方法参数与常识不符合,过时方法标记容易被人忽略,产 生误用。比如,看如下代码:

想当然的输出为2016-08-15,但其实输出为:


        之所以产生这个输出,是因为Date构造方法中的year表示的是与 1900年的差,month是从0开始的

2.Calendar操作比较烦琐

Calendar API的设计不是很成功,一些简单的操作都需要多次方法 调用,写很多代码,比较臃肿

另外,Calendar难以进行比较复杂的日期操作,比如,计算两个日 期之间有多少个月,根据生日计算年龄,计算下个月的第一个周一等

3.DateFormat的线程安全性

DateFormat/SimpleDateFormat不是线程安全的。关于线程概念,第 15章会详细介绍,这里简单说明一下。多个线程同时使用一个 DateFormat实例的时候,会有问题,因为DateFormat内部使用了一个 Calendar实例对象,多线程同时调用的时候,这个Calendar实例的状态 可能就会紊乱

解决这个问题大概有以下方案:

·每次使用DateFormat都新建一个对象。

·使用线程同步(第15章介绍)。

·使用ThreadLocal(第19章介绍)。

·使用Joda-Time或Java 8的API,它们是线程安全的。

参考目录

绝大多数内容来自于:Java编程的逻辑 作者: 马俊昌(7.5 剖析日期和时间)

Java官方文档
https://docs.oracle.com/javase/specs/index.html

Java 常用基本类 剖析日期和时间(Java8以前)相关推荐

  1. java8 日期比较_20 个案例教你在 Java 8 中如何处理日期和时间?

    点击左上角蓝字,关注"SpringForAll社区" 专注分享Spring周边技术内容 前言 前面一篇文章写了<SimpleDateFormat 如何安全的使用?>, ...

  2. Java 8 中处理日期和时间示例

    在Java 8以前,日期和时间处理一直被广大java程序员抱怨太难用,首先是java.util和java.sql中,都包含Date类,如果要进行时间格式化,还需要java.text.DateForma ...

  3. 【Java】7.5 正则表达式 7.6 Java 8 新增的日期、时间格式器

    目录 Pattern类 Matcher类 Java 8 新增的日期.时间格式器 Pattern类 Pattern类的实例是将一个编译好的正则表达式封装起来.因此正则表达式字符串必须先被变异成Patte ...

  4. 计算机程序的思维逻辑 (32) - 剖析日期和时间

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>(马俊昌著),由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买:京东自营链接 ...

  5. (Java常用类)日期时间类

    文章目录 Date类 概述 常用方法 代码演示 DateFormat类 构造方法 格式规则 常用方法 代码演示 Calendar类 概念 获取方式 常用方法 get/set方法 add方法 getTi ...

  6. JAVA常用类之日期处理

    在应用程序设计中,我们经常会用到日期时间,比如出生日期之类的,JAVA中为我们提供了一些处理日期的类.这一片博客将为大家介绍一下. java.util.Date Date类是我们用的比较多的一个处理时 ...

  7. java 初始化duration_Java的日期与时间 java.time.Duration (转)

    一个Duration对象表示两个Instant间的一段时间,是在Java 8中加入的新功能. 一个Duration实例是不可变的,当创建出对象后就不能改变它的值了.你只能通过Duration的计算方法 ...

  8. 【小家java】java8新特性之---全新的日期、时间API(JSR 310规范),附SpringMVC、Mybatis中使用JSR310的正确姿势

    [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小家java ...

  9. 【Java】7.3 基本类 7.4 Java 8 的日期、时间类

    目录 String.StringBuffer和StringBuilder类 Rondom类 BigDecimal类 Java 8 新增的日期.时间包 String.StringBuffer和Strin ...

最新文章

  1. 1.11 超过人的表现-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  2. OpenCV hdr成像技术的实例(附完整代码)
  3. Python项目生成requirements.txt的多种方式
  4. 土地转移矩阵的计算步骤
  5. MC9S12XEP100 CAN通信配置
  6. 用Python做一个证件照制作器
  7. 巴旦木和杏仁的营养价值哪个好?丨巴旦木功效与作用
  8. python读取文件夹下所有图片并重命名_python 对文件夹下图片 批量重命名
  9. Windows安装TortoiseSVN
  10. 初学者之路——————对抗神经网络
  11. 计算机装机知识策划案,电脑装机大赛策划案.doc
  12. 新人学c,求助%5.2f含义
  13. java and运算_JAVA中逻辑运算符“|”和“”与“||”和“”的用法
  14. python 爬虫 表格,python爬虫爬取网页表格数据
  15. Python中的repr()函数与 ‘!r‘的作用
  16. 这里有一份面筋请查收(一)
  17. ruby 数组自定义排序_在Ruby中对数组排序
  18. 计算机课学生段密码,在线学习平台学生端常见问题
  19. PCB这个工艺,免费了!
  20. vue自适应企业门户模板

热门文章

  1. python中chr函数的作用_chr()函数以及Python中的示例
  2. GD32F310k_flash
  3. Win10下python3和python2同时安装并解决pip共存问题
  4. 了不起的外设 | AidLux小课堂
  5. 如何将微课应用到计算机教学,如何将微课应用于高校计算机教学中
  6. python 动物分类_动物分哪六大类
  7. linux命令英文缩写的含义(方便记忆)
  8. 电商系统对接支付渠道的解决方案
  9. 华为1+x网络通信与维护
  10. 转:关于北京铁路局实行电话订票的通知