昨天在工作中,写了一个关于时间的工具类,其中一个函数的功能是判断当前时间是否为0点。本来想一天是86400秒,如果通过当前的unix时间与86400取余,且余数为0的话,那么肯定是0点了。所以我写了如下的代码:
public class DateUtil {
public Boolean is0Clock(Long unixTimeStamp) {
return unixTimeStamp % 86400 == 0;
}
public static void main(String[] args) {
DateUtil dateUtil = new DateUtil();
/*设置日期与时间 2018-06-14T00:00:00*/
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2017);
/*Calendar类月份是0~11*/
calendar.set(Calendar.MONTH, 5);
calendar.set(Calendar.DATE, 14);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
System.out.println(dateUtil.is0Clock(calendar.getTimeInMillis() / 1000));
}
}
但是结果却不是预测的true,如下:
Connected to the target VM, address: '127.0.0.1:50429', transport: 'socket'
false
Disconnected from the target VM, address: '127.0.0.1:50429', transport: 'socket'
Process finished with exit code 0
后来我查了unix时间的获取方式,发现是因为时区的原因导致的。java中的unix时间是没有时区概念的,计算方式是此刻0时区的时间距0时区1970-01-01 00:00:00秒数。上例中2018-06-14T00:00:00其实是东八区的0点,在0时区的对应的时间其实是2018-06-13T16:00:00,减去1970-01-01T00:00:00,得到时间戳1528905600,与86400取余不为0。到此,上述例子不过的原因已经明了。东八区判断时间是否为0点的逻辑应该如下:
public Boolean is0Clock(Long unixTimeStamp) {
return (unixTimeStamp + 8 * 3600) % 86400 == 0;
}
Java8之前日期相关类
- java.util.Date
java.util.Date这个类现在除了获取unix时间的getTime(),构造函数Date()、Date(long date) 、方法from(Instatnt instant)及toString()方法外,其他方法都以标记为@Deprecated。 且toString()使用了默认时区,方法返回的是英文版日期格式,如下:
public class DateTest {
public void dateTest(){
Date date0 = new Date();
System.out.println(String.format("%d - %s", date0.getTime(), date0.toString()));
Date date1 = new Date(1528943069 * 1000L);
System.out.println(String.format("%d - %s", date1.getTime(), date1.toString()));
date1.setTime(0L);
System.out.println(String.format("%d - %s", date1.getTime(), date1.toString()));
}
public static void main(String[] args){
DateTest dateTest = new DateTest();
dateTest.dateTest();
}
}
运行结果:
Connected to the target VM, address: '127.0.0.1:51417', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:51417', transport: 'socket'
1528944004363 - Thu Jun 14 10:40:04 CST 2018
1528943069000 - Thu Jun 14 10:24:29 CST 2018
0 - Thu Jan 01 08:00:00 CST 1970
Process finished with exit code 0
- java.util.Calendar
java.util.Calendar 使用默认时区,除了获取 Unix 时间,也提供了诸多接口:“年、月、日、时、分、秒” 字段的获取与设置,时区设置,日期计算等接口。
public class CalendarTest {
void printCalendar(Calendar calendar) {
/*date 表示时间和日期,输出:
1. Unix 时间(毫秒)
2. 格式化输出 yyyy-MM-dd HH:mm:ss*/
System.out.println(String.format("%d -> %04d-%02d-%02d %02d:%02d:%02d", calendar.getTime().getTime(),
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DATE),
calendar.get(Calendar.HOUR), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND)));
}
public void calendarTest() {
/*当前时间*/
Calendar calendar = Calendar.getInstance();
printCalendar(calendar);
calendar.setTime(new Date(12 * 1000L));
/*输出:12000 -> 1970-01-01 08:00:12 注意是8点,因为Calendar使用了默认时区*/
printCalendar(calendar);
/*设置日期与时间 2018-06-14 00:00:00*/
calendar.set(2018, Calendar.JUNE, 14, 0, 0, 0);
/*输出:1528905600 -> 2018-06-14 00:00:00*/
printCalendar(calendar);
/*设置时区*/
calendar.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo"));
/*输出:东京: 1528905600000 - Thu Jun 14 00:00:00 CST 2018*/
System.out.println(String.format("东京: %d - %s", calendar.getTime().getTime(), calendar.getTime().toString()));
/*输出:1528905600000 -> 2018-06-14 01:00:00*/
printCalendar(calendar);
calendar.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
/*输出:上海: 1528905600000 - Thu Jun 14 00:00:00 CST 2018*/
/*输出:1528905600000 -> 2018-06-14 00:00:00*/
System.out.println(String.format("上海: %d - %s", calendar.getTime().getTime(), calendar.getTime().toString()));
printCalendar(calendar);
calendar.set(Calendar.SECOND, 30);
/*输出:1528905630000 - Thu Jun 14 00:00:30 CST 2018*/
System.out.println(String.format("%d - %s", calendar.getTime().getTime(), calendar.getTime().toString()));
/*输出:1528905630000 -> 2018-06-14 00:00:30*/
printCalendar(calendar);
/*日期计算*/
/*加30秒*/
calendar.add(Calendar.MINUTE, 30);
/*输出:1528907430000 -> 2018-06-14 00:30:30*/
printCalendar(calendar);
calendar.roll(Calendar.DATE, 1);
/*加1天*/
/*输出:1528993830000 -> 2018-06-15 00:30:30*/
printCalendar(calendar);
}
public static void main(String[] args){
CalendarTest calendarTest = new CalendarTest();
calendarTest.calendarTest();
}
}
- 格式化
java8之前,可以使用java.text.SimpleDateFormat,快速地进行日期与时间的格式化输出和字符串解析。
public class SimpleDateFormatTest {
public static void main(String[] args) {
try {
SimpleDateFormatTest simpleDateFormatTest = new SimpleDateFormatTest();
simpleDateFormatTest.simpleDateFormatTest();
} catch (Exception e) {
e.printStackTrace();
}
}
public void simpleDateFormatTest() throws ParseException {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
DateFormat dateFormatWithZone = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss XXX");
/*格式化*/
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(0));
/*输出:1970-01-01T08:00:00 +08:00*/
System.out.println(dateFormatWithZone.format(calendar.getTime()));
/*输出:1970-01-01T08:00:00*/
System.out.println(dateFormat.format(calendar.getTime()));
/*解析*/
String timeWithZone = "2017-11-30T00:00:00 +00:00";
/*转化为date对象*/
Date dateWithZone = dateFormatWithZone.parse(timeWithZone);
/*转化为String输出,输出:2017-11-30T08:00:00 +08:00*/
System.out.println(dateFormatWithZone.format(dateWithZone));
String time = "2017-11-30T00:00:00";
/*转化为date对象*/
Date date = dateFormat.parse(time);
/*转化为String输出,输出:2017-11-30T08:00:00*/
System.out.println(dateFormat.format(date));
}
}
综上,可以看出,使用Date类来进行时间操作是不合理的,因为Date类大多数方法都已经过期了,无法使用Date类的方法进行时间操作,另外Date不支持时区指定。Calendar可以进行时间操作,且可以指定时区,但是使用起来也有一定的不方便之处,比如Calendar的月份是0~11。用来格式化和解析的SimpleDateFormat也非线程安全类。所以Java8之后,提供了另外一套时间类。分别是Instant、LocalDateTime、LocalDate、LocalTime、ZonedDateTime,及用来格式化的DateTimeFormat。
Java8之后日期相关类
- Instant
java.time.Instant为纳秒级精度unix时间,其toString()方法基于 ISO-8601 进行格式化。Instant更多用作Date和LocalDateTime、LocalDate、LocalTime转换的中间工具。如下:
public class InstantTest {
public void instantTest(){
Instant now = Instant.now();
/*输出:2018-06-14T04:21:35.411Z*/
System.out.println(now);
Instant instant = Instant.ofEpochSecond(1528905600L);
/*输出:2018-06-13T16:00:00Z*/
System.out.println(instant);
instant = Instant.ofEpochMilli(1528905600000L);
/*输出:2018-06-13T16:00:00Z*/
System.out.println(instant);
instant = Instant.ofEpochSecond(1528905600L, 999);
/*2018-06-13T16:00:00.000000999Z*/
System.out.println(instant);
}
public static void main(String[] args){
InstantTest instantTest = new InstantTest();
instantTest.instantTest();
}
}
- LocalDateTime、LocalDate、LocalTime
LocalDateTime本地日期时间,LocalDate本地日期,LocalTime本地时间。LocalDate实例与LocalTime实例能够共同构建LocalDateTime实例,由LocalDateTime实例能够获取LocalDate实例与LocalTime实例。如下:
public class LocalDateTimeTest {
public void localDateTimeTest(){
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
/*设置到分*/
LocalDateTime dateTime = LocalDateTime.of(2018,6,14,0,0);
/*输出:2018-06-14T00:00*/
System.out.println(dateTime);
/*设置毫秒*/
dateTime = LocalDateTime.of(2018,6,14,0,0,30);
/*输出:2018-06-14T00:00:30*/
System.out.println(dateTime);
/*设置到纳秒*/
dateTime = LocalDateTime.of(2018,6,14,0,0,30, 999);
/*输出:2018-06-14T00:00:30.000000999*/
System.out.println(dateTime);
/*LocalDate & LocalTime -> LocalDateTime*/
LocalDate date = LocalDate.of(2018,6,14);
LocalTime time = LocalTime.of(1, 0, 0, 30);
dateTime = date.atTime(time);
/*输出:2018-06-14T01:00:00.000000030*/
System.out.println(dateTime);
dateTime = date.atTime(1,59,59);
/*输出:2018-06-14T01:59:59*/
System.out.println(dateTime);
dateTime = date.atStartOfDay();
/*输出:2018-06-14T00:00*/
System.out.println(dateTime);
dateTime = time.atDate(date);
/*输出:2018-06-14T01:00:00.000000030*/
System.out.println(dateTime);
/*LocalDateTime -> LocalDate & LocalTime*/
dateTime = LocalDateTime.of(2018,6,14,0,0,30, 999);
date = dateTime.toLocalDate();
/*输出:2018-06-14*/
System.out.println(date);
time = dateTime.toLocalTime();
/*输出:00:00:30.000000999*/
System.out.println(time);
}
public static void main(String[] args){
LocalDateTimeTest localDateTimeTest = new LocalDateTimeTest();
localDateTimeTest.localDateTimeTest();
}
}
- java.time.ZonedDateTime
java.time.ZonedDateTime用于表示位于特定 “时区” 的 “日期与时间”,通常用于 “Unix 时间” 和特定时区时间的本地时间调整。如下:
public class ZonedDateTimeTest {
public void zonedDateTimeTest(){
LocalDateTime localDateTime = LocalDateTime.of(2018, 6, 14,0,0,0);
/*localDateTime设置为2018-06-14 00:00:00*/
System.out.println(localDateTime);
/*localDateTime的东八区本地时间*/
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
/*输出:2018-06-14T00:00+08:00[Asia/Shanghai]*/
System.out.println(zonedDateTime);
/*东八区本地时间转unix时间,输出:1528905600*/
System.out.println(zonedDateTime.toInstant().getEpochSecond());
/*由zonedDateTime获取相同的Unix时间下,东九区的zoneDateTime*/
zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
/*输出:2018-06-14T01:00+09:00[Asia/Tokyo]*/
System.out.println(zonedDateTime);
/*东九区时间转unix时间,输出:1528905600*/
System.out.println(zonedDateTime.toInstant().getEpochSecond());
/*由东九区zoneDateTime,获取相同本地时间东八区的zoneDateTime*/
zonedDateTime = zonedDateTime.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
/*输出:2018-06-14T01:00+08:00[Asia/Shanghai]*/
System.out.println(zonedDateTime);
/*东八区unix时间,输出:1528909200*/
System.out.println(zonedDateTime.toInstant().getEpochSecond());
}
public static void main(String[] args){
ZonedDateTimeTest zonedDateTimeTest = new ZonedDateTimeTest();
zonedDateTimeTest.zonedDateTimeTest();
}
}
- java.time.format.DateTimeFormatter
java.time.format.DateTimeFormatter 能够进行LocalDate、LocalTime、LocalDateTime、ZonedDateTime的格式化输出。同时,LocalDate、LocalTime、LocalDateTime、ZonedDateTime 提供了静态的 parse 方法,能够进行字符串解析。
public class DateTimeFormatTest {
public void dateTimeFormatTest(){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒");
ZonedDateTime zonedDateTime = Instant.ofEpochSecond(1528905600).atZone(ZoneId.of("Asia/Shanghai"));
/*输出:2018 年 06 月 14 日 00 时 00 分 00 秒*/
System.out.println(formatter.format(zonedDateTime));
zonedDateTime = Instant.ofEpochSecond(1528905600).atZone(ZoneId.of("Asia/Tokyo"));
/*输出:2018 年 06 月 14 日 01 时 00 分 00 秒*/
System.out.println(formatter.format(zonedDateTime));
LocalDateTime localDateTime = LocalDateTime.parse("2018 年 06 月 14 日 01 时 00 分 00 秒", formatter);
/*输出:2018-06-14T01:00*/
System.out.println(localDateTime);
}
public static void main(String[] args){
DateTimeFormatTest dateTimeFormatTest = new DateTimeFormatTest();
dateTimeFormatTest.dateTimeFormatTest();
}
}
另外,LocalDate、LocalTime、LocalDateTime、ZonedDateTime的toString方法也可以格式化输出,预格式如下:
类型 | 默认格式示例 |
| 2017-11-23T10:15:30.00Z |
| 2017-11-23 |
| 10:15:30 |
| 2017-11-23T10:15:30 |
| 2017-11-23T10:15:30+01:00[Asia/Shanghai] |