在应用程序设计中,我们经常会用到日期时间,比如出生日期之类的,JAVA中为我们提供了一些处理日期的类。这一片博客将为大家介绍一下。
java.util.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 日的相同含义。
构造方法
下表中展示的为Date类的构造方法,已经过时的方法没有列出:
方法名 | 说明 |
---|---|
Date() | 分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒) |
Date(long date) | 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数 |
方法摘要
返回值 | 方法名 | 说明 |
---|---|---|
boolean | after(Date when) | 测试此日期是否在指定日期之后 |
boolean | before(Date when) | 测试此日期是否在指定日期之前 |
Object | clone() | 返回此对象的副本 |
int | compareTo(Date anotherDate) | 比较两个日期的顺序 |
boolean | equals(Object obj) | 比较两个日期的相等性 |
long | getTime() | 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数 |
int | hashCode() | 返回此对象的哈希码值 |
void | setTime(long time) | 设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点 |
String | toString() | 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat) |
使用示例
import java.util.Date;
public class DateDemo
{
public static void main(String[] args)
{
// 获得当前时间
Date now = new Date();
// 表示的毫秒数
long time = now.getTime();
// 创建一个过去的时间
Date before = new Date(time - 1000);
// 创建一个未来的时间
Date after = new Date(time + 1000);
// 时间比较
System.out.println(now.after(before));
System.out.println(now.before(after));
}
}
输出结果为:
true
true
java.util.Calendar
Date类可以很方便的表示时间,但是诸如获得当前小时、分钟等方法已经被标记为过时的方法。所以这里为大家介绍Calendar类。
Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
Calendar 对象能够生成为特定语言和日历风格实现日期-时间格式化所需的所有日历字段值,例如,日语-格里高里历,日语-传统日历。Calendar 定义了某些日历字段返回值的范围,以及这些值的含义。
- 获得并设置日历字段值
可以通过调用 set 方法来设置日历字段值。在需要计算时间值(距历元所经过的毫秒)或日历字段值之前,不会解释 Calendar 中的所有字段值设置。调用 get、getTimeInMillis、getTime、add 和 roll 涉及此类计算。 - 宽松性
Calendar 有两种解释日历字段的模式,即 lenient 和 non-lenient。当 Calendar 处于 lenient 模式时,它可接受比它所生成的日历字段范围更大范围内的值。当 Calendar 重新计算日历字段值,以便由 get() 返回这些值时,所有日历字段都被标准化。例如,lenient 模式下的 GregorianCalendar 将 MONTH == JANUARY、DAY_OF_MONTH == 32 解释为 February 1。
当 Calendar 处于 non-lenient 模式时,如果其日历字段中存在任何不一致性,它都会抛出一个异常。例如,GregorianCalendar 总是在 1 与月份的长度之间生成 DAY_OF_MONTH 值。如果已经设置了任何超出范围的字段值,那么在计算时间或日历字段值时,处于 non-lenient 模式下的 GregorianCalendar 会抛出一个异常。 - 第一个星期
Calendar 使用两个参数定义了特定于语言环境的 7 天制星期:星期的第一天和第一个星期中的最小一天(从 1 到 7)。这些数字取自构造 Calendar 时的语言环境资源数据。还可以通过为其设置值的方法来显式地指定它们。
在设置或获得 WEEK_OF_MONTH 或 WEEK_OF_YEAR 字段时,Calendar 必须确定一个月或一年的第一个星期,以此作为参考点。一个月或一年的第一个星期被确定为开始于 getFirstDayOfWeek() 的最早七天,它最少包含那一个月或一年的getMinimalDaysInFirstWeek() 天数。第一个星期之前的各星期编号为 …、-1、0;之后的星期编号为 2、3、…。注意,get() 返回的标准化编号方式可能有所不同。例如,特定 Calendar 子类可能将某一年第 1 个星期之前的那个星期指定为前一年的第 n 个星期。 - 日历字段解析
在计算日历字段中的日期和时间时,可能没有足够的信息用于计算(例如只有年和月,但没有日),或者可能有不一致的信息( 例如 “Tuesday, July 15, 1996”(格林威治时间)——实际上,1996 年 7 月 15 日是星期一 )。Calendar 将解析日历字段值,以便用以下方式确定日期和时间。
如果日历字段值中存在任何冲突,则 Calendar 将为最近设置的日历字段提供优先权。以下是日历字段的默认组合。将使用由最近设置的单个字段所确定的最近组合。
对于日期字段:
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
对于时间字段:
HOUR_OF_DAY
AM_PM + HOUR
如果在选定的字段组合中,还有尚未设置值的任一日历字段,那么 Calendar 将使用其默认值。每个字段的默认值可能依据具体的日历系统而有所不同。例如,在 GregorianCalendar 中,字段的默认值与历元起始部分的字段值相同:即 YEAR = 1970、MONTH = JANUARY、DAY_OF_MONTH = 1,等等。
注: 对于某些特别时间的解释可能会有某些歧义,可以用下列方式解决:
23:59 是一天中的最后一分钟,而 00:00 是下一天的第一分钟。因此,1999 年 12 月 31 日的 23:59 < 2000 年 1 月 1 日的 00:00。
尽管从历史上看不够精确,但午夜也属于 “am”,,中午属于 “pm”,所以在同一天,12:00 am ( 午夜 ) < 12:01 am,12:00 pm ( 中午 ) < 12:01 pm。
日期或时间格式字符串不是日历定义的一部分,因为在运行时,用户必须能够修改或重写它们。可以使用 DateFormat 格式化日期。
- 字段操作
可以使用三种方法更改日历字段:set()、add() 和 roll()。
- 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() 之后没有发生重新计算。 - 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 中的月份发生变化时,该值也需要发生变化。 - roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整:
Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH 是一个比 HOUR 大的字段。
- set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set() 更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。
- 使用模型
为了帮助理解 add() 和 roll() 的行为,假定有一个用户界面组件,它带有用于月、日、年和底层 GregorianCalendar 的递增或递减按钮。如果从界面上读取的日期为 1999 年 1 月 31 日,并且用户按下月份的递增按钮,那么应该得到什么?如果底层实现使用 set(),那么可以将该日期读为 1999 年 3 月 3 日。更好的结果是 1999 年 2 月 28 日。此外,如果用户再次按下月份的递增按钮,那么该日期应该读为 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通过保存原始日期并使用 add() 或 roll(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。
构造方法
方法名 | 说明 |
---|---|
protected Calendar() | 构造一个带有默认时区和语言环境的 Calendar |
protected Calendar(TimeZone zone, Locale aLocale) | 构造一个带有指定时区和语言环境的 Calendar |
方法摘要
返回值 | 方法名 | 说明 |
---|---|---|
abstract void | add(int field, int amount) | 根据日历的规则,为给定的日历字段添加或减去指定的时间量 |
boolean | after(Object when) | 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之后,返回判断结果 |
boolean | before(Object when) | 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之前,返回判断结果 |
void | clear() | 将此 Calendar 的所日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义 |
void | clear(int field) | 将此 Calendar 的给定日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义 |
Object | clone() | 创建并返回此对象的一个副本 |
int | compareTo(Calendar anotherCalendar) | 比较两个 Calendar 对象表示的时间值(从历元至现在的毫秒偏移量) |
protected void | complete() | 填充日历字段中所有未设置的字段 |
protected abstract void | computeFields() | 将当前毫秒时间值 time 转换为 fields[] 中的日历字段值 |
protected abstract void | computeTime() | 将 fields[] 中的当前日历字段值转换为毫秒时间值 time |
boolean | equals(Object obj) | 将此 Calendar 与指定 Object 比较 |
int | get(int field) | 返回给定日历字段的值 |
int | getActualMaximum(int field) | 给定此 Calendar 的时间值,返回指定日历字段可能拥有的最大值 |
int | getActualMinimum(int field) | 给定此 Calendar 的时间值,返回指定日历字段可能拥有的最小值 |
static Locale[] | getAvailableLocales() | 返回所有语言环境的数组,此类的 getInstance 方法可以为其返回本地化的实例 |
String | getDisplayName(int field, int style, Locale locale) | 返回给定style和locale下的日历field值的字符串表示形式 |
Map< String, Integer> | getDisplayNames(int field, int style, Locale locale) | 返回给定style和locale下包含日历 field 所有名称的Map及其相应字段值 |
int | getFirstDayOfWeek() | 获取一星期的第一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY |
abstract int | getGreatestMinimum(int field) | 返回此 Calendar 实例给定日历字段的最高的最小值 |
static Calendar | getInstance() | 使用默认时区和语言环境获得一个日历 |
static Calendar | getInstance(Locale aLocale) | 使用默认时区和指定语言环境获得一个日历 |
static Calendar | getInstance(TimeZone zone) | 使用指定时区和默认语言环境获得一个日历 |
static Calendar | getInstance(TimeZone zone, Locale aLocale) | 使用指定时区和语言环境获得一个日历 |
abstract int | getLeastMaximum(int field) | 返回此 Calendar 实例给定日历字段的最低的最大值 |
abstract int | getMaximum(int field) | 返回此 Calendar 实例给定日历字段的最大值 |
int | getMinimalDaysInFirstWeek() | 获取一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则此方法将返回 1 |
abstract int | getMinimum(int field) | 返回此 Calendar 实例给定日历字段的最小值 |
Date | getTime() | 返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象 |
long | getTimeInMillis() | 返回此 Calendar 的时间值,以毫秒为单位 |
TimeZone | getTimeZone() | 获得时区 |
int | hashCode() | 返回该此日历的哈希码 |
protected int | internalGet(int field) | 返回给定日历字段的值 |
boolean | isLenient() | 判断日期/时间的解释是否为宽松的 |
boolean | isSet(int field) | 确定给定日历字段是否已经设置了一个值,其中包括因为调用 get 方法触发内部字段计算而导致已经设置该值的情况 |
abstract void | roll(int field, boolean up) | 在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段 |
void | roll(int field, int amount) | 向指定日历字段添加指定(有符号的)时间量,不更改更大的字段 |
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 | setFirstDayOfWeek(int value) | 设置一星期的第一天是哪一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY |
void | setLenient(boolean lenient) | 指定日期/时间解释是否是宽松的 |
void | setMinimalDaysInFirstWeek(int value) | 设置一年中第一个星期所需的最少天数,例如,如果定义第一个星期包含一年第一个月的第一天,则使用值 1 调用此方法 |
void | setTime(Date date) | 使用给定的 Date 设置此 Calendar 的时间 |
void | setTimeInMillis(long millis) | 用给定的 long 值设置此 Calendar 的当前时间值 |
void | setTimeZone(TimeZone value) | 使用给定的时区值来设置时区 |
String | toString() | 返回此日历的字符串表示形式 |
使用示例
import java.util.Calendar;
public class CalendarDemo
{
public static void main(String[] args)
{
// 获得当前时间
Calendar now = Calendar.getInstance();
System.out.println("年:" + now.get(Calendar.YEAR));
System.out.println("月:" + now.get(Calendar.MONTH));
System.out.println("日:" + now.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" + now.get(Calendar.HOUR_OF_DAY));
System.out.println("分:" + now.get(Calendar.MINUTE));
System.out.println("秒:" + now.get(Calendar.SECOND));
System.out.println("豪秒:" + now.get(Calendar.MILLISECOND));
System.out.println("星期:" + now.get(Calendar.DAY_OF_WEEK));
}
}