欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java中时间日期的操作

程序员文章站 2022-05-27 17:05:48
...

Java中时间日期的操作

相关的类

Date

java.util.Date
很多方法已经过时,现在主要用于在Calendar类和String转换间的一个存储介质.
所有已实现的接口:
Serializable, Cloneable, Comparable
子类:
Date, Time, Timestamp
构造方法:

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

主要方法:

 boolean    after(Date when):测试此日期是否在指定日期之后。
 boolean    before(Date when):测试此日期是否在指定日期之前。

Calendar

目前用于操作时间对象的主要类,是一个抽象类没有构造方法,通过
静态方法Calendar.getInstance() 来得到Calendar对象。
所有已实现的接口:
Serializable, Cloneable, Comparable
子类:
GregorianCalendar
构造方法:
public static Calendar getInstance(TimeZone zone,Locale aLocale)

(无参数,任意一个参数也可)使用指定时区和语言环境获得一个日历。返回的 Calendar 基于当前时间,使用了给定的时区和给定的语言环境。

参数:
    zone - 要使用的时区 (默认为系统时区)
    aLocale - 用于星期数据的语言环境 (默认为系统语言,常用的还有Locale.ENGLISH)
返回:
    一个 Calendar。 

主要方法:

Date    getTime()
          返回一个表示此 Calendar 时间值(从历元至现在的毫秒偏移量)的 Date 对象。
void    setTime(Date date)
          使用给定的 Date 设置此 Calendar 的时间。
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 的值。
int     get(int field)
          返回给定日历字段的值。
abstract  void  add(int field, int amount)
          根据日历的规则,为给定的日历字段添加或减去指定的时间量。
abstract  void  roll(int field, boolean up)
          在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。
void    roll(int field, int amount)
          向指定日历字段添加指定(有符号的)时间量,不更改更大的字段。

注意事项:
- 宽松性

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(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。

SimpleDateFormat

主要用于字符串和Date类型的相互转换
父类:

java.lang.Object
  继承者 java.text.Format
      继承者 java.text.DateFormat
          继承者 java.text.SimpleDateFormat

构造方法:

SimpleDateFormat()
          用默认的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern)
          用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
          用给定的模式和日期符号构造 SimpleDateFormat。
SimpleDateFormat(String pattern, Locale locale)
          用给定的模式和给定语言环境的默认日期格式符号构造 SimpleDateFormat。

主要方法:

Date    parse(String source)
          从给定字符串的开始解析文本,以生成一个日期。
String  format(Date date)
          将一个 Date 格式化为日期/时间字符串。

