Java 8 日期处理

912 阅读5分钟

原文

Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限。

Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API。

新API基于ISO标准日历系统,java.time包下的所有类都是不可变类型而且线程安全

类的名称描述
java.time.Instant时间戳
java.time.Duration持续时间,时间差
java.time.LocalDate只包含日期,比如:2018-02-05
java.time.MonthDay只包含月日,比如:02-05
java.time.LocalTime只包含时间,比如:23:12:10
java.time.LocalDateTime包含日期和时间,比如:2018-02-05 23:14:21
java.time.Period时间段
java.time.ZoneId时区id,比如 Asia/Shanghai
java.time.ZoneOffset时区偏移量,比如:+8:00
java.time.ZonedDateTime带时区的时间
java.time.Clock时钟,比如获取目前美国纽约的时间
java.time.format.DateTimeFormatter时间格式化

java.time.Instant

Instant 类有一个静态工厂方法 now() 会返回当前的时间戳

import java.time.Instant;

public class Demo {
    public static void main(String[] args) {
        Instant timestamp = Instant.now();
        System.out.println("What is value of this instant " + timestamp.toEpochMilli());
    }
}

时间戳信息里同时包含了日期和时间,这和 java.util.Date 很像。实际上 Instant 类确实等同于 Java 8 之前的 Date 类,你可以使用 Date 类 和 Instant 类各自的转换方法互相转换。

例如:
将 Instant 转换成 java.util.Date

Date.from(Instant) 

将 java.util.Date 转换成 Instant 类

Date.toInstant()

java.time.Duration

java.time.LocalDate

java 8 中的 LocalDate 用于表示当天日期。和 java.util.Date 不同,它只有日期,不包含时间。当你仅需要表示日期时就用这个类。

获取当前日期

import java.time.LocalDate;

public class Demo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
    }
}

获取当前 年、月、日

import java.time.LocalDate;

public class Demo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        int year = today.getYear();
        int month = today.getMonthValue();
        int day = today.getDayOfMonth();
    }
}

我们通过静态工厂方法 now() 非常容易地创建了当天日期。

你还可以调用另一个有用的工厂方法 LocalDate.of() 创建任意日期。 该方法需要传入年、月、日做参数,返回对应的 LocalDate 实例。这个方法的好处是没再犯老 API 的设计错误(比如年度起始于 1900,月份是从 0 开始等等)。

指定年月日

import java.time.LocalDate;

public class Demo {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2020,3,17);
    }
}

判断两个日期是否相等

import java.time.LocalDate;

public class Demo {
    public static void main(String[] args) {
        LocalDate date1 = LocalDate.now();
        LocalDate date2 = LocalDate.of(2020,3,17);

        if(date1.equals(date2)){
            System.out.println("时间相等");
        }else{
            System.out.println("时间不等");
        }
    }
}

LocalDate 日期不包含时间信息,它的 plus() 方法用来增加天、周、月,ChronoUnit 类声明了这些时间单位。由于 LocalDate 是不变类型,返回后一定要用变量赋值。

增加日期

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class Demo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        System.out.println("今天的日期为:"+today);
        LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
        System.out.println("一周后的日期为:"+nextWeek);
    }
}

可以看到新日期离当天日期是7天,也就是一周。你可以用同样的方法增加1个月、1年、1小时、1分钟甚至一个世纪,更多选项可以查看Java 8 API中的ChronoUnit类

判断日期是早于还是晚于另一个日期 LocalDate类有两类方法isBefore()和isAfter()用于比较日期。调用isBefore()方法时,如果给定日期小于当前日期则返回true。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class Demo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();

        LocalDate tomorrow = LocalDate.of(2018,2,6);
        if(tomorrow.isAfter(today)){
            System.out.println("之后的日期:"+tomorrow);
        }

        LocalDate yesterday = today.minus(1, ChronoUnit.DAYS);
        if(yesterday.isBefore(today)){
            System.out.println("之前的日期:"+yesterday);
        }
    }
}

检查闰年

