Java常用类库——时间处理类Date、Calendar、GregorianCalendar以及日期格式化和计算

在Java中经常需要对时间进行操作,日期比较、格式转换、日期计算等。

经常使用的类有java.util.Date;java.sql.Date;java.util.Calendar;java.util.GregorianCalendar;java.text.DateFormat以及sql中的Time和Timestamp类,以上的几个类中Date、Calendar及其子类GregorianCalendar一般用来进行日期值的处理,DateFormat和String.format方法用来进行日期格式的转换,sql.Time和sql.Timestamp一般用在日期数据要与数据库进行交互的情况下。

Date在线中文文档:http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/Date.html

Calendar在线中文文档:http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/Calendar.html

GregorianCalendar在线中文文档:http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/GregorianCalendar.html

各类详细介绍及方法详见上面在线文档或附录中的方法整理。

一、各类简介

1.Date

Date表示特定的瞬间,精确到毫秒。

JDK 1.1之前,类 Date有两个其他的函数。它允许把日期解释为年、月、日、小时、分钟和秒值。它也允许格式化和解析日期字符串。不过,这些函数的 API不易于实现国际化。从 JDK 1.1开始,应该使用 Calendar类实现日期和时间字段之间转换,使用 DateFormat类来格式化和解析日期字符串。Date中的相应方法已废弃。

在类 Date所有可以接受或返回年、月、日期、小时、分钟和秒值的方法中,将使用下面的表示形式:

  • 年份 y由整数 y - 1900表示。

  • 月份由从 0 11的整数表示;0是一月、1是二月等等;因此 11是十二月。

  • 日期(一月中的某天)按通常方式由整数 1 31表示。

  • 小时由从 0 23的整数表示。因此,从午夜到 1 a.m.的时间是 0点,从中午到 1 p.m. 的时间是 12 点。

  • 分钟按通常方式由 0 59的整数表示。

  • 秒由 0 61的整数表示;值 60 61只对闰秒发生,尽管那样,也只用在实际正确跟踪闰秒的 Java实现中。于按当前引入闰秒的方式,两个闰秒在同一分钟内发生是极不可能的,但此规范遵循 ISO C的日期和时间约定。

在所有情形中,针对这些目的赋予方法的参数不需要在指定的范围内;例如,可以把日期指定为 1 32日,并把它解释为 2 1日的相同含义。

2.Calendar

publicabstractclass Calendar

Calendar类是一个抽象类,它为特定瞬间与一组诸如 YEARMONTHDAY_OF_MONTHHOUR日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 1 1日的 00:00:00.000,格里高利历)的偏移量。

3.GregorianCalendar

该类是Calendar的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统。

4.Time/Timestamp

java.util.Date类有关的瘦包装器 (thin wrapper),它允许 JDBC API将该类标识为 SQL TIME/ TIMESTAMP值。

5.DateFormat

DateFormat 是日期/时间格式化子类的抽象类,要格式化一个当前语言环境下的日期,可使用某个静态工厂方法:

  myString = DateFormat.getDateInstance().format(myDate);

6.SimpleDateFormat

格式化日期值的常用类,是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。

二、方法测试

1.Calendar#equals

		Calendar c1 = Calendar.getInstance();
		System.out.println(c1.getTimeZone().getID());//Asia/Shanghai
		Calendar c2 = Calendar.getInstance();
		c2.setTimeZone(TimeZone.getTimeZone("Asia/Beijing"));
		System.out.println(c1.getTimeInMillis()==c2.getTimeInMillis());
		System.out.println(c1.equals(c2));//false

Calendar的equals方法返回true的条件很苛刻,底层实现:

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        try {
            Calendar that = (Calendar)obj;
            return compareTo(getMillisOf(that)) == 0 &&
                lenient == that.lenient &&
                firstDayOfWeek == that.firstDayOfWeek &&
                minimalDaysInFirstWeek == that.minimalDaysInFirstWeek &&
                zone.equals(that.zone);
        } catch (Exception e) {
            // Note: GregorianCalendar.computeTime throws
            // IllegalArgumentException if the ERA value is invalid
            // even it's in lenient mode.
        }
        return false;
    }

从上面的代码可以发现,在使用equals方法判断两个Calendar对象是否相等时,Calendar 参数是通过isLenientgetFirstDayOfWeekgetMinimalDaysInFirstWeekgetTimeZone 方法表示的值。如果在两个Calendar 之间这些参数中存在任何不同之处,则此方法返回false。当且仅当参数是同一日历系统的 Calendar 对象时,结果才为true。
2.Date(long time)

