在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
类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 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 参数是通过isLenient、getFirstDayOfWeek
、getMinimalDaysInFirstWeek
和getTimeZone
方法表示的值。如果在两个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、add;set方法在宽松模式下计算会发生溢出,而在非宽松模式下出现溢出会报错,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类的方法可以分为四类
构造方法
日期比较
单个属性值的获取和设置
转换及其他方法
构造方法
构造方法 |
说明 |
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)取代。 |
日期比较
返回值类型 |
方法 |
说明 |
boolean |
after(Date when) |
测试此日期是否在指定日期之后。 |
boolean |
before(Date when) |
测试此日期是否在指定日期之前。 |
int |
compareTo(Date anotherDate) |
比较两个日期的顺序。 |
boolean |
equals(Object obj) |
比较两个日期的相等性。 |
单个属性值的获取和设置
返回值类型 |
方法 |
说明 |
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)取代。 |
转换及其他方法
返回值类型 |
方法 |
说明 |
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类方法分类整理
2.1用户不可调用的方法
protected void |
complete() |
填充日历字段中所有未设置的字段。 |
protected abstract void |
computeFields() |
将 fields[]中的当前日历字段值转换为毫秒时间值 time。 |
protected abstract void |
computeTime() |
将 fields[]中的当前日历字段值转换为毫秒时间值 time。 |
protected int |
internalGet(int field) |
返回给定日历字段的值。 |
构造方法摘要 |
|
protected |
Calendar() |
protected |
(TimeZone zone, Locale aLocale) |
2.2用户可以调用的方法
2.2.1日期比较
boolean |
after(Object when) |
判断此 Calendar表示的时间是否在指定 Object表示的时间之后,返回判断结果。 |
boolean |
before(Object when) |
判断此 Calendar表示的时间是否在指定 Object表示的时间之前,返回判断结果。 |
int |
compareTo(Calendar anotherCalendar) |
比较两个 Calendar对象表示的时间值(从历元至现在的毫秒偏移量)。 |
boolean |
equals(Object obj) |
将此 Calendar与指定 Object比较。 |
2.2.2对日期值获取和修改的方法
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) |
设置日历字段 YEAR、MONTH和 DAY_OF_MONTH的值。 |
void |
set(int year, int month, int date, int hourOfDay, int minute) |
设置日历字段 YEAR、MONTH、DAY_OF_MONTH、HOUR_OF_DAY和 MINUTE的值。 |
void |
set(int year, int month, int date, int hourOfDay, int minute, int second) |
设置字段 YEAR、MONTH、DAY_OF_MONTH、HOUR、MINUTE和 SECOND的值。 |
void |
setTime(Date date) |
使用给定的 Date设置此 Calendar的时间。 |
void |
setTimeInMillis(long millis) |
用给定的 long值设置此 Calendar的当前时间值。 |
Date |
getTime() |
返回一个表示此 Calendar时间值(从历元至现在的毫秒偏移量)的 Date对象。 |
long |
getTimeInMillis() |
返回此 Calendar的时间值,以毫秒为单位。 |
2.2.3对时区相关信息、日期格式属性值的获取和修改
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) |
使用给定的时区值来设置时区。 |
2.2.4获取Calendar实例及与Date类对象之间的转换
static Calendar |
getInstance() |
使用默认时区和语言环境获得一个日历。 |
static Calendar |
getInstance(TimeZone zone, Locale aLocale) |
使用指定时区和默认语言环境获得一个日历。 |
static Calendar |
getInstance(TimeZone zone) |
使用指定时区和默认语言环境获得一个日历。 |
2.2.5其他
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方法分类整理
3.1构造方法
构造方法摘要 |
|
GregorianCalendar() |
|
GregorianCalendar(int year, int month, int dayOfMonth) |
|
GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute) |
|
GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second) |
|
GregorianCalendar(Locale aLocale) |
|
GregorianCalendar(TimeZone zone) |
|
GregorianCalendar(TimeZone zone,Locale aLocale) |
3.2从父类继承的方法
3.3该类中的方法
void |
add(int field, int amount) |
根据日历规则,将指定的(有符号的)时间量添加到给定的日历字段中。 |
Object |
clone() |
创建并返回此对象的一个副本。 |
boolean |
equals(Object obj) |
比较此 GregorianCalendar与指定的 Object。 |
int |
getActualMaximum(int field) |
考虑到给定的时间值和 getFirstDayOfWeek、getMinimalDaysInFirstWeek、getGregorianChange和 getTimeZone方法的当前值,返回此日历字段可能具有的最大值。 |
int |
getActualMinimum(int field) |
考虑到给定的时间值和 getFirstDayOfWeek、getMinimalDaysInFirstWeek、getGregorianChange和 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) |
使用给定的时区值来设置时区。 |