import java.time.LocalDate;

public class Demo {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        if(today.isLeapYear()){
            System.out.println("This year is Leap year");
        }else {
            System.out.println("This year is not a Leap year");
        }

    }
}

java.time.MonthDay

MonthDay 只包含月和日,比较适合周期性的事件,比如生日

检查生日这种周期性事件

import java.time.MonthDay;

public class Demo {
    public static void main(String[] args) {
        MonthDay birthday = MonthDay.of(3,17);
		MonthDay currentMonthDay = MonthDay.now();
		
		if(currentMonthDay.equals(birthday)){
			System.out.println("是你的生日");
		}
    }
}

java.time.LocalTime

LocalTime 只包含时分秒

获取当前时间

import java.time.LocalTime;

public class Demo {
    public static void main(String[] args) {
        LocalTime time = LocalTime.now();
        System.out.println("获取当前的时间,不含有日期:" + time);
    }
}

通过增加小时、分、秒来计算将来的时间很常见。Java 8 除了不变类型和线程安全的好处之外,还提供了更好的 plusHours() 方法替换 add() ,并且是兼容的。注意,这些方法返回一个全新的 LocalTime 实例,由于其不可变性,返回后一定要用变量赋值。

增加时间

import java.time.LocalTime;

public class Demo {
    public static void main(String[] args) {
        LocalTime time = LocalTime.now();
        LocalTime newTime = time.plusHours(3);
        System.out.println("三个小时后的时间为:" + newTime);
    }
}

java.time.LocalDateTime

初始化时间

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime = LocalDateTime.of(2020, 8, 8, 0, 0);

加一个小时

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime newDateTime = dateTime.plusHours(1)

一减个小时

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime newDateTime = dateTime.minusHours(1)

格式化

LocalDateTime dateTime = LocalDateTime.now();
String time = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(dateTime)

java.time.ZonedDateTime、java.time.ZoneId

Java 8 不仅分离了日期和时间,也把时区分离出来了。现在有一系列单独的类如 ZoneId 来处理特定时区,ZoneDateTime 类来表示某时区下的时间。在 Java 8 以前这都是 GregorianCalendar 类来做的。

把本时区的时间转换成另一个时区的时间

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Demo {
    public static void main(String[] args) {
    	LocalDateTime localDateTime = LocalDateTime.now();
    	
    	ZoneId shanghai = ZoneId.of("Asia/Shanghai");
    	ZonedDateTime shanghaiDateTime  = ZonedDateTime.of(localDateTime, shanghai );
    }
}

java.time.Period

Period 用于表示时间段

计算两个日期之间的天数、周数或月数

import java.time.LocalDate;
import java.time.Period;

public class Demo {
    public static void main(String[] args) {
		LocalDate today = LocalDate.now();
		LocalDate date = LocalDate.of(2018, 12, 14);

		Period period = Period.between(today, date);
		System.out.println("相差" + period.getMonths() + "个月");
    }
}

java.time.format.DateTimeFormatter

DateTimeFormatter 用来 解析或格式化日期

格式化日期

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Demo {
    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("格式化后的日期为:" + formatter.format(localDateTime));
    }
}

解析格式化日期

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Demo {
    public static void main(String[] args) {
        String dateString = "2020-02-03 11:22:33";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        LocalDateTime localDateTime = LocalDateTime.parse(dateString,formatter);
    }
}

java.time.Clock

Java 8增加了一个Clock时钟类用于获取当时的时间戳,或当前时区下的日期时间信息。以前用到System.currentTimeInMillis()和TimeZone.getDefault()的地方都可用Clock替换

import java.time.Clock;

public class Demo {
    public static void main(String[] args) {
        // Returns the current time based on your system clock and set to UTC.
        Clock clock = Clock.systemUTC();
        System.out.println("Clock : " + clock.millis());

        // Returns time based on system clock zone
        Clock defaultClock = Clock.systemDefaultZone();
        System.out.println("Clock : " + defaultClock.millis());
    }
}