这个是Date的一个构造方法,看下面的代码,结果是什么?

		SimpleDateFormat format = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
		System.out.println(format.format(new Date(0)));<strong><span style="color:#ff0000;">//与API描述不符</span></strong>

在输出行的后面我天真的注释到:与API不符,实际输出语句为:1970-01-01 08:00:00, API上写的不是从70-01-01 00:00:00 开始的时间偏移么,为什么会打印出08点呢?我天真的以为我发现了一个bug……就在提交该bug之前我认识到如果我在零时区测试这句代码的话就不会出bug了,忘了时间默认的是使用默认语言环境创建实例的,new Date(0)创建的是我现在所在地区的时间,也就是东八区地区时间。
三、日期格式转换

在前面的String.format中整理了,formatter类对日期的格式转换http://blog.csdn.net/goskalrie/article/details/50721894

使用日期格式转换处理类SimpleDateFormat转换日期格式,在这里涉及线程安全的问题,参见http://www.cnblogs.com/peida/archive/2013/05/31/3070790.html

三、测试代码

对于日历对象存在宽松性一个属性,这个属性会影响日期计算的行为模式,计算的常用方法有set、roll、addset方法在宽松模式下计算会发生溢出,而在非宽松模式下出现溢出会报错,add方法在任意模式下都会发生溢出而roll在任意模式下都不会发生溢出。

注:溢出就是指低位字段对高位字段影响。

		/**
		 * 1.get a time
		 * 2.date calculations
		 * 3.format conversion
		 * 4.format conversion
		 */
		//1.1使用默认环境获取当前时间
		Date d = new Date();//最常用,无论是学习还是在实际使用中
		Calendar c = Calendar.getInstance();//抽象类生成实例,不是单例的
		GregorianCalendar g = new GregorianCalendar();
		//1.2 获取特定环境的时间
		//TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
		//获取纽约的当前时间
		c.setTimeZone(TimeZone.getTimeZone("America/New_York"));
		c.setTime(d);
		Calendar c1= Calendar.getInstance();  
		c1.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND));  
		System.out.println(c1.getTime());
		//1.3根据指定的时间获取时间类对象,可以先生成对应的实例,然后使用各自的set方法进行对时间的各部分进行设置
		Date d1 = new Date("2015/01/01 11:12:13");
		Calendar c2 = Calendar.getInstance();
		c2.set(2016, 2, 01, 20, 20, 20);//月份0~11,这里的是三月
		//或是使用Calendar中的静态字段进行设置      	c2.set(Calendar.DATE, 01);
		System.out.println(c2.getTime());
		Date d2 = new Date(Date.parse("2015/01/01 11:12:13"));
		System.out.println(d2);
		//GregorianCalendar 与Calendar类似<pre class="java" name="code">		//GregorianCalendar进行时间计算的常用方法有set add roll g.isLenient()==true时这些方法中都可以指定负值
		//setLenient方法指定时间的宽松性,该属性影响时间计算时对个字段的校验方法以及计算时个字段之间是否有影响
		GregorianCalendar g = new GregorianCalendar();
		g.set(GregorianCalendar.SECOND, 46800);//相当于在当前时间上加46800秒
		g.set(GregorianCalendar.DAY_OF_MONTH, 0);//前一天
		g.add(GregorianCalendar.DAY_OF_MONTH , 1);//下一天
		g.add(GregorianCalendar.DAY_OF_MONTH , -1);//前一天
		g.add(GregorianCalendar.DAY_OF_MONTH , 0);//当前
		g.add(GregorianCalendar.SECOND , 46800);//在当前时间上加46800秒
		//上面的操作中某个字段值发生了溢出后会向下一个更大的字段溢出,比如当前是早上10点,在时间上增加46800秒,也就是13个小时,所以结果是晚上是23点,首先计算的是秒字段,秒字段向上溢出影响分钟字段,然后分钟字段也发生了溢出,进而影响了小时字段,以此类推
		//也就是说时间的模式是宽松的,如果不想发生这种溢出,需要设置时间的模式为非宽松性的
		g.setLenient(false);
		g.set(GregorianCalendar.SECOND , 46800);//该句会报错,时间是在非宽松模式下计算的,发生溢出后就会报错。使用add和roll方法时不会报错
		g.roll(GregorianCalendar.DAY_OF_MONTH , false);//向指定日历字段减少单位时间量,不更改更大的字段。
		g.roll(GregorianCalendar.DAY_OF_MONTH , 32);//向指定日历字段添加单位时间量,不更改更大的字段。
		//在Calendar类中lenient属性:    private boolean lenient = true;默认的是宽松性的,在宽松模式下,set值时会发生溢出影响上下字段,而add方法会始终发生溢出,roll方法始终不会发生溢出
		//查看这三个方法的源码就可以发现在roll和add方法中根本就没有判断lenient的逻辑,而在set方法中最外层的就是lenient判断逻辑
		Date d = new Date("2015/02/29 10:10:10");//这句话输出的是Sun Mar 01 10:10:10 CST 2015,也是自动溢出到下一字段

 
 
		//3.1格式转换
		/**
			定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留): 
			字母  日期或时间元素  表示  示例  
			G  Era 标志符  Text  AD  
			y  年  Year  1996; 96  
			M  年中的月份  Month  July; Jul; 07  
			w  年中的周数  Number  27  
			W  月份中的周数  Number  2  
			D  年中的天数  Number  189  
			d  月份中的天数  Number  10  
			F  月份中的星期  Number  2  
			E  星期中的天数  Text  Tuesday; Tue  
			a  Am/pm 标记  Text  PM  
			H  一天中的小时数(0-23)  Number  0  
			k  一天中的小时数(1-24)  Number  24  
			K  am/pm 中的小时数(0-11)  Number  0  
			h  am/pm 中的小时数(1-12)  Number  12  
			m  小时中的分钟数  Number  30  
			s  分钟中的秒数  Number  55  
			S  毫秒数  Number  978  
			z  时区  General time zone  Pacific Standard Time; PST; GMT-08:00  
			Z  时区  RFC 822 time zone  -0800  
		 */
		String f1 = "现在时刻,北京时间yyyy年ah点M分s秒E";
		String f2 = "y-M-d H:m:s";
		System.out.println(new SimpleDateFormat(f2).format(new Date()));
		//使用上面的表格中的模式字母可以任意指定时间的格式,还有String#format方法也可以对时间进行格式化

