Java中时间日期的操作

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’ 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (‘) 引起来,以免进行解释。””” 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在解析时与输入字符串进行匹配。
模式字母对应表如下:

母通常是重复的,其数量确定其精确表示:

  • 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 文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值