JAVA Calendar方法详解

 究竟什么是一个 Calendar 呢?中文的翻译就是日历,那咱们马上能够想到咱们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢?java

    好比有:数据库

     月份的定义 - 阳`(公)历 一年12 个月,每月的天数各不一样;阴(农)历,每月固定28天编程

    每周的第一天 - 阳(公)历星期日是第一天;阴(农)历,星期一是第一天函数

    实际上,在历史上有着许多种纪元的方法。它们的差别实在太大了,好比说一我的的生日是\"八月八日\" 那么一种多是阳(公)历的八月八日,但也能够是阴(农)历的日期。因此为了计时的统一,必需指定一个日历的选择。那如今最为普及和通用的日历就是 \"Gregorian Calendar\"。也就是咱们在讲述年份时经常使用 \"公元几几年\"。Calendar 抽象类定义了足够的方法,让咱们可以表述日历的规则。Java 自己提供了对 \"Gregorian Calendar\" 规则的实现。咱们从 Calendar.getInstance() 中所得到的实例就是一个 \"GreogrianCalendar\" 对象(与您经过 new GregorianCalendar() 得到的结果一致)。性能

    下面的代码能够证实这一点:学习

    import java.io.*;     import java.util.*;翻译

    public class WhatIsCalendar    orm

{对象

    public static void main(String[] args) {内存

    Calendar calendar = Calendar.getInstance();

    if (calendar instanceof GregorianCalendar)

    System.out.println(\"It is an instance of GregorianCalendar\"t;

    }  

   }

    Calendar 在 Java 中是一个抽象类(Abstract Class),GregorianCalendar 是它的一个具体实现。

    咱们也能够本身的 Calendar 实现类,而后将它做为 Calendar 对象返回(面向对象的特性)。在 IBM alphaWorks 上,IBM 的开发人员实现了多种日历(http://www.alphaworks.ibm.com/tech/calendars)。一样在 Internet 上,也有对中国农历的实现。本文对如何扩展 Calendar 不做讨论,你们能够经过察看上述 Calendar 的源码来学习。

    Calendar 与 Date 的转换很是简单:

    Calendar calendar = Calendar.getInstance();     // 从一个 Calendar 对象中获取 Date 对象

    Date date = calendar.getTime();     // 将 Date 对象反应到一个 Calendar 对象中,

    // Calendar/GregorianCalendar 没有构造函数能够接受 Date 对象     // 因此咱们必需先得到一个实例,而后设置 Date 对象

    calendar.setTime(date);

    Calendar 对象在使用时,有一些值得注意的事项:

    1. Calendar 的 set() 方法

    set(int field, int value) - 是用来设置\"年/月/日/小时/分钟/秒/微秒\"等值

    field 的定义在 Calendar 中

    set(int year, int month, int day, int hour, int minute, int second) 但没有

    set(int year, int month, int day, int hour, int minute, int second, int millisecond) 前面 set(int,int,int,int,int,int) 方法不会自动将 MilliSecond 清为 0。

    另外,月份的起始值为0而不是1,因此要设置八月时,咱们用7而不是8。

    calendar.set(Calendar.MONTH, 7);

    咱们一般须要在程序逻辑中将它清为 0,不然可能会出现下面的状况:

    import java.io.*;     import java.util.*;

    public class WhatIsCalendarWrite    

{

    public static void main(String[] args) throws Exception{[Page]    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(\"calendar.out\"t);

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0, 0);

    out.writeObject(cal1);

    Calendar cal2 = Calendar.getInstance();

    cal2.set(2000, 7, 1, 0, 0, 0);

    cal2.set(Calendar.MILLISECOND, 0);

    out.writeObject(cal2);     out.close();

    }

    }

    咱们将 Calendar 保存到文件中

    import java.io.*;

    import java.util.*;

    public class WhatIsCalendarRead

    {

    public static void main(String[] args) throws Exception{

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"calendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0, 0);  

   if (cal1.equals(cal2))     System.out.println(\"Equals\"t;

    else     System.out.println(\"NotEqual\"t;

    System.out.println(\"Old calendar \"+cal2.getTime().getTime());

    System.out.println(\"New calendar \"+cal1.getTime().getTime());

    cal1.set(Calendar.MILLISECOND, 0);

    cal2 = (Calendar)in.readObject();

    if (cal1.equals(cal2))     System.out.println(\"Equals\"t;

    else     System.out.println(\"NotEqual\"t;     System.out.println(\"Processed Old calendar \"+cal2.getTime().getTime());

    System.out.println(\"Processed New calendar \"+cal1.getTime().getTime());     }     }

    而后再另一个程序中取回来(模拟对数据库的存储),可是执行的结果是:

    NotEqual     Old calendar 965113200422 <------------ 最后三位的MilliSecond与当前时间有关     New calendar 965113200059 <-----------/     Equals     Processed Old calendar 965113200000     Processed New calendar 965113200000

    另外咱们要注意的一点是,Calendar 为了性能缘由对 set() 方法采起延缓计算的方法。在 JavaDoc 中有下面的例子来讲明这个问题:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 31, 0, 0 , 0); //2000-8-31

    cal1.set(Calendar.MONTH, Calendar.SEPTEMBER); //应该是 2000-9-31,也就是 2000-10-1

    cal1.set(Calendar.DAY_OF_MONTH, 30); //若是 Calendar 转化到 2000-10-1,那么如今的结果就该是 2000-10-30

    System.out.println(cal1.getTime()); //输出的是2000-9-30,说明 Calendar 不是立刻就刷新其内部的记录

 2. Calendar 对象的容错性,Lenient 设置     咱们知道特定的月份有不一样的日期,当一个用户给出错误的日期时,Calendar 如何处理的呢?

 

    import java.io.*;

    import java.util.*;

    public class WhatIsCalendar

    {

    public static void main(String[] args) throws Exception{

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 1, 32, 0, 0, 0);

    System.out.println(cal1.getTime());

    cal1.setLenient(false);

[Page]     cal1.set(2000, 1, 32, 0, 0, 0);  

   System.out.println(cal1.getTime());

    }

    }

    它的执行结果是:

    Tue Feb 01 00:00:00 PST 2000

    Exception in thread \"main\" java.lang.IllegalArgumentException

    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:1368)

    at java.util.Calendar.updateTime(Calendar.java:1508)

    at java.util.Calendar.getTimeInMillis(Calendar.java:890)

    at java.util.Calendar.getTime(Calendar.java:871)

    at WhatIsCalendar.main(WhatIsCalendar.java:12)  

   当咱们设置该 Calendar 为 Lenient false 时,它会依据特定的月份检查出错误的赋值。

    3. 不稳定的 Calendar

    咱们知道 Calendar 是能够被 serialize 的,可是咱们要注意下面的问题

    import java.io.*;

    import java.util.*;

    public class UnstableCalendar implements Serializable     {

    public static void main(String[] args) throws Exception{  

   Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0 , 0);  

   cal1.set(Calendar.MILLISECOND, 0);

    ObjectOutputStream out =     new ObjectOutputStream(     new FileOutputStream(\"newCalendar.out\"t);

    out.writeObject(cal1);

    out.close();

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"newCalendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    cal2.set(Calendar.MILLISECOND, 0);

    System.out.println(cal2.getTime());

    }

    }

    运行的结果居然是: Thu Jan 01 00:00:00 PST 1970

    它被复原到 EPOC 的起始点,咱们称该 Calendar 是处于不稳定状态。这个问题的根本缘由是 Java 在 serialize GregorianCalendar 时没有保存全部的信息,因此当它被恢复到内存中,又缺乏足够的信息时,Calendar 会被恢复到 EPOCH 的起始值。Calendar 对象由两部分构成:字段和相对于 EPOC 的微秒时间差。字段信息是由微秒时间差计算出的,而 set() 方法不会强制 Calendar 从新计算字段。这样字段值就不对了。

    下面的代码能够解决这个问题:

    import java.io.*;

    import java.util.*;

    public class StableCalendar implements Serializable     {

    public static void main(String[] args) throws Exception{

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 7, 1, 0, 0 , 0);

    cal1.set(Calendar.MILLISECOND, 0);

    ObjectOutputStream out =     new ObjectOutputStream(     new FileOutputStream(\"newCalendar.out\"t);

    out.writeObject(cal1);

    out.close();

    ObjectInputStream in =     new ObjectInputStream(     new FileInputStream(\"newCalendar.out\"t);

    Calendar cal2 = (Calendar)in.readObject();

    cal2.get(Calendar.MILLISECOND); //先调用 get(),强制 Calendar 刷新

    cal2.set(Calendar.MILLISECOND, 0);//再设值     System.out.println(cal2.getTime());     }     }

    运行的结果是: Tue Aug 01 00:00:00 PDT 2000

    这个问题主要会影响到在 EJB 编程中,参数对象中包含 Calendar 时。通过 Serialize/Deserialize 后,直接操做 Calendar 会产生不稳定的状况。

4. add() 与 roll() 的区别

    add() 的功能很是强大,add 能够对 Calendar 的字段进行计算。若是须要减去值,那么使用负数值就能够了,如 add(field, -value)。

    add() 有两条规则:

    当被修改的字段超出它能够的范围时,那么比它大的字段会自动修正。如:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(2000, 8, 31, 0, 0 , 0); //2000-8-31

    cal1.add(Calendar.MONTH, 1); //2000-9-31 => 2000-10-1,对吗?

    System.out.println(cal1.getTime()); //结果是 2000-9-30

    另外一个规则是,若是比它小的字段是不可变的(由 Calendar 的实现类决定),那么该小字段会修正到变化最小的值。

    以上面的例子,9-31 就会变成 9-30,由于变化最小。

    Roll() 的规则只有一条:

    当被修改的字段超出它能够的范围时,那么比它大的字段不会被修正。如:

    Calendar cal1 = Calendar.getInstance();

    cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 周日

    cal1.roll(Calendar.WEEK_OF_MONTH, -1); //1999-6-1, 周二

    cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 周日

    cal1.add(Calendar.WEEK_OF_MONTH, -1); //1999-5-30, 周日

    WEEK_OF_MONTH 比 MONTH 字段小,因此 roll 不能修正 MONTH 字段。

    Date类介绍

    Data和Calendar类:     1、建立一个日期对象r

    让咱们看一个使用系统的当前日期和时间建立一个日期对象并返回一个长整数的简单例子. 这个时间一般被称为Java 虚拟机(JVM)主机环境的系统时间.

    import java.util.Date;

    public class DateExample1

{

     public static void main(String[] args)

{     // Get the system date/time

    Date date = new Date();

    System.out.println(date.getTime());

    }

    }

    在星期六, 2001年9月29日, 下午大约是6:50的样子, 上面的例子在系统输出设备上     显示的结果是 1001803809710. 在这个例子中,值得注意的是咱们使用了Date 构造

    函数建立一个日期对象, 这个构造函数没有接受任何参数. 而这个构造函数在内部     使用了System.currentTimeMillis() 方法来从系统获取日期.若是用

    System.out.println(new Date());

    则输出形式为:Tue Nov 08 14:28:07 CST 2005

    那么, 如今咱们已经知道了如何获取从1970年1月1日开始经历的毫秒数了. 咱们如     何才能以一种用户明白的格式来显示这个日期呢? 在这里类java.text.     SimpleDateFormat 和它的抽象基类 java.text.DateFormat 就派得上用场了.

    2、日期数据的定制格式

    假如咱们但愿定制日期数据的格式, 比方星期六-9月-29日-2001年. 下面的例子展     示了如何完成这个工做:

    import java.text.SimpleDateFormat;     import java.util.Date;

    public class DateExample2 {

    public static void main(String[] args) {

    SimpleDateFormat bartDateFormat =     new SimpleDateFormat(\"EEEE-MMMM-dd-yyyy\");

    Date date = new Date();

    System.out.println(bartDateFormat.format(date));     }     }

    只要经过向SimpleDateFormat 的构造函数传递格式字符串\"EEE-MMMM-dd-yyyy\",     咱们就可以指明本身想要的格式. 你应该能够看见, 格式字符串中的ASCII 字符     告诉格式化函数下面显示日期数据的哪个部分. EEEE是星期, MMMM是月, dd是日[Page]     , yyyy是年. 字符的个数决定了日期是如何格式化的.传递\"EE-MM-dd-yy\"会显示     Sat-09-29-01. 请察看Sun 公司的Web 站点获取日期格式化选项的完整的指示.

    3、将文本数据解析成日期对象r

    假设咱们有一个文本字符串包含了一个格式化了的日期对象, 而咱们但愿解析这个     字符串并从文本日期数据建立一个日期对象. 咱们将再次以格式化字符串     \"MM-dd-yyyy\" 调用SimpleDateFormat类, 可是这一次, 咱们使用格式化解析而不     是生成一个文本日期数据. 咱们的例子, 显示在下面, 将解析文本字符串     \"9-29-2001\"并建立一个值为001736000000 的日期对象.

    例子程序:

    import java.text.SimpleDateFormat;     import java.util.Date;

    public class DateExample3 {

    public static void main(String[] args) { 

    // Create a date formatter that can parse dates of

    // the form MM-dd-yyyy.  

   SimpleDateFormat bartDateFormat =     new SimpleDateFormat(\"MM-dd-yyyy\");

    // Create a string containing a text date to be parsed.

    String dateStringToParse = \"9-29-2001\";

    try {    

// Parse the text version of the date.    

// We have to perform the parse method in a   

  // try-catch construct in case dateStringToParse    

// does not contain a date in the format we are expecting.    

Date date = bartDateFormat.parse(dateStringToParse);

    // Now send the parsed date as a long value    

// to the system output.    

System.out.println(date.getTime());

    }     catch (Exception ex) {     System.out.println(ex.getMessage());     }     }     }

5、使用标准的日期格式化过程

    既然咱们已经能够生成和解析定制的日期格式了, 让咱们来看一看如何使用内建的     格式化过程. 方法 DateFormat.getDateTimeInstance() 让咱们得以用几种不一样的     方法得到标准的日期格式化过程. 在下面的例子中, 咱们获取了四个内建的日期格     式化过程. 它们包括一个短的, 中等的, 长的, 和完整的日期格式.

    import java.text.DateFormat;     import java.util.Date;

    public class DateExample4 {

    public static void main(String[] args) {

    Date date = new Date();

    DateFormat shortDateFormat =DateFormat.getDateTimeInstance(DateFormat.SHORT,  DateFormat.SHORT);

    DateFormat mediumDateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);

    DateFormat longDateFormat =  DateFormat.getDateTimeInstance( DateFormat.LONG,  DateFormat.LONG);

    DateFormat fullDateFormat =     DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);

    System.out.println(shortDateFormat.format(date));

    System.out.println(mediumDateFormat.format(date));

    System.out.println(longDateFormat.format(date));

    System.out.println(fullDateFormat.format(date));

    }  

   }

    注意咱们在对 getDateTimeInstance的每次调用中都传递了两个值. 第一个参数     是日期风格, 而第二个参数是时间风格. 它们都是基本数据类型int(整型). 考虑     到可读性, 咱们使用了DateFormat 类提供的常量: SHORT, MEDIUM, LONG, 和 [Page]     FULL. 要知道获取时间和日期格式化过程的更多的方法和选项, 请看Sun 公司Web     站点上的解释.

    运行咱们的例子程序的时候, 它将向标准输出设备输出下面的内容:     9/29/01 8:44 PM     Sep 29, 2001 8:44:45 PM     September 29, 2001 8:44:45 PM EDT     Saturday, September 29, 2001 8:44:45 PM EDT

相关文章
相关标签/搜索