四、附录

附录一:Date类方法分类整理

Date类的方法可以分为四类

  • 构造方法

  • 日期比较

  • 单个属性值的获取和设置

  • 转换及其他方法

    1. 构造方法

构造方法

说明

Date()

分配 Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。

Date(int year, int month, int date)

已过时。  JDK 1.1 开始,由 Calendar.set(year + 1900, month, date) GregorianCalendar(year + 1900, month, date)取代。

Date(int year, int month, int date, int hrs, int min)

 已过时。  JDK 1.1 开始,由 Calendar.set(year + 1900, month, date, hrs, min) GregorianCalendar(year + 1900, month, date, hrs, min)取代。

Date(int year, int month, int date, int hrs, int min, int sec)

已过时。  JDK 1.1 开始,由 Calendar.set(year + 1900, month, date, hrs, min, sec) GregorianCalendar(year + 1900, month, date, hrs, min, sec)取代。

Date(long date)

分配 Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 1 1 00:00:00 GMT)以来的指定毫秒数。

Date(String s)

已过时。  JDK 1.1 开始,由 DateFormat.parse(String s)取代。

    1. 日期比较

返回值类型

方法

说明

boolean

after(Date when)

测试此日期是否在指定日期之后。

boolean

before(Date when)

测试此日期是否在指定日期之前。

int

compareTo(Date anotherDate)

比较两个日期的顺序。

boolean

equals(Object obj)

比较两个日期的相等性。

    1. 单个属性值的获取和设置

返回值类型

方法

说明

int

getDate()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.DAY_OF_MONTH)取代。

int

getDay()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.DAY_OF_WEEK)取代。

int

getHours()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.HOUR_OF_DAY)取代。

int

getMinutes()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.MINUTE)取代。

int

getMonth()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.MONTH)取代。

int

getSeconds()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.SECOND)取代。

long

getTime()

返回自 1970 1 1 00:00:00 GMT以来此 Date对象表示的毫秒数。

int

getTimezoneOffset()

已过时。  JDK 1.1 开始,由 -(Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)取代。

int

getYear()

已过时。  JDK 1.1 开始,由 Calendar.get(Calendar.YEAR) - 1900取代。

void

setDate(int date)

已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.DAY_OF_MONTH, int date)取代。

void

setHours(int hours)

已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.HOUR_OF_DAY, int hours)取代。

void

setMinutes(int minutes)

 已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.MINUTE, int minutes)取代。

void

setMonth(int month)

