JAVA常用类之日期处理

在应用程序设计中,我们经常会用到日期时间,比如出生日期之类的,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 大的字段。
  • 使用模型
    为了帮助理解 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));
   }
}
上一篇:Linux 更改文件打开数和进程数、高并发调优


下一篇:puppet连载六:创建测试模块test