概述
Java原本提供了Data和Calendar用于处理日期、时间的类,包括创建日期、时间对象,获取系统当前日期、时间等操作。但Date不仅无法实现国际化,而且它对不同属性也使用了前后矛盾的偏移量,比如月份与小时都是从0开始的,月份中的天则是从1开始的,年又是从1900开始的,而java.util.Calendar则显得过于复杂。从下面介绍可以看到Java对日期、时间处理不足。Java洗浴了Joda-Time库(一个被广泛使用的日期、时间库)的经验,提供了一套全新的日期时间库。
一、Date类
Java提供了Data类来处理日期、时间(此处的Date指的是java.util包下的Date类,而不会死java.sql包下的Date类),Date对象既包含日期,也包含时间。Date类送JDK1.0开始就存在了,但因为它历史悠久,所以它的大部分构造器、方法都已经过时了,不再推荐使用了。
Date类提供了6个构造器,其中4个已经Deprecated(Java不再推荐使用,使用不再推荐的构造器编译器会提出警告信息,并导致程序性能、安全性等方面的问题),剩下的两个构造器如下
①Date():生成一个代表当前日期时间的Date对象。该构造器在底层调用了System.currentTimeMillis()获得long整数作为日期参数。
②Date(long date):根据指定的long型整数来生成一个Date对象。该构造器的参数表示创建的Date对象和GMT 1970年1月1日00:00:00之间的时间差,以毫秒作为计时单位。
Date对象的大部分方法也已经Deprecated了,剩下为数不多的几个方法。
①boolean after(Date when):测试该日期是否在指定日期when之后。
②boolean before(Date when):测试该日期是否在指定日期when之前。
③long getTime():返回该时间对于的long型整数,即从GMT 1970-01-01 00:00:00之间的时间差,以毫秒作为单位。
④void setTime(long time):设置该Date对象的事件。
下面下程序示范了Date类的用法。
public class DateTest
{
public static void main(String[] args)
{
Date d1 = new Date();
//获取当前时间后100ms的事件
Date d2 = new Date(System.currentTimeMillis() + 100);
System.out.println(d2);
System.out.println(d1.compareTo(d2));
System.out.println(d1.before(d2));
}
}
总体来说,Date是一个设计相当糟糕的类,因此Java官方推荐尽量少用Date的构造器和方法。
二、Calendar类
因为Date类在设计上存在一些缺陷,所以Java提供了Calendar类来更好地处理日期和时间。Calendar是一个抽象类,它用于表示日历。
历史上有着许多种纪年方法,它的差异实在太大了。为了统一计时,全世界通常选择最普及、最通用的日历:Gregorian Calendar,也就是日常介绍年份时常用的“公元几几年”。
Calendar类本身是一个抽象类,它是所有日历类的模板,并提供了一些所有日历通用的方法;但它本身不能直接实例化,程序只能创建Calendar子类的实例,Java本身提供了一个GregorianCalendar类,一个代表格里高利日的子类。
Calendar类是一个抽象类,所以不能使用构造器来创建Calendar对象。但它提供了几个静态getInstance()方法来获取Calendar对象,这些方法根据TimeZone,Locale类来获取特定的Calendar,如果不指定Time、Locale,则使用了默认的TimeZone、Locale来创建Calendar。
Calendar与Date都是表示日期的工具类,它们直接可以自由转换。
//创建一个默认的Calendar对象
Calendar calendar = Calendar.getInstance();
//从Calendar对象中取出Date对象
Date date = calendar.getTime();
//通过Date对象获得对于的Calendar对象
//因为Calendar/GregorianCalendar没有构造函数可以接收Date对象
//所以必须先获得一个Calendar实例,然后调用其setTime方法
Calendar calendar2 = Calendar.getInstance();
calendar2.setTime(date);
Calendar类提供了大量访问、修改日期时间的方法。很多方法需要一个int类型的field,field是Calendar类的类变量,如Calendar.YEAR、Calendar.MONTH等分别代表了年、月、日、小时、分钟、秒等时间字段。
需要注意的是,Calendar.MONTH字段代表月份,月份的起始值不是1,而是0,所以要设置8月时,用7而不是8。如下程序示范了Calendar类的常规用法。
public class CalendarTest
{
public static void main(String[] args)
{
Calendar c = Calendar.getInstance();
//取出年
System.out.println(c.get(Calendar.YEAR));
//取出月份
System.out.println(c.get(Calendar.MONTH));
//取出日
System.out.println(c.get(Calendar.DATE));
//分别设置年、月、日、小时、分钟、秒
c.set(2003,10,23,12,32,23);//2003-11-23 12:32:23
System.out.println(c.getTime());
//将Calendar往前推1年
c.add(Calendar.YEAR, -1);//2002-11-23 12:32:23
//Calendar往前推8个月
c.roll(Calendar.MONTH, -8);//2002-03-23 12:32:23
System.out.println(c.getTime());
}
}
Calendar还有如下几个注意点。
1.add和roll的区别
add(int filed,int amount)的功能非常强大,add主要用于改变Calendar特定字符的值。如果需要增加某字段的值,则让amount为正数;如果需要减少某字段的值,则让amount为负数即可。
add(int field,int amount)有如下两条规则、
①当被修改的字段超出它所允许的范围时,会发生进位,即上一级字段也会增大。例如:
Calendar cal1 = Calendar.getInstance();
cal1.set(2003,7,23,0,0,0);//2003-8-23
cal1.add(Calendar.MONTH,6);//2004-2-23
②如果下一字段也需要改变,则该字段会修正到变化最小的值。
Calendar cal2 = Calendar.getInstance();
cal2.set(2003,7,31,0,0,0);//2003-8-31
//因为进位后月份改为2月,2月没有31日,自动变成29日
cal2.add(Calendar.MONTH,6);//2004-2-29
roll()的规则与add()处理规则不同:
①当被修改的字段超出它允许的范围时,上一级字段不会增大。
Calendar cal3 = Calendar.getInstance();
cal3.set(2003,7,23,0,0,0);//2003-8-23
//MONTH字段"进位",但YEAR字段并不会增加
cal3.roll(Calendar.MONTH,6);//2003-8-23→2003-2-23
②下一级字段的处理规则,与add()相似
Calendar cal4 = Calendar.getInstance();
cal4.set(2003,7,31,0,0,0);//2003-8-31
//MONTH字段"进位"后变成2,2没有31日
//YEAR字段不会改变,2003年2月只有28天
cal4.roll(Calendar.MONTH,6);//2003-8-31→2003-2-28
2.设置Calendar的容错性
调用Calendar对象的set()方法来改变指定时间字段的值时,有可能传入一个不合法的参数,例如为MONTH字段设置13。这将会导致怎样的后果呢?
public class LenientTest
{
public static void main(String[] args)
{
Calendar cal = Calendar.getInstance();
//结果是YEAR字段加1,MONTH字段为1(2月)
cal.set(Calendar.MONTH, 13);//①
System.out.println(cal.getTime());
//关闭容错性
cal.setLenient(false);
//导致运行时异常
cal.set(Calendar.MONTH, 13);//①
System.out.println(cal.getTime());
}
}
上面程序①②两处代码完全相似,但它们运行的结果不一样;①处代码可以正常运行,因为MONTH字段为13,将会导致YEAR字段加1;②处代码将会导致运行时异常,因为设置MONTH字段超出了MONTH字段允许的范围。
Calendar提供了一个setLenient()用于设置它的容错性,Calendar默认支持较好的容错性,通过setLenient(false)可以关闭Calendar的容错性,让它进行严格的参数检查。
Calendar有两种解释日历字段的模式:
lenient模式和non-lenient模式。
lenient模式:当Calendar处于lenient模式时,每个时间字段可接受超出它允许范围的值;
non-lenient模式:当Calendar处于non-lenient模式时,如果为某个时间字段设置的值超过了它允许的取值范围,程序将会抛出异常。
3.set()方法延迟修改
set(f,value)方法将日历字段f更改为value,此外它还设置了一个内部成员变量,以指示日历字段已经被更改。
尽管日历字段f是立即更改的,但该Calendar所代表的时间却不会立即修改,知道下次调用get()、getTime()、getTimeInMillis()、add()或roll()时才会重新计算日历的时间。这杯称为set()方法的延迟修改,采用延迟修改的优势就是多次调用set()不会触发多次不必要的计算(需要计算出一个代表实际时间的long型整数。
下面程序延时了set()方法延迟修改的效果:
public class LazyTest
{
public static void main(String[] args)
{
Calendar cal = Calendar.getInstance();
cal.set(2003, 7 , 31);
//将月份设为9,系统会把cal自动调整到10月1日
//如果立即修改,系统将会把cal改为10月1日
cal.set(Calendar.MONTH, 8);
//下面代码输出10月1日
//System.out.println(cal.getTime());//①
//设置DATE字段为5
cal.set(Calendar.DATE, 5);//②
System.out.println(cal.getTime());//③
}
}
上面代码中创建了代表2003-8-31的Calendar对象,当把这个对象的MONTH字段加1后,应该得到10-1(因为9月没有31日),如果程序在①号代码处输出当前Calendar的日期,也会看到输出2003-10-1。③号代码则会输出2003-10-5。
如果程序员将①处代码注释起来,因为Calendar的set()方法具有延迟修改的特性,即调用set()方法后Calendar实际上并未计算真实的日期,它只是使用内部成员变量记录MONTH字段被修改为8,接着程序设置DATE字段为5,程序内部再次记录DATE字段为5——也就是9月5日。因此看到③处输出2003-9-5。
三、Java 8新增的日期、时间包
Java 8专门新增了一个java.time包,该包下包含了如下常用的类。
Clock:该类用于获取指定时区的当前日期、时间。该类可取代System类的currentTimeMillis()方法,而且提供了更多方法来获取当前日期、时间,
Duration:该类代表持续时间。该类可以很方便地获取一段时间。
Instant:代表一个具体的时间,可以精确到纳秒。该类提供了静态的now()方法来获取当前日期,也提供了静态的now(Clock clock)方法了获取clock对应的日期。除此之外,它还提供了minusXxx()方法在当前年份上加上几年、几月、几周或几日等,也提供了plusXxx()方法在当前年份上加上几年、几月几周或几日。
LocalDate:该类代表不带时区的日期,例如2007-12-03。
LocalTime:该类代表不带时区的时间,例如10:15:30。
LocalDateTime:该类代表不带时区的日期、时间。例如2007-12-03T10:15:30。
ZonedDateTime:该类代表一个时区化的日期、时间。
ZoneID:该类代表一个时区。
四、使用DateFormat格式化日期、时间
与NumberFormat相似的是,DateFormat也是一个抽象类,它也提供了如下几个类方法用于获取DateFormat对象。
①getDateInstance():返回一个日期格式器,它格式化的字符串只有日期,没有时间,该方法可以传入多个参数,用于指定日期样式和Locale等参数;如果不指定这些参数,则使用默认参数。
②getTimeInstance():返回一个时间格式器,它格式化的字符只有时间,没有日期。该方法可以传入多个参数,用于指定时间样式和Locale等参数;如果不指定这些参数,则所有默认参数。
③getDateFormat():返回一个日期、时间格式器,它格式化后的字符既有日期,也有时间。该方法可以传入多个参数,用于指定日期样式、时间样式和Locale参数;如果不指定这些参数,则使用默认参数。
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
public class DateFormatTest {
public static void main(String[] args) {
//需要被格式化的时间
Date dt = new Date();
//创建两个Locale,分别代表中国、美国
Locale[] locales = {Locale.CHINA,Locale.US};
DateFormat[] df = new DateFormat[16];
//为上面两个Locale对象创建16个DateFormat对象
for(int i = 0;i < locales.length;i++)
{
df[i * 8] = DateFormat.getDateInstance(DateFormat.SHORT,locales[i]);
df[i * 8 + 1] = DateFormat.getDateInstance(DateFormat.MEDIUM,locales[i]);
df[i * 8 + 2] = DateFormat.getDateInstance(DateFormat.LONG,locales[i]);
df[i * 8 + 3] = DateFormat.getDateInstance(DateFormat.FULL,locales[i]);
df[i * 8 + 4] = DateFormat.getTimeInstance(DateFormat.SHORT,locales[i]);
df[i * 8 + 5] = DateFormat.getTimeInstance(DateFormat.MEDIUM,locales[i]);
df[i * 8 + 6] = DateFormat.getTimeInstance(DateFormat.LONG,locales[i]);
df[i * 8 + 7] = DateFormat.getTimeInstance(DateFormat.FULL,locales[i]);
}
for(int i = 0;i < locales.length;i++)
{
String tip = i == 0 ? "------中国日期格式-------" : "------美国日期格式-------";
System.out.println(tip);
System.out.println("SHORT格式的日期格式:"+ df[i * 8].format(dt));
System.out.println("MEDIUM格式的日期格式:"+ df[i * 8 + 1].format(dt));
System.out.println("LONG格式的日期格式:"+ df[i * 8 + 2].format(dt));
System.out.println("FULL格式的日期格式:"+ df[i * 8 + 3].format(dt));
System.out.println("SHORT格式的时间格式:"+ df[i * 8 + 4].format(dt));
System.out.println("MEDIUM格式的时间格式:"+ df[i * 8 + 5].format(dt));
System.out.println("LONG格式的时间格式:"+ df[i * 8 + 6].format(dt));
System.out.println("FULL格式的时间格式:"+ df[i * 8 + 7].format(dt));
}
}
}
上面程序共创建了16个DateFormat对象,分别为中国和美国两个Locale各创建了8个DateFormat对象,分别是Short、MEDIUM、Long、Full四种样式的日期格式器、时间格式器。
获得了DateFormat之后,还可以调用它的setLenient(boolean lenient)方法来设置该格式器是否采用严格语法。举例来说,如果采用不严格的日期语法(该方法的参数为true),则对于“2004-2-31”会转换成2004年3月2日;如果采用严格的日期语法,解析该字符串时将抛出异常。
DateFormat的parse()方法可以把一个字符串解析成Date对象,但它要求被解析的字符串必须符合日期字符串的要求,否则可能抛出ParseException异常。
例如:
String str1 = "2014-12-12";
String str2 = "2014年12月10日";
//下面输出Fri Dec 12 00:00:00 CST 2014
System.out.println(DateFormat.getDateInstance().
parse(str1));
//下面输出Wed Dec 10 00:00:00 CST 2014
System.out.println(DateFormat.getDateInstance(LONG).
parse(str2));
//下面抛出ParseException异常
System.out.println(DateFormat.getDateInstance().
parse(str2));
上面代码中最后一行代码解析日期字符串时引发ParseException异常,因为”2014年12月10日”是一个LONG样式日期字符串,必须用LONG样式的DateFormat实例解析,否则将抛出异常。
五、使用SimpleDateFormat格式化日期
前面介绍的DateFormat的parse()方法可把字符串解析成Date对象,但实际上DateFormat的parse()方法不够灵活——它要求被解析的字符串必须满足特定的格式!为了更好地格式化日期、解析字符串,Java提供了SimpleDateFormat类。
SimpleDateFormat类是DateFormat的子类,正如他的名字所暗示的,它是“简单”的日期格式器。实际上SimpleDateFormat比DateFormat更简单,功能更强大。
SimpleDateFormat可以非常灵活地格式化Date,也可以用于解析各种格式的日期字符串。创建SimpleDateFormat对象时需要传入一个pattern字符串,这个pattern是一个日期模板字符串。
Date d = new Date();
//创建一个SimpleDateFormat对象
SimpleDateFormat sdf1 = new SimpleDateFormat("Gyyyy年中第D天");
//将d格式化成日期,输出:公元2016年中第223天
String dateStr = sdf1.format(d);
System.out.println(dateStr);
//一个非常特殊的日期字符串
String str = "16###八月##10";
SimpleDateFormat sdf2 = new SimpleDateFormat("y###MMM##d");
//将日期中字符串解析成日期输出:Wed Aug 10 00:00:00 CST 2016
System.out.println(sdf2.parse(str));
从上面程序可以看出,使用SimpleDateFormat类可以通过日期格式化成形如“公元2014年中第101天”这样的字符串,也可以把形如“14###三月##21”这样的字符串解析成日期,功能非常强大。
SimpleDateFormat把日期格式化成怎样的字符串,以及能把怎样的字符串解析成Date,完全取决于创建该对象时指定的pattern参数:pattern是一个使用日期字段占位符的日期模板。
六、Java 8新增的日期、时间格式器
Java 8新增的日期、时间API不仅包括了Instant、LocalDate、LocalDateTime、LocalTime等代表日期、时间的类,而且在java.time.format包下提供了一个DateTimeFormater格式器类,该类相当于前面介绍的DateFormat和SimpleDateFormat的合体,功能非常强大。
为了使用DateTimeFormatter进行格式化或解析,必须先获取DateTimeFormattr对象,获取DateTimeFormatter对象有如下三种常见的方式。
①**直接使用静态常量来创建DateTimeFormatter格式器。**DateTimeFormatter类中包含了大量的形如ISO_LOCAL_DATE、ISO_LOCAL_TIME、ISO_LOCAL_DATE_TIME等静态常量,这些静态常量本身就是DateFormatter实例。
②使用代表不同风格的枚举值来创建DateTimeFormatter格式器。在FormatStyle风格类中定义了FULL、LONG、MEDIUM、SHORT四个枚举值,它们代表日期、时间的不同风格。
③根据模板字符串来创建DateFormat格式器。类似于SimpleDateFormat,可以采用模式字符串来创建DateFormat。
1.使用DateTimeFormatter完成格式化
使用DateTimeFormatter将日期、时间(LocalDate、LocalDateTime、LocalTime等实例)格式化为字符串。可通过如下两种方式。
①调用DateTimeFormatter的format(TemporalAccessor temporal)方法执行格式化,其中LocalDate、LocalDateTime、LocalTime等都是TemporalAccessor接口的实现类。
②调用LocalDate、LocalDateTime、LocalTime等日期、时间对象的format(DateTimeFormatter formatter)方法执行格式化。
上面两种方式的功能相同,用法也类似。
public class DateFormatTest {
public static void main(String[] args) throws ParseException {
DateTimeFormatter[] formatters = new DateTimeFormatter[] {
//直接使用 常量创建DateTimeFormatter格式器
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
//使用本地化的不同风格来创建DateTimeFormatter格式器
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL,
FormatStyle.MEDIUM),
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG),
//根据模式字符串来创建DateFormatter格式器
DateTimeFormatter.ofPattern("Gyyyy%%MMM%%dd
HH:mm:ss")
};
LocalDateTime date = LocalDateTime.now();
//依次使用不同的格式器对LocalDateTime进行格式化
for(int i = 0;i < formatters.length;i++ )
{
//下面两行代码的作用相同
System.out.println(date.format(formatters[i]));
System.out.println(formatters[i].format(date));
}
}
}
使用DateTimeFormatter进行格式化时
不仅可以按系统预置的格式对日期、时间进行格式化,
也可以使用模式字符串对日期、时间进行自定义格式化,
由此可见,DateTimeFormatter的功能完全覆盖了传统的DateFormatter、SimpleDateFormatter功能。
提示:DateTimeFormatter还提供了一个toFormat()方法,该方法可以获取DateTimeFormatter对于的Format读写。
2.使用DateTimeFormatter解析字符串
为了使用DateTimeFormatter将指定字符串解析成日期、时间对象(LocalDateTime、LocalTime等实例),可通过日期、时间对象提供的parse(CharSequence text,DateFormatter formatter)方法进行解析。
//定义一个任意格式的日期、时间字符串
String str1 = "2016==08==10 01时17分10秒";
//根据需要解析的日期、时间字符串定义解析所用的格式器
DateTimeFormatter formatter1 = DateTimeFormatter
.ofPattern("yyyy==MM==dd HH时mm分ss秒");
//执行解析
LocalDateTime dt1 = LocalDateTime.parse(str1,
formatter1);
System.out.println(dt1);//输出2016-08-10T01:17:10
//下面代码再次解析另一个字符串
String str2 = "2016$$$八月$$$10 20时";
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy$$$MMM$$$dd HH时");
LocalDateTime dt2 = LocalDateTime.parse(str2,
formatter2);
System.out.println(dt2);//输出2016-08-10T20:00
上面程序中定义两个不同的格式日期、时间字符串。
为了解析它们,程序分别使用对于的格式化字符串创建了DateTimeFormatter对象,这样DateTimeFormatter即可按该格式字符串将日期、时间字符串解析成LocalDateTime对象。
最后
以上就是仁爱大门为你收集整理的Java 8的日期、时间类一、Date类二、Calendar类三、Java 8新增的日期、时间包四、使用DateFormat格式化日期、时间五、使用SimpleDateFormat格式化日期六、Java 8新增的日期、时间格式器的全部内容,希望文章能够帮你解决Java 8的日期、时间类一、Date类二、Calendar类三、Java 8新增的日期、时间包四、使用DateFormat格式化日期、时间五、使用SimpleDateFormat格式化日期六、Java 8新增的日期、时间格式器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复