Java 语言的Calendar,GregorianCalendar (日历),Date(日期)和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分。
我们将讨论下面的类:
1、 具体类(和抽象类相对)java.util.Date
2、 抽象类java.text.DateFormat 和它的一个具体子类,java.text.SimpleDateFormat
3、 抽象类java.util.Calendar 和它的一个具体子类,java.util.GregorianCalendar(公元日历)
具体类可以被实例化, 但是抽象类却不能。 你首先必须实现抽象类的一个具体子类。
Calendar c = Calendar.getInstance(); 等价于 Calendar gc = new GregorianCalendar();
Date date = new Date();
SimpleDateFormat df = new SimpleDateFormat(格式模式字符串);
1、Date及其格式化
Date 类从Java 开发包(JDK) 1.0 就开始进化,当时它只包含了几个取得或者设置一个日期数据的各个部分的方法, 比如说年、月、日。 这些方法现在遭到了批评并且已经被转移到了Calendar类里去了。这种改进旨在更好的处理日期数据的国际化格式。就在JDK 1.1中一样, Date 类实际上只是一个包裹类,它包含的是一个长整型long数据,表示的是从GMT(格林尼治标准时间)1970年1 月 1日00:00:00这一刻之前或者是之后经历的毫秒数。Long类型表示的最大正值和最大负值可以轻松的表示290,000,000年(足够使用)的时间。
让我们看一个使用系统的当前日期和时间创建一个日期对象并返回一个长整数的简单例子。这个时间通常被称为Java 虚拟机(JVM)主机环境的系统时间。
例如要得到今年的年份:date.getYear()+1900或者Calendar.get(Calendar.YEAR)
<span style="font-size:14px;">import java.util.Date; public class Test_1 { public static void main(String[] args) { //当前系统时间,这个构造函数在内部使用了System.currentTimeMillis()方法来从系统获取日期 Date date = new Date(); //返回时间的long类型(毫秒数) System.out.println("long类型(毫秒数):"+date.getTime()); //Calendar.get(Calendar.YEAR)-1900 System.out.println("年份x-1900:"+date.getYear()); //Calendar.get(Calendarendar.MONTH) <span style="color:#cc33cc;">(0-11) </span> System.out.println("月份(<span style="color:#cc33cc;">0-11</span>):"+date.getMonth()); //Calendar.get(Calendar.DAY_OF_MONTH) System.out.println("几号:"+date.getDate()); //Calendar.get(Calendar.DAY_OF_WEEK) <span style="color:#6600cc;"> (1-日,2-周一,3-周二...7-周六) </span> System.out.println("星期<span style="color:#cc33cc;">(0-日,1-1,2-2...6-6</span>):"+date.getDay()); //Calendar.get(Calendar.HOUR_OF_DAY) System.out.println("小时(0-23):"+date.getHours()); //Calendar.get(Calendar.MINUTE) System.out.println("分钟(0-59):"+date.getMinutes()); //Calendar.get(Calendar.SECOND) System.out.println("秒(0-59):"+date.getSeconds()); } }</span>
同时,也有与get相应的set方法。
- 将日期格式化为字符串:
Date是一个long类型的数据,而我们想要将其格式化为我们能都读取的日期格式,这里就需要用到SimpleDateFormat了。SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
首先,先实例化一个自定义格式的SimpleDateFormat,例如SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd E”);模式字母参考API文档中SimpleDateFormat。接下来将给定的Date
格式化为日期/时间字符串,sdf.format(new Date()); 即得到 2014-05-13 星期二。
<span style="font-size:14px;color:#ff0000;"> SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd E"); System.out.println(sdf.format(new Date()));</span>
- 文本数据解析成日期对象
假设我们有一个已知格式化的日期对象的文本字符串,而我们希望解析这个字符串并从文本日期数据创建一个日期对象。我们将再次以格式化字符串"MM-dd-yyyy" 调用SimpleDateFormat类, 但是这一次, 我们使用格式化解析而不是生成一个文本日期数据。例如将文本字符串"05-13-2014"解析为一个值为1399910400000 的日期对象。
通过parse()方法,DateFormat能够以一个字符串创立一个Date对象。这个方法能抛出ParseException异常,所以你必须使用适当的异常处理技术。
<span style="font-size:18px;"> </span><span style="font-size:14px;">SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy"); String str = "05-13-2014"; Date date = null; try { date = <span style="color:#ff0000;">sdf.parse(str);</span> } catch (ParseException e) { e.printStackTrace(); } System.out.println(date.getTime());</span>
这样就将"05-13-2014"解析为一个值为1399910400000 的日期对象,当然,实际中我们很少需要这样一个长整型的日期对象,这时就我们用到上面的将日期对象转换为我们需要的字符串了。实际上我们是将日期对象date作为一个中转,实例化两个格式化对象作为转换的通道。 String1 ——(sdf1)——> date ——(sdf2)——>String2
另外,各种求日期之间的计算,都可以将字符串类型转换为Date的长整型,然后数学计算,之后再将Data类型转换为String。例如:
<span style="font-size:14px;">import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Test_1 { public static void main(String[] args){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; Date mydate = null; try { date = sdf.parse("2003-05-1"); mydate= sdf.parse("1899-12-30"); } catch (ParseException e) { e.printStackTrace(); } long day =(date.getTime()-mydate.getTime())/(24*60*60*1000); System.out.println(day); } }</span>
- 使用标准的日期格式化过程
我们已经可以生成和解析定制的日期格式了, 让我们来看一看如何使用内建的格式化过程.
1.方法DateFormat df = DateFormat.getDateInstance();获得一个默认的日期(只是日期)格式化风格;
2.方法DateFormat.getTimeInstance()获得一个默认的时间(只是时间)格式化风格;
3.方法 DateFormat.getDateTimeInstance() 获得标准的日期+时间格式化风格。
这三个方法都有想对应的可传参改变日期风格或日期风格的的同类型方法。参数可用DateFormat中的常量,如下:
参数(int):DateFormat.SHORT DateFormat.MEDIUM DateFormat.LONG DateFormat.FULL
注意我们在对 getDateTimeInstance的每次调用中都传递了两个值。第一个参数是日期风格,而第二个参数是时间风格。它们都是基本数据类型int(整型)。考虑到可读性,我们使用了DateFormat 类提供的常量:SHORT, MEDIUM, LONG, 和 FULL。例如DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
2、 Calendar 日历类
首先请记住 Calendar 只是一个抽象类, 也就是说你无法直接获得它的一个实例,换而言之你可以提供一个自己开发的 Calendar 对象。
那究竟什么是一个 Calendar 呢?中文的翻译就是日历,那我们立刻可以想到我们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢?
比如有:月份的定义 - 阳`(公)历 一年12 个月,每个月的天数各不同;阴(农)历,每个月固定28天,每周的第一天 - 阳(公)历星期日是第一天;阴(农)历,星期一是第一天。实际上,在历史上有着许多种纪元的方法。它们的差异实在太大了,比如说一个人的生日是"八月八日" 那么一种可能是阳(公)历的八月八日,但也可以是阴(农)历的日期。所以为了计时的统一,必需指定一个日历的选择。那现在最为普及和通用的日历就是 "Gregorian Calendar"。也就是我们在讲述年份时常用 "公元几几年"。Calendar 抽象类定义了足够的方法,让我们能够表述日历的规则。Java 本身提供了对 "Gregorian Calendar" 规则的实现。我们从 Calendar.getInstance() 中所获得的实例就是一个 "GreogrianCalendar" 对象(与您通过 new GregorianCalendar() 获得的结果一致)。
Calendar c = Calendar.getInstance(); 等价于 Calendar gc = new GregorianCalendar();获得标准日历系统(公元)
获取当前的Date, Date date = c.getTime()就等价与Date date = new Date(),当然有getTime()就有setTime(Date),使用给定的Date
设置此 Calendar 的时间。
设置日历字段:
set(int field, int value) 将给定的日历字段设置为给定值。field是指要设置的域,例如,要设置月份,则可以使用方法set(Calendar.MONTH, 4),将月份设置为5月(另外,月份的起始值为0而不是1,所以要设置五月时,我们用4而不是5)。为了增加可读性和防止出错,我们一般这样使用:set(Calendar.MONTH, Calendar.MARCH)。
set(int field, int value) 还有3个重写的方法,分别设置不同字段范围的值,例如set(2014,4, 15, 14, 25, 33);设置年月日时分秒,2014年5月15日14点25分33秒。而要设置毫秒值必须用第一个方法。
获得日历字段:
使用get(int field)方法可以获得想要的字段。例如:get(Calendar.YEAR);获得当前日历中的年份,当然还有很多值,MONTH(当前月份-1),DATE = DAY_OF_MONTH(几号),DAY_OF_YEAR(一年中的第几天),DAY_OR_WEEK(周日=1,周一=2,周二=3...周六=7)
值得注意的是
1. 在获取月份时,Calendar.MONTH + 1 的原因
Java中的月份遵循了罗马历中的规则:当时一年中的月份数量是不固定的,第一个月是JANUARY。而Java中Calendar.MONTH返回的数值其实是当前月距离第一个月有多少个月份的数值,JANUARY在Java中返回“0”,所以我们需要+1。
2. 在获取星期几 Calendar.DAY_OF_WEEK – 1 的原因
Java中Calendar.DAY_OF_WEEK其实表示:一周中的第几天,所以他会受到 第一天是星期几 的影响。
有些地区以星期日作为一周的第一天,而有些地区以星期一作为一周的第一天,这2种情况是需要区分的。
看下表的返回值
星期日为一周的第一天 | SUN | MON | TUE | WED | THU | FRI | SAT |
DAY_OF_WEEK返回值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
星期一为一周的第一天 | MON | TUE | WED | THU | FRI | SAT | SUN |
DAY_OF_WEEK返回值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
所以Calendar.Day_OF_WEEK需要根据本地设置的不同而确定是否需要“-1”,默认为第一种,即周日为第一天,java中设置不同地区的输出可以使用Local.setDefalut(Locale.地区名)来实现。
3.获取日期时Calendar.DAY_OF_MONTH不需要特殊的操作,它直接返回一个月中的第几天。即几号就是几。
错误日期:
add(field, value),add可以对Calendar的字段进行计算。如果需要减去值,那么value使用负值就可以了。
add()可以对Calendar的字段进行计算。如果需要减去值,那么使用负数值就可以了,如 add(field, -value)。add(f, delta) 将delta
添加到f
字段中。这等同于调用set(f, get(f) + delta)
,但要带以下两个调整:
i. 当被修改的字段超出它可以的范围时,那么比它大的字段会自动修正,溢出的字段调整回其范围内。如:
现在日期为2013年5月30日,调用add(Calendar.DAY_OF_MONTH,2) ,即2013年5月32日。而5月最多为31日,这是个错误的日期,所以,比错误字段(日)大的字段(月),就会自动修正,即得到整齐的日期2013年6月1日。所以,日的加减可以不用考虑是否合法,Calendar会自动变成合法的。但是月份就不同了。使用set()设置的时候同样。
ii.另一个规则是,如果期望某一个更小的字段是不变的(由 Calendar 的实现类决定,如下,修改月份时,没有修改日),那么它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。例如:
因为日是比月更小的单元,所以9-31会变为9-30而不是10-1。
<span style="font-size:14px;">Calendar cal1 = Calendar.getInstance(); cal1.set(2000, 7, 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</span>
roll(f, delta)
将 delta
添加到 f
字段中,但不更改更大的字段。这等同于调用add(f, delta)
,但要带以下调整:
roll规则:在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH
是一个比 HOUR
大的字段。
以下是一个很实用的工具类,实现的日期与字符串还有日历间的各种转换
import java.util.Calendar; import java.util.Date; import java.text.*; public class Date_test { /** * 将Date类型日期转化成String类型"任意"格式 * java.sql.Date,java.sql.Timestamp类型是java.util.Date类型的子类 * * @param date * Date * @param format * String "2003-01-01"格式 "yyyy年M月d日" "yyyy-MM-dd HH:mm:ss"格式 * @return String */ public static String dateToString(java.util.Date date, String format) { if (date == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); String str = sdf.format(date); return str; } /** * 将String类型日期转化成java.utl.Date类型"2003-01-01"格式 * * @param str * String 要格式化的字符串 * @param format * String * @return Date */ public static java.util.Date stringToUtilDate(String str, String format) { if (str == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); java.util.Date date = null; try { date = sdf.parse(str); } catch (Exception e) { e.printStackTrace(); } return date; } /** * 将String类型日期转化成java.sql.Date类型"2003-01-01"格式 * * @param str * String * @param format * String * @return Date */ public static java.sql.Date stringToSqlDate(String str, String format) { if (str == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); java.util.Date date = null; try { date = sdf.parse(str); } catch (Exception e) { return null; } return new java.sql.Date(date.getTime()); //默认调用java.sql.Date的toString()方法,返回yyyy-MM-dd格式 } /** * 将String类型日期转化成java.sql.Date类型"2003-01-01"格式 * * @param str * String * @param format * String * @return Timestamp */ public static java.sql.Timestamp stringToTimestamp(String str, String format) { if (str == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); java.util.Date date = null; try { date = sdf.parse(str); } catch (Exception e) { return null; } return new java.sql.Timestamp(date.getTime()); } /** * 将java.util.Date日期转化成java.sql.Date类型 * * @param Date * @return 格式化后的java.sql.Date */ public static java.sql.Date toSqlDate(Date date) { if (date == null) { return null; } return new java.sql.Date(date.getTime()); } /** * 将字符串转化为时间格式 string to string * * @param str * String * @param format * String * @return String */ public static String toDateString(String str, String oldformat, String newformat) { return dateToString(stringToUtilDate(str, oldformat), newformat); } /** * 将日历转化为日期 * * @param calendar * Calendar * @return Date */ public static java.util.Date converToDate(java.util.Calendar calendar) { return Calendar.getInstance().getTime(); } /** * 将日期转化为日历 * * @param date * Date * @return Calendar */ public static java.util.Calendar converToCalendar(java.util.Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar; } /** * 求得从某天开始,过了几年几月几日几时几分几秒后,日期是多少 几年几月几日几时几分几秒可以为负数 * * @param date * Date * @param year * int * @param month * int * @param day * int * @param hour * int * @param min * int * @param sec * int * @return Date */ public static java.util.Date modifyDate(java.util.Date date, int year, int month, int day, int hour, int min, int sec) { Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(Calendar.YEAR, year); cal.add(Calendar.MONTH, month); cal.add(Calendar.DATE, day); cal.add(Calendar.HOUR, hour); cal.add(Calendar.MINUTE, min); cal.add(Calendar.SECOND, sec); return cal.getTime(); } /** * 取得当前日期时间 1:year 2:month 3:day */ public static int getCurTime(int i) { if (i == 1) { return java.util.Calendar.getInstance().get(Calendar.YEAR); } else if (i == 2) { return java.util.Calendar.getInstance().get(Calendar.MONTH) + 1; } else if (i == 3) { return java.util.Calendar.getInstance().get(Calendar.DATE); } return 0; } public static void main(String[] args) { System.out.println(dateToString( modifyDate(Calendar.getInstance().getTime(), -1, -1, -1, -1, -1, -1), "yyyy-MM-dd HH:mm:ss")); } }