已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.MONTH, int month)取代。

void

setSeconds(int seconds)

已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.SECOND, int seconds)取代。

void

setTime(long time)

设置此 Date对象,以表示 1970 1 1 00:00:00 GMT以后 time毫秒的时间点。

void

setYear(int year)

 已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.YEAR, year + 1900)取代。

    1. 转换及其他方法

返回值类型

方法

说明

Object

clone()

返回此对象的副本。

int

hashCode()

返回此对象的哈希码值。

 String

toGMTString()

已过时。  JDK 1.1 开始,由 Calendar.set(Calendar.YEAR, year + 1900)取代。

 String

toLocaleString()

已过时。  JDK 1.1 开始,由 DateFormat.format(Date date)取代。

 String

toString()

把此 Date对象转换为以下形式的 String dow mon dd hh:mm:ss zzz yyyy 其中: dow是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)

static long

UTC(int year, int month, int date, int hrs, int min, int sec)

 已过时。  JDK 1.1 开始,由 Calendar.set(year + 1900, month, date, hrs, min, sec) GregorianCalendar(year + 1900, month, date, hrs, min, sec)取代,使用 UTC TimeZone,后跟 Calendar.getTime().getTime()

附录二:Calendar类方法分类整理

21用户不可调用的方法

protected void

complete()

填充日历字段中所有未设置的字段。

protected abstract  void

computeFields()

fields[]中的当前日历字段值转换为毫秒时间值 time

protected abstract  void

computeTime()

fields[]中的当前日历字段值转换为毫秒时间值 time

protected  int

internalGet(int field)

返回给定日历字段的值。

 

构造方法摘要

protected

Calendar()
          
构造一个带有默认时区和语言环境的 Calendar

protected

(TimeZone zone, Locale aLocale)
          
构造一个带有指定时区和语言环境的 Calendar

22用户可以调用的方法

221日期比较

boolean

after(Object when)

判断此 Calendar表示的时间是否在指定 Object表示的时间之后,返回判断结果。

boolean

before(Object when)

判断此 Calendar表示的时间是否在指定 Object表示的时间之前,返回判断结果。

int

compareTo(Calendar anotherCalendar)

比较两个 Calendar对象表示的时间值(从历元至现在的毫秒偏移量)。

boolean

equals(Object obj)

将此 Calendar与指定 Object比较。

222对日期值获取和修改的方法

abstract  void

add(int field, int amount)

根据日历的规则,为给定的日历字段添加或减去指定的时间量。

void

clear()

将此 Calendar的所日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。

void

clear(int field)

将此 Calendar的给定日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。

int

get(int field)

返回给定日历字段的值。

void

roll(int field, int amount)

在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。

void

roll(int field, boolean up)

向指定日历字段添加指定(有符号的)时间量,不更改更大的字段。

void

set(int field, int value)

将给定的日历字段设置为给定值。

void

set(int year, int month, int date)

设置日历字段 YEARMONTH DAY_OF_MONTH的值。

void

set(int year, int month, int date, int hourOfDay, int minute)

设置日历字段 YEARMONTHDAY_OF_MONTHHOUR_OF_DAY MINUTE的值。

void

set(int year, int month, int date, int hourOfDay, int minute, int second)

设置字段 YEARMONTHDAY_OF_MONTHHOURMINUTE SECOND的值。

void

setTime(Date date)

 使用给定的 Date设置此 Calendar的时间。

void

setTimeInMillis(long millis)

用给定的 long值设置此 Calendar的当前时间值。

Date

getTime()

返回一个表示此 Calendar时间值(从历元至现在的毫秒偏移量)的 Date对象。

long

getTimeInMillis()

返回此 Calendar的时间值,以毫秒为单位。

223对时区相关信息、日期格式属性值的获取和修改

int

getActualMaximum(int field)

给定此 Calendar的时间值,返回指定日历字段可能拥有的最大值。

int

getActualMinimum(int field)

给定此 Calendar的时间值,返回指定日历字段可能拥有的最小值。

static Locale[]

getAvailableLocales()

返回所有语言环境的数组,此类的 getInstance方法可以为其返回本地化的实例。

 Map<String,Integer>

getDisplayNames(int field, int style, Locale locale)

返回给定 style locale下包含日历 field所有名称的 Map及其相应字段值。

int

getFirstDayOfWeek()

获取一星期的第一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY

abstract  int

getGreatestMinimum(int field)

返回此 Calendar实例给定日历字段的最高的最小值。

abstract  int

getLeastMaximum(int field)