注意事项:
日期和时间格式由日期和时间模式 字符串指定。在日期和时间模式字符串中,未加引号的字母 ‘A’ 到 ‘Z’ 和 ‘a’ 到 ‘z’ 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (‘) 引起来,以免进行解释。””” 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在解析时与输入字符串进行匹配。
模式字母对应表如下:
Java中时间日期的操作
母通常是重复的,其数量确定其精确表示:

  • Text: 对于格式化来说,如果模式字母的数量大于等于 4,则使用完全形式;否则,在可用的情况下使用短形式或缩写形式。对于解析来说,两种形式都是可接受的,与模式字母的数量无关。

  • Number: 对于格式化来说,模式字母的数量是最小的数位,如果数位不够,则用 0 填充以达到此数量。对于解析来说,模式字母的数量被忽略,除非必须分开两个相邻字段。

  • Year: 如果格式器的 Calendar 是格里高利历,则应用以下规则。
    对于格式化来说,如果模式字母的数量为 2,则年份截取为 2 位数,否则将年份解释为 number。
    对于解析来说,如果模式字母的数量大于 2,则年份照字面意义进行解释,而不管数位是多少。因此使用模式 “MM/dd/yyyy”,将 “01/11/12” 解析为公元 12 年 1 月 11 日。
    在解析缩写年份模式(”y” 或 “yy”)时,SimpleDateFormat 必须相对于某个世纪来解释缩写的年份。这通过将日期调整为 SimpleDateFormat 实例创建之前的 80 年和之后 20 年范围内来完成。例如,在 “MM/dd/yy” 模式下,如果 SimpleDateFormat 实例是在 1997 年 1 月 1 日创建的,则字符串 “01/11/12” 将被解释为 2012 年 1 月 11 日,而字符串 “05/04/64” 将被解释为 1964 年 5 月 4 日。在解析时,只有恰好由两位数字组成的字符串(如 Character.isDigit(char) 所定义的)被解析为默认的世纪。其他任何数字字符串将照字面意义进行解释,例如单数字字符串,3 个或更多数字组成的字符串,或者不都是数字的两位数字字符串(例如”-1”)。因此,在相同的模式下, “01/02/3” 或 “01/02/003” 解释为公元 3 年 1 月 2 日。同样,”01/02/-3” 解析为公元前 4 年 1 月 2 日。
    否则,则应用日历系统特定的形式。对于格式化和解析,如果模式字母的数量为 4 或大于 4,则使用日历特定的 long form。否则,则使用日历特定的 short or abbreviated form。

  • Month: 如果模式字母的数量为 3 或大于 3,则将月份解释为 text;否则解释为 number。

  • General time zone: 如果时区有名称,则将它们解释为 text。对于表示 GMT 偏移值的时区,使用以下语法:

     GMTOffsetTimeZone:
             GMT Sign Hours : Minutes
     Sign: one of
             + -
     Hours:
             Digit
             Digit Digit
     Minutes:
             Digit Digit
     Digit: one of
             0 1 2 3 4 5 6 7 8 9
    

    Hours 必须在 0 到 23 之间,Minutes 必须在 00 到 59 之间。格式是与语言环境无关的,并且数字必须取自 Unicode 标准的 Basic Latin 块。

    对于解析来说,RFC 822 time zones 也是可接受的。

  • RFC 822 time zone: 对于格式化来说,使用 RFC 822 4-digit 时区格式:

     RFC822TimeZone:
             Sign TwoDigitHours Minutes
     TwoDigitHours:
             Digit Digit
    

    TwoDigitHours 必须在 00 和 23 之间。其他定义请参阅 general time zones。

    对于解析来说,general time zones 也是可接受的。

SimpleDateFormat 还支持本地化日期和时间模式 字符串。在这些字符串中,以上所述的模式字母可以用其他与语言环境有关的模式字母来替换。SimpleDateFormat 不处理除模式字母之外的文本本地化;而由类的客户端来处理。

常用的方法示例

  • 日期及时间的加减计算
  • 日期转字符串
  • 字符串转日期
  • 日期相关其他信息(星期,一年的第几天…)
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * Created by caget on 2017/12/5.
 */
public class Datetime {
    public static void main(String[] args) throws ParseException {

        //字符串转Date
        String time="2017/1/1 22:23:11";
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//注意月份和分钟的大小写
        Date date=sdf.parse(time);//根据模式转换为Date类型,会抛出ParseException

        //Date转Calendar类
        Calendar c= Calendar.getInstance(Locale.ENGLISH);//使用静态方法得到默认实例
        c.setTime(date);//转换为Calendar类型进行日期相关操作

        //Calendar转Date
        SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd E HH:mm:ss", Locale.ENGLISH);
        Date date1=c.getTime();//与date相同
        //Date再转字符串
        String str_date1=sdf2.format(date1);
        System.out.println(str_date1+
                "\r\n weekday:"+c.get(Calendar.DAY_OF_WEEK)+ //周日是第一天
                "\r\n days in year: "+c.get(Calendar.DAY_OF_YEAR)+
                "\r\n week in year: "+c.get(Calendar.WEEK_OF_YEAR)+
                "\r\n week in month: "+c.get(Calendar.WEEK_OF_MONTH));

        //日期加减,比较等操作
        c.add(Calendar.DATE,2);
        c.add(Calendar.MINUTE,1);
        System.out.println(sdf2.format(c.getTime())+
                "\r\n weekday:"+c.get(Calendar.DAY_OF_WEEK)+
                "\r\n days in year: "+c.get(Calendar.DAY_OF_YEAR)+
                "\r\n week in year: "+c.get(Calendar.WEEK_OF_YEAR)+
                "\r\n week in month: "+c.get(Calendar.WEEK_OF_MONTH));
    }
}

输出结果

2017-01-01 Sun 22:23:11
 weekday:1
 days in year: 1
 week in year: 1
 week in month: 1
2017-01-03 Tue 22:24:11
 weekday:3
 days in year: 3
 week in year: 1
 week in month: 1

参考

Java API 文档。