<时间-2>Date & DateFormat & SimpleDateFormat & Calendar
程序员文章站
2022-05-25 14:54:01
...
前面介绍了计算机世界里的一些时间概念,下面着重说明java里最常用的时间类和基本用法。
1.java.util.Date
此类代表一个精确到毫秒的特定时刻。
在 JDK 1.1 之前,类 Date 有两个额外的功能。它允许把日期解释为年、月、日、小时、分钟和秒值。它也允许格式化和解析日期字符串。不过,这些函数的 API 不易于实现国际化。从 JDK 1.1 开始,应该使用 Calendar 类(后面会介绍)实现日期和时间字段之间转换,使用 DateFormat (这是一个抽象类,我们常使用的是SimpleDateFormat)类来格式化和解析日期字符串。Date 中的相应方法已废弃。
尽管 Date 类打算反映协调世界时 (UTC),但无法做到如此准确,这取决于 Java 虚拟机的主机环境。当前几乎所有操作系统都假定 1 天 = 24 × 60 × 60 = 86400 秒。但对于 UTC,大约每一两年出现一次额外的一秒,称为“闰秒”。闰秒始终作为当天的最后一秒增加,并且始终在 12 月 31 日或 6 月 30 日增加。例如,1995 年的最后一分钟是 61 秒,因为增加了闰秒。大多数计算机时钟不是特别的准确,因此不能反映闰秒的差别。所以,Date类实际上表示的GMT时间。
Date类没有TimeZone时区和Locale区域的设置。同时对于对日期进行加、减、今天是周几等操作时,一般使推荐用Calendar。
1.1 Date的构造函数
常用的两个,
1)Date() //返回与地区时区无关的时间,只是toString方法会转换成当前jvm所在机器的本地时间。
打开源码可以看到:
获取的是系统当前时间距离1970年1月1日 GMT时间的毫秒数。内部实际调用的是Date(long date)。
2)Date(long date)
查看源码:
其中fastTime 的定义:private transient long fastTime;
为Date对象指定1970 年 1 月 1 日 00:00:00 GMT以来的毫秒数,据此Date的toString方法可以转换为jvm所在机器的本地时间。
经过上面两个构造函数产生的对象可以调用getTime()方法获取自1970年1月1日以来的GMT时间的毫秒数。这个值不会因为时区等外在因素改变,据此可以转换成任意需要的本地时间。对应,可以为已经构造好的Date对象重新设置时间,调用setTime(long time)。
除此之外指定年月日时分秒等形式的构造函数已经废弃,推荐Calendar或GregorianCalendar相应的构造函数。
1.2 常用方法
1)上面说的getTime(),setTime().
2)比较两个日期对象哪个先哪个后,after(Date when),before(Date when),compareTo(Date anotherDate),equals(Object obj),查看内部实现,其实这三个方法都是获取的距GMT时间1970年1月1日的毫秒数然后比较。
再除了clone(),toString(),hashCode()方法,其他方法都已经废弃。
所以,涉及到时间的运算操作,比如加减某个日期,或者单独获取/设置年月日时分秒需要使用替换类Calendar(或GregorianCalendar);如果是Date类型与String转换请使用DateFormat(或SimpleDateFormat)。
2.java.text.DateFormat/java.text.SimpleDateFormat
public abstract class DateFormat extends Format
可以看到DateFormat 是日期/时间(日期就是指年月日,时间指时分秒)格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年 1 月 1 日 00:00:00 这一刻开始的毫秒数。
DateFormat是抽象类,所以不能创建对象来使用,直接使用它的静态方法或字段,或通过子类来使用它的可重写方法。
查看源码可看到它提供了几个静态常量,用来表示格式化的时间或日期的格式,有全格式,长,中和短格式,默认“中格式”,表示格式化后日期时间展示出字段的完整性,使用之后才能看到实际效果:
对应使用这些常量的方式是public final static DateFormat getDateInstance()系列方法和public final static DateFormat getTimeInstance()系列方法和public final static DateFormat getDateTimeInstance()系列方法。查看源码可以看到,这些获取实例的方法返回的是SimpleDateFormat实例。通常获取实例后使用format方法来按照格式来格式化日期时间。一般情况DateFormat很少直接使用,而是使用子类SimpleDateFormat。
使用示例:
SimpleDateFormat是DateFormat的子类,可以实例化。SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
public class SimpleDateFormat extends DateFormat
2.1 日期和时间模式
日期和时间格式由日期和时间模式 字符串指定。在日期和时间模式字符串中,未加引号的字母 'A' 到 'Z' 和 'a' 到 'z' 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (') 引起来,以免进行解释。"''" 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在解析时与输入字符串进行匹配。
定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留):
讲到这里,不得不先对时区TimeZone和语言地区Locale进行说明。
public final class Locale extends Object implements Cloneable, Serializable
Locale 对象表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的 操作,它使用 Locale 为用户量身定制信息。例如,显示一个数值就是语言环境敏感的操作,应该根据用户的国家、地区或文化的风俗/传统来格式化该数值。
使用此类中的构造方法来创建 Locale:
比如:
以下展示每个构造器如何使用:
语言参数是一个有效的 ISO 语言代码。这些代码是由 ISO-639 定义的小写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.loc.gov/standards/iso639-2/englangn.html
示例:
Language Code
=====================
Arabic ar
German de
English en
Spanish es
Japanese ja
Hebrew he
China zh
国家/地区参数是一个有效的 ISO 国家/地区代码。这些代码是由 ISO-3166 定义的大写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
如:
Country Code
======================
China CH
Canada CA
France FR
Japan JP
Germany DE
预定义的Locale
Locale类里有几个Locale类型的静态成员实例.比如 说,Locale.FRANCE 就是预先就准备好的代表法国法语.你可在想要的地方用Locale.FRANCE也可以用new Locale("fr", "FR")的方式来实现.下面给出了一些预定义的现成Locale对象实例
Locale Name Locale(toString())
=======================================
Locale.CHINA zh_CN
Locale.CHINESE zh
Locale.SIMPLIFIED_CHINESE zh_CN
Locale.TRADITIONAL_CHINESE zh_TW
Locale.PRC zh_CN
Locale.* zh_TW
Locale.ENGLISH en
Locale.UK en_GB
Locale.US en_US
Locale.FRANCE fr_FR
Locale.FRENCH fr
public abstract class TimeZone extends Object implements Serializable, Cloneable
TimeZone 表示时区偏移量,也可以计算夏令时。
1)通常使用 getDefault 获取 TimeZone,getDefault 基于程序运行所在的时区创建 TimeZone。例如,对于在日本运行的程序,getDefault 基于日本标准时间创建 TimeZone 对象。
2)也可以用 getTimeZone 及时区 ID 获取 TimeZone 。例如美国太平洋时区的时区 ID 是 "America/Los_Angeles"。因此,可以使用下面语句获得美国太平洋时间 TimeZone 对象:
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
可以使用 getAvailableIDs 方法来对所有受支持的时区 ID 进行迭代。可以选择受支持的 ID 来获得 TimeZone。如果想要的时区无法用受支持的 ID 之一表示,那么可以指定自定义时区 ID 来生成 TimeZone。自定义时区 ID 的语法是:
CustomID:
GMT Sign Hours : Minutes
GMT Sign Hours Minutes
GMT Sign Hours
Sign: 下面之一
+ -
Hours:
Digit
Digit Digit
Minutes:
Digit Digit
Digit: 下面之一
0 1 2 3 4 5 6 7 8 9
Hours 必须在 0 至 23 之间,Minutes 必须在 00 至 59 之间。例如,"GMT+10" 和 "GMT+0010" 分别意味着比 GMT 提前 10 小时和 10 分钟。
格式是与区域无关的,并且数字必须取自 Unicode 标准的 Basic Latin 块。没有夏令时转换安排可以用自定义时区 ID 指定。如果指定的字符串与语法不匹配,就使用 "GMT"。
当创建一个 TimeZone 时,指定的自定义时区 ID 采用下面的语法进行标准化:
NormalizedCustomID:
GMT Sign TwoDigitHours : Minutes
Sign: 下面之一
+ -
TwoDigitHours:
Digit Digit
Minutes:
Digit Digit
Digit: 下面之一
0 1 2 3 4 5 6 7 8 9
例如,TimeZone.getTimeZone("GMT-8").getID() 返回 "GMT-08:00"。
三字母时区ID(已废弃)
为了与 JDK 1.1.x 兼容,一些三字母时区 ID(比如 "PST"、"CTT"、"AST")也受支持。但是,它们的使用被废弃,这是因为相同的缩写经常用于多个时区(例如,"CST" 可以是美国的 "Central Standard Time" 和 "China Standard Time"),但是 Java 平台只可以识别其中一种。
具体可以使用的方法可以查看api文档,比如获取和设置时区,获取默认时区等。
再回到SimpleDateFormat,此类有几个可以选用的构造函数,可以采用默认时区和语言地区,也可以自己指定,可以指定格式化时间的具体格式,格式指定就采用上面提到的日期和时间模式。如果需要中间改变格式,就使用applyPattern()方法指定新格式就可以了。
常用构造函数如下:
SimpleDateFormat()
用默认的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern, Locale locale)
用给定的模式和给定语言环境的默认日期格式符号构造 SimpleDateFormat。
举例:
2.2 跟时区相关的转换
SimpleDateFormat可以根据不同时区对同一个Date转换成不同的本地时间。
比如:
2.3 DateFormat 和 SimpleDateFormat 类非线程安全
DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。可以参考:
http://www.cnblogs.com/peida/archive/2013/05/31/3070790.html
要点总结:
1)频繁创建SimpleDateFormat类会导致性能低下。
2)可使用同步或threadLocal来解决并发问题。
3.java.util.Calendar
public abstract class Calendar extends Object
implements Serializable, Cloneable, Comparable<Calendar>
Calendar 类是一个抽象类(跟DateFormat类似),它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,GMT)的偏移量。
该类还为实现包范围外的具体日历系统提供了其他字段和方法。这些字段和方法被定义为 protected。
与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
因为Calendar是抽象类,此方法返回的实例实际上是它的子类GregorianCalendar的实例。
Calendar 对象能够生成为特定语言和日历风格实现日期-时间格式化所需的所有日历字段值,例如,日语-格里高里历,日语-传统日历。Calendar 定义了某些日历字段返回值的范围,以及这些值的含义。例如,对于所有日历,日历系统第一个月的值是 MONTH == JANUARY。其他值是由具体子类(例如 ERA)定义的。Calendar类与时区或地区相关。有关此内容的细节,请参阅每个字段的文档和子类文档。
字段操作
可以使用三种方法更改日历字段:set()、add() 和 roll()。
1)set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。
2)add(f, delta) 将 delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整:
Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。
Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f 发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。
此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将 DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。
3)roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:
Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。
示例:请参阅 GregorianCalendar.roll(int, int)。
使用模型。为了帮助理解 add() 和 roll() 的行为,假定有一个用户界面组件,它带有用于月、日、年和底层 GregorianCalendar 的递增或递减按钮。如果从界面上读取的日期为 1999 年 1 月 31 日,并且用户按下月份的递增按钮,那么应该得到什么?如果底层实现使用 set(),那么可以将该日期读为 1999 年 3 月 3 日。更好的结果是 1999 年 2 月 28 日。此外,如果用户再次按下月份的递增按钮,那么该日期应该读为 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通过保存原始日期并使用 add() 或 roll(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。
使用示例:
另外,可以再去参考jdk介绍Calendar的子类GregorianCalendar。
4.总结
简单总结,主要是明确不同类的使用场景。
Date类就是用来表示与本地地区时区无关的某个确切的时间点,比如对时间进行各种操作之后用来表示一个确切的时间。
DateFormat(SimpleDateFormat)是与时区和地区相关的,可以对时间按指定格式进行format和parse。指定Date,对于不同时区(TimeZone),用DateFormatformat之后的时间是不同的。
Calendar类也是与地区和时区相关的,可以对Date的每个字段(年月日,时分秒)等进行单独的操作,进行时间的增减,比较等运算。
还可以参考:
http://zhujinguo.iteye.com/blog/702013
http://hi.baidu.com/hqheeonuxybeiuq/item/43ee7645013b38eca4c06636
1.java.util.Date
此类代表一个精确到毫秒的特定时刻。
在 JDK 1.1 之前,类 Date 有两个额外的功能。它允许把日期解释为年、月、日、小时、分钟和秒值。它也允许格式化和解析日期字符串。不过,这些函数的 API 不易于实现国际化。从 JDK 1.1 开始,应该使用 Calendar 类(后面会介绍)实现日期和时间字段之间转换,使用 DateFormat (这是一个抽象类,我们常使用的是SimpleDateFormat)类来格式化和解析日期字符串。Date 中的相应方法已废弃。
尽管 Date 类打算反映协调世界时 (UTC),但无法做到如此准确,这取决于 Java 虚拟机的主机环境。当前几乎所有操作系统都假定 1 天 = 24 × 60 × 60 = 86400 秒。但对于 UTC,大约每一两年出现一次额外的一秒,称为“闰秒”。闰秒始终作为当天的最后一秒增加,并且始终在 12 月 31 日或 6 月 30 日增加。例如,1995 年的最后一分钟是 61 秒,因为增加了闰秒。大多数计算机时钟不是特别的准确,因此不能反映闰秒的差别。所以,Date类实际上表示的GMT时间。
Date类没有TimeZone时区和Locale区域的设置。同时对于对日期进行加、减、今天是周几等操作时,一般使推荐用Calendar。
1.1 Date的构造函数
常用的两个,
1)Date() //返回与地区时区无关的时间,只是toString方法会转换成当前jvm所在机器的本地时间。
打开源码可以看到:
public Date() { this(System.currentTimeMillis()); }
获取的是系统当前时间距离1970年1月1日 GMT时间的毫秒数。内部实际调用的是Date(long date)。
2)Date(long date)
查看源码:
public Date(long date) { fastTime = date; }
其中fastTime 的定义:private transient long fastTime;
为Date对象指定1970 年 1 月 1 日 00:00:00 GMT以来的毫秒数,据此Date的toString方法可以转换为jvm所在机器的本地时间。
经过上面两个构造函数产生的对象可以调用getTime()方法获取自1970年1月1日以来的GMT时间的毫秒数。这个值不会因为时区等外在因素改变,据此可以转换成任意需要的本地时间。对应,可以为已经构造好的Date对象重新设置时间,调用setTime(long time)。
除此之外指定年月日时分秒等形式的构造函数已经废弃,推荐Calendar或GregorianCalendar相应的构造函数。
1.2 常用方法
1)上面说的getTime(),setTime().
2)比较两个日期对象哪个先哪个后,after(Date when),before(Date when),compareTo(Date anotherDate),equals(Object obj),查看内部实现,其实这三个方法都是获取的距GMT时间1970年1月1日的毫秒数然后比较。
再除了clone(),toString(),hashCode()方法,其他方法都已经废弃。
所以,涉及到时间的运算操作,比如加减某个日期,或者单独获取/设置年月日时分秒需要使用替换类Calendar(或GregorianCalendar);如果是Date类型与String转换请使用DateFormat(或SimpleDateFormat)。
2.java.text.DateFormat/java.text.SimpleDateFormat
public abstract class DateFormat extends Format
可以看到DateFormat 是日期/时间(日期就是指年月日,时间指时分秒)格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年 1 月 1 日 00:00:00 这一刻开始的毫秒数。
DateFormat是抽象类,所以不能创建对象来使用,直接使用它的静态方法或字段,或通过子类来使用它的可重写方法。
查看源码可看到它提供了几个静态常量,用来表示格式化的时间或日期的格式,有全格式,长,中和短格式,默认“中格式”,表示格式化后日期时间展示出字段的完整性,使用之后才能看到实际效果:
/** * Constant for full style pattern. */ public static final int FULL = 0; /** * Constant for long style pattern. */ public static final int LONG = 1; /** * Constant for medium style pattern. */ public static final int MEDIUM = 2; /** * Constant for short style pattern. */ public static final int SHORT = 3; /** * Constant for default style pattern. Its value is MEDIUM. */ public static final int DEFAULT = MEDIUM;
对应使用这些常量的方式是public final static DateFormat getDateInstance()系列方法和public final static DateFormat getTimeInstance()系列方法和public final static DateFormat getDateTimeInstance()系列方法。查看源码可以看到,这些获取实例的方法返回的是SimpleDateFormat实例。通常获取实例后使用format方法来按照格式来格式化日期时间。一般情况DateFormat很少直接使用,而是使用子类SimpleDateFormat。
使用示例:
Date date = new Date(); String s; s = DateFormat.getDateTimeInstance().format(date); System.out.println("dateTime:" + s); /** 输出格式: 2014-1-24 */ s = DateFormat.getDateInstance().format(date); System.out.println("date:" + s); s = DateFormat.getTimeInstance().format(date); System.out.println("time:" + s); /** 输出格式: 2014-1-24 */ s = DateFormat.getDateInstance(DateFormat.DEFAULT).format(date); System.out.println("date:" + s); /** 输出格式: 17:40:54 */ s = DateFormat.getTimeInstance(DateFormat.DEFAULT).format(date); System.out.println("time:" + s); /** 输出格式: 2014-1-24 */ s = DateFormat.getDateInstance(DateFormat.MEDIUM).format(date); System.out.println("date:" + s); s = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date); System.out.println("time:" + s); /** 输出格式: 2014年1月24日 星期五*/ s = DateFormat.getDateInstance(DateFormat.FULL).format(date); System.out.println("date:" + s); s = DateFormat.getTimeInstance(DateFormat.FULL).format(date); System.out.println("time:" + s); /** 输出格式: 2014年1月24日*/ s = DateFormat.getDateInstance(DateFormat.LONG).format(date); System.out.println("date:" + s); s = DateFormat.getTimeInstance(DateFormat.LONG).format(date); System.out.println("time:" + s); /** 输出格式: 14-1-24 */ s = DateFormat.getDateInstance(DateFormat.SHORT).format(date); System.out.println("date:" + s); s = DateFormat.getTimeInstance(DateFormat.SHORT).format(date); System.out.println("time:" + s); 输出结果: dateTime:2014-1-24 17:40:54 date:2014-1-24 time:17:40:54 date:2014-1-24 time:17:40:54 date:2014-1-24 time:17:40:54 date:2014年1月24日 星期五 time:下午05时40分54秒 CST date:2014年1月24日 time:下午05时40分54秒 date:14-1-24 time:下午5:40
SimpleDateFormat是DateFormat的子类,可以实例化。SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
public class SimpleDateFormat extends DateFormat
2.1 日期和时间模式
日期和时间格式由日期和时间模式 字符串指定。在日期和时间模式字符串中,未加引号的字母 'A' 到 'Z' 和 'a' 到 'z' 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (') 引起来,以免进行解释。"''" 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在解析时与输入字符串进行匹配。
定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留):
讲到这里,不得不先对时区TimeZone和语言地区Locale进行说明。
public final class Locale extends Object implements Cloneable, Serializable
Locale 对象表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的 操作,它使用 Locale 为用户量身定制信息。例如,显示一个数值就是语言环境敏感的操作,应该根据用户的国家、地区或文化的风俗/传统来格式化该数值。
使用此类中的构造方法来创建 Locale:
Locale(String language) Locale(String language, String country) Locale(String language, String country, String variant)
比如:
以下展示每个构造器如何使用:
//创建一个通用英语的locale. Locale locale1 = new Locale("en"); //创建一个加拿大英语的locale. Locale locale2 = new Locale("en", "CA"); //创建一个美式英语的locale //硅谷的英语 Locale locale3 = new Locale("en", "US", "SiliconValley");
语言参数是一个有效的 ISO 语言代码。这些代码是由 ISO-639 定义的小写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.loc.gov/standards/iso639-2/englangn.html
示例:
Language Code
=====================
Arabic ar
German de
English en
Spanish es
Japanese ja
Hebrew he
China zh
国家/地区参数是一个有效的 ISO 国家/地区代码。这些代码是由 ISO-3166 定义的大写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:
http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
如:
Country Code
======================
China CH
Canada CA
France FR
Japan JP
Germany DE
预定义的Locale
Locale类里有几个Locale类型的静态成员实例.比如 说,Locale.FRANCE 就是预先就准备好的代表法国法语.你可在想要的地方用Locale.FRANCE也可以用new Locale("fr", "FR")的方式来实现.下面给出了一些预定义的现成Locale对象实例
Locale Name Locale(toString())
=======================================
Locale.CHINA zh_CN
Locale.CHINESE zh
Locale.SIMPLIFIED_CHINESE zh_CN
Locale.TRADITIONAL_CHINESE zh_TW
Locale.PRC zh_CN
Locale.* zh_TW
Locale.ENGLISH en
Locale.UK en_GB
Locale.US en_US
Locale.FRANCE fr_FR
Locale.FRENCH fr
public abstract class TimeZone extends Object implements Serializable, Cloneable
TimeZone 表示时区偏移量,也可以计算夏令时。
1)通常使用 getDefault 获取 TimeZone,getDefault 基于程序运行所在的时区创建 TimeZone。例如,对于在日本运行的程序,getDefault 基于日本标准时间创建 TimeZone 对象。
2)也可以用 getTimeZone 及时区 ID 获取 TimeZone 。例如美国太平洋时区的时区 ID 是 "America/Los_Angeles"。因此,可以使用下面语句获得美国太平洋时间 TimeZone 对象:
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
可以使用 getAvailableIDs 方法来对所有受支持的时区 ID 进行迭代。可以选择受支持的 ID 来获得 TimeZone。如果想要的时区无法用受支持的 ID 之一表示,那么可以指定自定义时区 ID 来生成 TimeZone。自定义时区 ID 的语法是:
CustomID:
GMT Sign Hours : Minutes
GMT Sign Hours Minutes
GMT Sign Hours
Sign: 下面之一
+ -
Hours:
Digit
Digit Digit
Minutes:
Digit Digit
Digit: 下面之一
0 1 2 3 4 5 6 7 8 9
Hours 必须在 0 至 23 之间,Minutes 必须在 00 至 59 之间。例如,"GMT+10" 和 "GMT+0010" 分别意味着比 GMT 提前 10 小时和 10 分钟。
格式是与区域无关的,并且数字必须取自 Unicode 标准的 Basic Latin 块。没有夏令时转换安排可以用自定义时区 ID 指定。如果指定的字符串与语法不匹配,就使用 "GMT"。
当创建一个 TimeZone 时,指定的自定义时区 ID 采用下面的语法进行标准化:
NormalizedCustomID:
GMT Sign TwoDigitHours : Minutes
Sign: 下面之一
+ -
TwoDigitHours:
Digit Digit
Minutes:
Digit Digit
Digit: 下面之一
0 1 2 3 4 5 6 7 8 9
例如,TimeZone.getTimeZone("GMT-8").getID() 返回 "GMT-08:00"。
三字母时区ID(已废弃)
为了与 JDK 1.1.x 兼容,一些三字母时区 ID(比如 "PST"、"CTT"、"AST")也受支持。但是,它们的使用被废弃,这是因为相同的缩写经常用于多个时区(例如,"CST" 可以是美国的 "Central Standard Time" 和 "China Standard Time"),但是 Java 平台只可以识别其中一种。
具体可以使用的方法可以查看api文档,比如获取和设置时区,获取默认时区等。
再回到SimpleDateFormat,此类有几个可以选用的构造函数,可以采用默认时区和语言地区,也可以自己指定,可以指定格式化时间的具体格式,格式指定就采用上面提到的日期和时间模式。如果需要中间改变格式,就使用applyPattern()方法指定新格式就可以了。
常用构造函数如下:
SimpleDateFormat()
用默认的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern, Locale locale)
用给定的模式和给定语言环境的默认日期格式符号构造 SimpleDateFormat。
举例:
public static void simpleDateFormatTest() { Date date = new Date(); // 使用无参构造函数 SimpleDateFormat sdf = new SimpleDateFormat(); // 使用applyPattern改变格式 sdf.applyPattern("yyyy/MM/dd hh:mm:ss"); System.out.println("yyyy/MM/dd hh:mm:ss = " + sdf.format(date)); // 注意跟上面的区别 ,最后有个a,会输出时间是上午还是下午。 sdf.applyPattern("yyyy/MM/dd hh:mm:ss a"); System.out.println("yyyy/MM/dd hh:mm:ss = " + sdf.format(date)); // 注意跟上面的区别,小时用的是大写H,代表24小时制。小写h代表12小时制,所以需要指明上午下午。 sdf.applyPattern("yyyy/MM/dd HH:mm:ss"); System.out.println("yyyy/MM/dd HH:mm:ss = " + sdf.format(date)); // 格式之间可是添加任意自己的说明语言 sdf.applyPattern("今天是yyyy年MM月dd日 HH:mm:ss"); System.out.println("今天是yyyy年MM月dd日 HH:mm:ss = " + sdf.format(date)); // 使用带格式参数的构造函数。注意跟上面的区别,只用一个M和d,这样1月就会展示1月,否则展示01月,但10月仍会展示成10月。d的区别一样。 sdf = new SimpleDateFormat("yyyy/M/d HH:mm:ss"); System.out.println("yyyy/M/d HH:mm:ss = " + sdf.format(date)); System.out.println("************"); try { // 此时sdf的格式是yyyy/M/d HH:mm:ss,因此可以正确解析“2014/1/12 22:32:34” 或 “2014/01/12 22:32:34” System.out.println("parse('2014/1/12 22:32:34') = " + sdf.parse("2014/1/12 22:32:34")); System.out.println("parse('2014/01/12 22:32:34') = " + sdf.parse("2014/01/12 22:32:34")); // 无法正确解析此格式。 System.out.println("parse('2014-1-12 22:32:34') = " + sdf.parse("2014-1-12 22:32:34")); } catch (ParseException e) { System.out.println("parse exception" + e); } } 输出结果: yyyy/MM/dd hh:mm:ss = 2014/02/06 09:49:37 yyyy/MM/dd hh:mm:ss = 2014/02/06 09:49:37 下午 yyyy/MM/dd HH:mm:ss = 2014/02/06 21:49:37 今天是yyyy年MM月dd日 HH:mm:ss = 今天是2014年02月06日 21:49:37 yyyy/M/d HH:mm:ss = 2014/2/6 21:49:37 ************ parse('2014/1/12 22:32:34') = Sun Jan 12 22:32:34 CST 2014 parse('2014/01/12 22:32:34') = Sun Jan 12 22:32:34 CST 2014 parse exceptionjava.text.ParseException: Unparseable date: "2014-1-12 22:32:34"
2.2 跟时区相关的转换
SimpleDateFormat可以根据不同时区对同一个Date转换成不同的本地时间。
比如:
// 按时区进行格式化时间 Date now = new Date(); SimpleDateFormat simpledDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String snow = simpledDF.format(now); // 2014-02-07 10:48:45 System.out.println("snow(gmt+8) = " + snow); simpledDF.setTimeZone(TimeZone.getTimeZone("GMT+9")); snow = simpledDF.format(now); // 2014-02-07 11:48:45 System.out.println("snow(gmt+9) = " + snow); 输出: snow(gmt+8) = 2014-02-07 10:48:45 snow(gmt+9) = 2014-02-07 11:48:45
2.3 DateFormat 和 SimpleDateFormat 类非线程安全
DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。可以参考:
http://www.cnblogs.com/peida/archive/2013/05/31/3070790.html
要点总结:
1)频繁创建SimpleDateFormat类会导致性能低下。
2)可使用同步或threadLocal来解决并发问题。
3.java.util.Calendar
public abstract class Calendar extends Object
implements Serializable, Cloneable, Comparable<Calendar>
Calendar 类是一个抽象类(跟DateFormat类似),它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,GMT)的偏移量。
该类还为实现包范围外的具体日历系统提供了其他字段和方法。这些字段和方法被定义为 protected。
与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
因为Calendar是抽象类,此方法返回的实例实际上是它的子类GregorianCalendar的实例。
Calendar 对象能够生成为特定语言和日历风格实现日期-时间格式化所需的所有日历字段值,例如,日语-格里高里历,日语-传统日历。Calendar 定义了某些日历字段返回值的范围,以及这些值的含义。例如,对于所有日历,日历系统第一个月的值是 MONTH == JANUARY。其他值是由具体子类(例如 ERA)定义的。Calendar类与时区或地区相关。有关此内容的细节,请参阅每个字段的文档和子类文档。
字段操作
可以使用三种方法更改日历字段:set()、add() 和 roll()。
1)set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。
2)add(f, delta) 将 delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整:
Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。
Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f 发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。
此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。
示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将 DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。
3)roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:
Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。
示例:请参阅 GregorianCalendar.roll(int, int)。
使用模型。为了帮助理解 add() 和 roll() 的行为,假定有一个用户界面组件,它带有用于月、日、年和底层 GregorianCalendar 的递增或递减按钮。如果从界面上读取的日期为 1999 年 1 月 31 日,并且用户按下月份的递增按钮,那么应该得到什么?如果底层实现使用 set(),那么可以将该日期读为 1999 年 3 月 3 日。更好的结果是 1999 年 2 月 28 日。此外,如果用户再次按下月份的递增按钮,那么该日期应该读为 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通过保存原始日期并使用 add() 或 roll(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。
使用示例:
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00")); //获取东八区时间 int year = c.get(Calendar.YEAR); //获取年 int month = c.get(Calendar.MONTH) + 1; //获取月份,0表示1月份 int day = c.get(Calendar.DAY_OF_MONTH); //获取当前天数 int first = c.getActualMinimum(c.DAY_OF_MONTH); //获取本月最小天数 int last = c.getActualMaximum(c.DAY_OF_MONTH); //获取本月最大天数 int time = c.get(Calendar.HOUR_OF_DAY); //获取当前小时 int min = c.get(Calendar.MINUTE); //获取当前分钟 int xx = c.get(Calendar.SECOND); //获取当前秒
另外,可以再去参考jdk介绍Calendar的子类GregorianCalendar。
4.总结
简单总结,主要是明确不同类的使用场景。
Date类就是用来表示与本地地区时区无关的某个确切的时间点,比如对时间进行各种操作之后用来表示一个确切的时间。
DateFormat(SimpleDateFormat)是与时区和地区相关的,可以对时间按指定格式进行format和parse。指定Date,对于不同时区(TimeZone),用DateFormatformat之后的时间是不同的。
Calendar类也是与地区和时区相关的,可以对Date的每个字段(年月日,时分秒)等进行单独的操作,进行时间的增减,比较等运算。
还可以参考:
http://zhujinguo.iteye.com/blog/702013
http://hi.baidu.com/hqheeonuxybeiuq/item/43ee7645013b38eca4c06636
上一篇: PHP判断数据库中的记录是否存在的方法
推荐阅读
-
16_常用API_第16天(正则表达式、Date、DateFormat、Calendar)_讲义
-
朋友圈发布时间(Date、DateFormat、Calendar)
-
Date,DateFormat,Calendar类的使用
-
Java基础学习笔记三 正则表达式和校验、Date、DateFormat、Calendar
-
Java13-day04【Integer、int和String的相转、自动装箱和拆箱、Date、SimpleDateFormat、Calendar、异常、try...catch、throws】
-
JAVA基础之Date类、DateFormat类及Calendar类
-
java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生
-
新手小白学JAVA 日期类Date SimpleDateFormat Calendar(入门)
-
JDK1.7及之前日期时间相关类:Date类、SimpleDateFormat类、Calender类
-
Java_Day013_正则表达式、Date、DateFormat、SimpleDateFormat、Calendar、Math