返回此 Calendar实例给定日历字段的最低的最大值。

abstract  int

getMaximum(int field)

返回此 Calendar实例给定日历字段的最大值。

int

getMinimalDaysInFirstWeek()

 获取一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则此方法将返回 1

abstract  int

getMinimum(int field)

返回此 Calendar实例给定日历字段的最小值。

TimeZone

getTimeZone()

获得时区。

void

setFirstDayOfWeek(int value)

设置一星期的第一天是哪一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY

void

setLenient(boolean lenient)

指定日期/时间解释是否是宽松的。

void

setMinimalDaysInFirstWeek(int value)

  设置一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则使用值 1 调用此方法。

void

setTimeZone(TimeZone value)

使用给定的时区值来设置时区。

224获取Calendar实例及与Date类对象之间的转换

static Calendar

getInstance()

使用默认时区和语言环境获得一个日历。

static Calendar

getInstance(TimeZone zone, Locale aLocale)

使用指定时区和默认语言环境获得一个日历。

static Calendar

getInstance(TimeZone zone)

使用指定时区和默认语言环境获得一个日历。

225其他

Object

clone()

创建并返回此对象的一个副本。

String

getDisplayName(int field, int style, Locale locale)

返回给定 style locale下的日历 field值的字符串表示形式。

int

hashCode()

返回该此日历的哈希码。

boolean

isLenient()

判断日期/时间的解释是否为宽松的。

boolean

isSet(int field)

确定给定日历字段是否已经设置了一个值,其中包括因为调用 get方法触发内部字段计算而导致已经设置该值的情况。

String

toString()

返回此日历的字符串表示形式。

附录三:GregorianCalendar方法分类整理

31构造方法

构造方法摘要

GregorianCalendar()
          
在具有默认语言环境的默认时区内使用当前时间构造一个默认的 GregorianCalendar

 

GregorianCalendar(int year, int month, int dayOfMonth)
          
在具有默认语言环境的默认时区内构造一个带有给定日期设置的 GregorianCalendar

 

GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute)
          
为具有默认语言环境的默认时区构造一个具有给定日期和时间设置的 GregorianCalendar

 

GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second)
          
为具有默认语言环境的默认时区构造一个具有给定日期和时间设置的 GregorianCalendar

 

GregorianCalendar(Locale aLocale)
          
在具有给定语言环境的默认时区内构造一个基于当前时间的 GregorianCalendar

 

GregorianCalendar(TimeZone zone)
          
在具有默认语言环境的给定时区内构造一个基于当前时间的 GregorianCalendar

 

GregorianCalendar(TimeZone zone,Locale aLocale)
          
在具有给定语言环境的给定时区内构造一个基于当前时间的 GregorianCalendar

 

32从父类继承的方法

33该类中的方法

void

add(int field, int amount)

根据日历规则,将指定的(有符号的)时间量添加到给定的日历字段中。

Object

clone()

创建并返回此对象的一个副本。

boolean

equals(Object obj)

比较此 GregorianCalendar与指定的 Object

int

getActualMaximum(int field)

考虑到给定的时间值和 getFirstDayOfWeekgetMinimalDaysInFirstWeekgetGregorianChange getTimeZone方法的当前值,返回此日历字段可能具有的最大值。

int

getActualMinimum(int field)

考虑到给定的时间值和 getFirstDayOfWeekgetMinimalDaysInFirstWeekgetGregorianChange getTimeZone方法的当前值,返回此日历字段可能具有的最小值。

int

getGreatestMinimum(int field)

返回此 GregorianCalendar实例给定日历字段的最高的最小值。

Date

getGregorianChange()

获得格里高利历的更改日期。

int

getMaximum(int field)

返回此 GregorianCalendar实例的给定日历字段的最大值。

int

getMinimum(int field)

返回此 GregorianCalendar实例的给定日历字段的最小值。

TimeZone

getTimeZone()

获得时区。

int

hashCode()

生成此 GregorianCalendar对象的哈希码。

boolean

isLeapYear(int year)

确定给定的年份是否为闰年。

void

roll(int field, boolean up)

在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。

void

roll(int field, int amount)

向指定日历字段添加有符号的时间量,不更改更大的字段。

void

setGregorianChange(Date date)

设置 GregorianCalendar的更改日期。

void

imeZone(TimeZone zone)

使用给定的时区值来设置时区。



你可能感兴趣的:(Java常用类库——时间处理类Date、Calendar、GregorianCalendar以及日期格式化和计算)