1. 前言
基本所有语言都免不了和日期打交道,无论是web应用、还是桌面、亦或手机应用;
所以,了解java日期相关类,了解如何处理日期是非常有必要的!
2. 几个概念
日期:2020-03-14
、2019-03-04
是日期
时间:12:30:00
、2020-03-14 20:21:00
是时间
时区:
GMT(Greenwich Mean Time)
格林尼治时间,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。
UTC(Universal Time Coordinated)
统一协调时间,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间,标准 UTC 时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSXXX
。
格林尼治时间已经不再被作为标准时间使用,UTC 是最主要的世界时间标准。
Java提供了两个获取当前时间的方法
-
System.currentTimeMillis()
,返回当前时间,以毫秒为单位,表示的就是当前时刻至1970-01-01 00:00:00.000
的毫秒差值。返回的long值可以用来初始化java.util.Date, java.sql.Date, java.sql.Timestamp和java.util.GregorianCalendar对象。 -
System.nanoTime()
,返回一个时间值(系统计时器的当前值),精确到纳秒。它是由 JVM 提供的一个时间,主要用来精确衡量两个时间段之间的时间
System.out.println(System.currentTimeMillis());
System.out.println(System.nanoTime());
/*1584156469894
11474125265100*/
上面的System.currentTimeMillis()
获取的值,通常也叫时间戳,时间戳有两种单位(秒、毫秒),显然毫秒级的时间戳更精确;
拥有时间戳其实我们更好的能处理时间,因为时间戳转换为日期时间后,并不会有时区的困扰;
3. Date类
java.util.Date
类用于封装日期及时间信息,一般仅用它显示某个日期,不对他作任何操作处理,作处理推荐用Calendar类,计算方便。
但是Date类有很多方法都已经过时了,下面仅列出未过时的方法!
构造方法
Date() :分配 Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date):分配 Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
演示
public static void main(String[] args) {
//1、使用Date类获取当前系统时间
Date date = new Date();
System.out.println(date);//Sat Mar 14 11:33:59 CST 2020
//2.传入一个时间戳,那么实例化出来的日期就是时间戳的
//分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数)
long time = System.currentTimeMillis();
System.out.println(new Date(time));//Sat Mar 14 11:35:00 CST 2020
}
4. Calendar类与GregorianCalendar
java.util.Calendar
类用于封装日历信息,其主作用在于其方法可以对时间分量进行运算。Calendar类是一个抽象类,它为特定瞬间与一组诸如 YEAR
、MONTH
、DAY_OF_MONTH
、HOUR
等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
Calendar类是抽象类,且Calendar类的构造方法是protected的,所以无法使用Calendar类的构造方法来创建对象,API中提供了getInstance方法用来创建对象。
Calendar calendar = new GregorianCalendar();//新建出来的calendar默认时间为当前时间,或者说创建出这个对象的时间。
System.out.println(calendar);
/*java.util.GregorianCalendar
[time=1584157080361,areFieldsSet=true,areAllFieldsSet=true,lenient=true,
zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,
dstSavings=0,useDaylight=false,transitions=29,lastRule=null],
firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2020,MONTH=2,
WEEK_OF_YEAR=11,WEEK_OF_MONTH=2,DAY_OF_MONTH=14,DAY_OF_YEAR=74,DAY_OF_WEEK=7,
DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=11,HOUR_OF_DAY=11,MINUTE=38,SECOND=0,
MILLISECOND=361,ZONE_OFFSET=28800000,DST_OFFSET=0]*/
通过Calendar的静态方法获取一个实例该方法会根据当前系统所在地区来自行决定时区,帮我们创建Calendar实例,这里要注意,实际上根据不同的地区,Calendar有若干个子类实现。而Calendar本身是抽象类,不能被实例化!我们不需要关心创建的具体实例为哪个子类,我们只需要根据Calendar规定的方法来使用就可以了。
日历类所解决的根本问题是简化日期的计算,要想表示某个日期还应该使用Date类描述。Calendar是可以将其描述的时间转化为Date的,我们只需要调用其getTime()方法就可以获取描述的日期的Date对象了。
Calendar calendar = new GregorianCalendar();
Date time = calendar.getTime();
System.out.println("time = " + time);//time = Sat Mar 14 11:39:39 CST 2020
通过日历类计算时间:为日历类设置时间,日历类设置时间使用通用方法set。set(int field,int value),field为时间分量,Calendar提供了相应的常量值,value为对应的值。
只有月份从0开始:0为1月,以此类推,11为12月,其他时间是正常的从1开始。也可以使用Calendar的常量 calendar.NOVEMBER……等。
Calendar.DAY_OF_MONTH //月里边的天---号;
Calendar.DAY_OF_WEEK //星期里的天---星期几
Calendar.DAY_OF_YEAR //年里的天
Calendar calendar=Calendar.getInstance();//构造出来表示当前时间的日历类
Date now=calendar.getTime();//获取日历所描述的日期
calendar.set(Calendar.YEAR, 2019);//设置日历表示2019年 calendar.set(Calendar.DAY_OF_MONTH,15);//设置日历表示15号
calendar.add(Calendar.DAY_OF_YEAR, 22);//想得到22天以后是哪天
calendar.add(Calendar.DAY_OF_YEAR, -5);//5天以前是哪天
calendar.add(Calendar.MONTH, 1);//得到1个月后是哪天
System.out.println(calendar.getTime());//得到日期Date对象
获取当前日历表示的日期中的某个时间单位可以使用get方法:
Calendar calendar = new GregorianCalendar();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "年" + (month + 1) + "月" + day + "日");//month要处理
//2020年3月14日
5. SimpleDateFormat类
构造方法
SimpleDateFormat(String pattern); //pattern -为描述日期和时间格式的模式
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
注:此构造方法可能不支持所有语言环境。要覆盖所有语言环境,请使用 DateFormat 类中的工厂方法。
常用方法
public final String format(Date date);//将一个 Date 格式化为日期/时间字符串
public Date parse(String source) throws ParseException;//从给定字符串的开始解析文本,以生成一个日期。
字符串转成Date对象
//创建一个SimpleDateFormat并且告知它要读取的字符串格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateFormat = "2020-03-14";//创建一个日期格式字符串
//将一个字符串转换为相应的Date对象
Date date = sdf.parse(dateFormat);//要先捕获异常
System.out.println(date);//输出这个Date对象
//Sat Mar 14 00:00:00 CST 2020
Date对象转成字符串
此方法我们常用来格式化时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String dateStr = sdf.format(date);//把日期对象传进去,继承自DateFormat类的方法。将一个Date格式化为日期/时间字符串
System.out.println("dateStr = " + dateStr);
//dateStr = 2020-03-14 11:45:40
一般中国网站都会使用类似:2020-03-14 11:45:40
的时间样式,所以需要格式化;
其中格式化出这样的样式来,完全依靠于我们传入的"yyyy-MM-dd HH:mm:ss"
;
SimpleDateFormat
是会解析特殊的字符,其余诸如空格之类的,是不会被解析的,更多特殊字符代表的含义参考下表:
Letter | Date or Time Component | Presentation | Examples |
---|---|---|---|
G | Era designator | Text | AD |
y | Year | Year | 1996; 96 |
Y | Week year | Year | 2009; 09 |
M | Month in year (context sensitive) | Month | July; Jul; 07 |
L | Month in year (standalone form) | Month | July; Jul; 07 |
w | Week in year | Number | 27 |
W | Week in month | Number | 2 |
D | Day in year | Number | 189 |
d | Day in month | Number | 10 |
F | Day of week in month | Number | 2 |
E | Day name in week | Text | Tuesday; Tue |
u | Day number of week (1 = Monday, ..., 7 = Sunday) | Number | 1 |
a | Am/pm marker | Text | PM |
H | Hour in day (0-23) | Number | 0 |
k | Hour in day (1-24) | Number | 24 |
K | Hour in am/pm (0-11) | Number | 0 |
h | Hour in am/pm (1-12) | Number | 12 |
m | Minute in hour | Number | 30 |
s | Second in minute | Number | 55 |
S | Millisecond | Number | 978 |
z | Time zone | General time zone | Pacific Standard Time; PST; GMT-08:00 |
Z | Time zone | RFC 822 time zone | -0800 |
X | Time zone | ISO 8601 time zone | -08; -0800; -08:00 |
6. DateFormat类
java.text.DateFormat
类(抽象类)是SimpleDateFormat类的父类,用的少,没SimpleDateFormat灵活。
Java8日期时间的默认格式如下:yyyy-MM-dd-HH-mm-ss.zzz
几个主要的核心类:
- LocalDate:日期类,不带时间
- LocalTime:时间类,不带日期
- LocalDateTime:日期和时间类
- ZonedDateTime:时区日期时间类
- OffsetDateTime:按UTC时间偏移来得到日期时间
- Clock:获取某个时区下当前的瞬时时间,日期或者时间
- Instant:Unix时间,代表时间戳,比如 2018-01-14T02:20:13.592Z
- Duration:两个时间之间,表示一个绝对的精确跨度,使用毫秒为单位
- Period:两个日期之间
- ZoneId:时区
- DateTimeFormatter:格式化输出
- TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等等
LocalDate、LocalTime、LocalDateTime
LocalDate
是不变的日期时间对象代表一个日期,往往被视为年月日。其他日期字段,如一年中的一天,一周和一周的一天,也可以访问。例如,“2007年10月2日”的值可以被存储在一个LocalDate。
LocalTime
是不变的日期时间对象代表一个时间,往往被视为小时分钟秒,时间为代表的纳秒级精度。例如,值“13:45.30.123456789”可以存储在一个LocalTime。
LocalDateTime
是不变的日期时间对象代表一个日期时间,往往被视为年、月、日、时、分、秒。其他日期和时间字段,如一年中的一天,一周和一周的一天,也可以访问。时间为代表的纳秒级精度。例如,值“2007年10月2日 13:45.30.123456789”可以存储在一个LocalDateTime。
public static void main(String[] args) throws ParseException {
//now()在默认时区中从系统时钟获取当前日期。
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); //等价于 today.plusWeeks(1)
//of(int year, int month, int dayOfMonth) 获得 LocalDate实例从一年、月、日。
LocalDate date = LocalDate.of(2020, 3, 14);
LocalTime time = LocalTime.of(11, 56, 20);
//of(LocalDate date, LocalTime time) 获得 LocalDateTime实例的日期和时间。
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println(dateTime);//2020-03-14T11:56:20
//LocalDate结合LocalTime成一个LocalDateTime
LocalDateTime dateTime2 = date.atTime(time);
System.out.println(dateTime2); //2020-03-14T11:56:20
}