Java8日期处理

Java处理日期、日历和时间的方式一直为社区所诟病,将java.util.Date设定为可变类型,以及SimpleDateFormat的非现场安全使其应用非常受限。
Java8的新API基于ISO标准的日历系统,java.time包下的所有类都有不可变类型而且线程安全
编号 类的名称 描述
1 Instant 以GMT时间为起点的偏移量
2 Duration 持续时间, 时间差
3 LocalDate 只包含日期,比如:2021-07-29
4 LocalTime 只包含时间,比如: “15:24:05”
5 LocalDateTime 包含日期和时间,比如: “2021-07-29 15:24:05”
6 Period 时间段
7 ZoneOffset 时区偏移量,比如 : “+8:00”
8 ZoneDateTime 带时区的时间
9 Clock 时钟,比如获取当前北京时间
10 DateTimeFormatter 时间格式化
1、Instant类
Java 8中以Instant类定义为从起点开始的偏移量,起点为格林威治时间(GMT)1970-01-01:00:00(可以理解为Instant类取代了Date类),可以进行时间戳和日期时间的互相转换。Instant.now().toEpochMilli()函数和System.currentTimeMillis()函数效果类似
示例代码如下:
        System.out.println(" Instant.now() : " + Instant.now()); //当前时间
        System.out.println(" Instant.now().toEpochMilli() : " +           Instant.now().toEpochMilli()); //当前时间的毫秒数
        System.out.println(" System.currentTimeMillis() : " + System.currentTimeMillis());   //当前时间的毫秒数
        System.out.println(" Instant.now().getEpochSecond() : " + Instant.now().getEpochSecond()); //当前时间的秒数
执行结果如下:
 Instant.now() : 2021-07-31T07:19:38.056Z
 Instant.now().toEpochMilli() : 1627715978222
 System.currentTimeMillis() : 1627715978222
 Instant.now().getEpochSecond() : 1627715978
1.1 获取当前时间的时间戳方法(毫秒)如下:
1) Instant.now().toEpochMilli()函数;
2) System.currentTimeMillis()函数;
1.2 获取当前时间的时间戳方法(秒)如下:
1) Instant.now().getEpochSecond();
1.3 时间戳和指定格式时间日期的转换:
1) 第一种方式如下:
    //从1970-01-01T00:00:00Z的纪元中使用毫秒获得Instant的实例。
Instant instant = Instant.ofEpochMilli(1545195386715L);
ZoneId zone = ZoneId.systemDefault(); //获取系统的默认时区   
    //方法从Instant和区域ID获取LocalDateTime的实例。
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr1 = dateTimeFormatter.format(localDateTime);
System.out.println("dateStr1 : " + dateStr1);
2) 第二种方式如下:
Instant instant = Instant.ofEpochMilli(1545195386715L);
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr2 = zdt.format(dateTimeFormatter);
System.out.println("dateStr2 : " + dateStr2);
执行结果如下:
dateStr1 : 2018-12-19 12:56:26
dateStr2 : 2018-12-19 12:56:26
2、Duration 持续时间,时间差
此类以秒和纳秒为单位对数量或时间进行建模。我们可以使用between()方法比较两个时间的时间差
下面分别采用LocalTime和Instant的方式,示例如下:
LocalTime start = LocalTime.of(3, 20, 25, 1024);
LocalTime end = LocalTime.of(3, 22, 27, 1544);
long timeBetweenS =  Duration.between(start, end).getSeconds();
long timeBetweenN =  Duration.between(start, end).getNano();
System.out.println(" timeBetweenS : " + timeBetweenS);
System.out.println(" timeBetweenN : " + timeBetweenN);

Instant startInstant = Instant.parse("2021-07-29T17:30:30.00Z");
Instant endInstant = Instant.parse("2021-07-29T17:33:35.00Z");
long timeBetweenIS =  Duration.between(startInstant, endInstant).getSeconds();
long timeBetweenIN =  Duration.between(start, end).getNano();
System.out.println(" timeBetweenIS : " + timeBetweenIS);
System.out.println(" timeBetweenIN : " + timeBetweenIN);
执行结果如下:
 timeBetweenS : 122
 timeBetweenN : 520
 timeBetweenIS : 185
 timeBetweenIN : 520
3、LocalDate
1) LocalDate用来表示日期(但不包括时间),例如当前日期可用LocalDate.now(),示例如下:
LocalDate today = LocalDate.now();
System.out.println("今天日期是 : " + today);
执行结果如下:
今天日期是 : 2021-07-31
2) LocalDate不仅可以获取当前日期,也可以获取特定的日期,并设定对应的日期格式,示例如下:
LocalDate localDate = LocalDate.of(2021,7,29);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = localDate.format(dateTimeFormatter);
String dateStr1 = dateTimeFormatter.format(localDate);
System.out.println("dateStr : " + dateStr);
System.out.println("dateStr1 : " + dateStr1);
执行结果如下:
dateStr : 2021-07-29
dateStr1 : 2021-07-29
3) 获取年月日信息
获取所在的年份 : LocalDate.now().getYear();
获取所在的月份 : LocalDate.now().getMonthValue();
获取所在的天 : LocalDate.now().getDayOfMonth();
4) 判断两个日期是否相当
例如: LocalDate.now().equals(LocalDate.of(2021,7,29))
5) 日期的增减
例如日期往后推一周: LocalDate.now().plus(1, ChronoUnit.WEEKS);
6) 检查像生日这种周期性事件,示例如下:
LocalDate today = LocalDate.now();
LocalDate dateDay = LocalDate.of(2021,7,15);
MonthDay birthday = MonthDay.of(dateDay.getMonth(),dateDay.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(today);

LocalDate.now().plus(1, ChronoUnit.WEEKS);
System.out.println("一周后的时间是 : " + LocalDate.now().plus(1, ChronoUnit.WEEKS));
if(currentMonthDay.equals(birthday)){
   System.out.println("是你的生日");
}else if(currentMonthDay.isBefore(birthday)){
   System.out.println("你的生日还没有到");
}else {
   System.out.println("你的生日已经过了");
}
执行结果如下:
一周后的时间是 : 2021-08-07
你的生日已经过了
7) 检查闰年
LocalDate中的isLeapYear()函数用来表示是否是闰年,示例如下:
LocalDate today = LocalDate.now();
if(today.isLeapYear()) { //判断是否是闰年
System.out.println("This year is Leap year !");
} else {
System.out.println("This year is not Leap year !");
}
执行结果如下:
This year is not Leap year !
4、LocalTime
LocalTime函数用来表示时间(但不包括日期),用法类似于3.
5、LocalDateTime
LocalDateTime用来表示日期和时间,用法亦类似于3.
6、Period
Period是ISO-8601日历系统中基于日期的时间量,例如“2年、3个月和4天”。此类以年、月和日为单位对数量和时间进行建模。
1) 使用between()方法获取两个日期之间的差作为Period 对象,isNegative()方法判断日期的先后,任何一个时间如果是负数,则isNegative()方法返回true,因此可以用于判断endDate是否大于startDate
LocalDate startDate = LocalDate.of(2015, 2, 20);
LocalDate endDate = LocalDate.of(2017, 1, 15);
Period period = Period.between(startDate, endDate);
System.out.println("startDate晚于endDate : " + period.isNegative());
执行结果如下:
startDate晚于endDate : false
2) Period类通过解析文本序列来创建Period,其格式为“PnYnMnD”,示例如下:
Period fromCharYears = Period.parse("P2Y3M5D");
int years = fromCharYears.getYears();
int months = fromCharYears.getMonths();
int days = fromCharYears.getDays();
System.out.println("years : " + years +
" months : " + months + " days : " + days);
执行结果如下:
years : 2 months : 3 days : 5
7、ZoneId 指定时区的标识符
ZoneId类的主要方法如下表7.1所示:
方法 描述
String getDisplayName(TextStyle style,Locale locale) 用于获取区域的文本表示形式,例如“北京时间”或者"+08:00"
abstract String getId() 用于获取唯一的时区ID
static ZoneId of(String zoneId) 用于从ID中获取ZoneId的实例, 以确保该ID有效并可供使用
static ZoneId systemDefault() 用于获取系统默认时区
boolean equals(Object obj) 用于检查该时区ID是否等于另一个时区ID
1) getDisplayName函数,用于获取区域的文本表示形式,代码如下:
public class TestMain {
    public static void main(String[] args) {
        ZoneId z = ZoneId.systemDefault();
        System.out.println(z.getDisplayName(TextStyle.FULL, Locale.ROOT));
    }
}
代码执行结果如下所示:
China Time
2) getId函数,用于获取唯一的时区ID,代码如下:
public class TestMain {
    public static void main(String[] args) throws ParseException {
        ZoneId z = ZoneId.systemDefault();
        String s = z.getId();
        System.out.println("时区ID为 : " + s);
    }
}
结果是:
时区ID为 : Asia/Shanghai
冷知识了解:
DST:Daylight Saving Time 中文称作“夏令时”,夏季天亮的早,将时间调快一小时。我国1986-1991年实行夏令时,于1992年废除。
CST:China Standard Time 中国标准时。
GMT:Greenwich Mean Time,格林威治标准时,地球每15°经度 被分为一个时区,全球共分为24个时区,相邻时区相差一小时;例: 中国北京位于东八区。
Java8中已做调整,因此在1986-1991时间段不再存在时间差的问题,Java8之前的还存在此问题,例如:
    public static void main(String[] args) throws ParseException {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern ("yyyy-MM-dd HH:mm:ss");
        ZoneId zA = ZoneId.of("Asia/Shanghai");
        LocalDateTime localDateTimeA = LocalDateTime.of(1988,7,15,15,15,15);
        ZonedDateTime zonedDateTimeA = localDateTimeA.atZone(zA);
        String dateStrA = zonedDateTimeA.format(dateTimeFormatter);
        System.out.println("dateStrA  " + dateStrA);

        ZoneId zG = ZoneId.of("GMT+8");
        LocalDateTime localDateTimeG = LocalDateTime.of(1988,7,15,15,15,15);
        ZonedDateTime zonedDateTimeG = localDateTimeG.atZone(zG);
        String dateStrG = zonedDateTimeG.format(dateTimeFormatter);
        System.out.println("dateStrG  " + dateStrG);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        Date dateA88 = sdf.parse("1988-07-15 15:15:15");
        System.out.println("dateA88 = " + dateA88.toString());

        sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        Date dateG88 = sdf.parse("1988-07-15 15:15:15");
        System.out.println("dateG88 = " + dateG88.toString());

        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        Date dateA92 = sdf.parse("1992-07-15 15:15:15");
        System.out.println("dateA92 = " + dateA92.toString());

        sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        Date dateG92 = sdf.parse("1992-07-15 15:15:15");
        System.out.println("dateG92 = " + dateG92.toString());
    }
结果如下:
dateStrA  1988-07-15 15:15:15
dateStrG  1988-07-15 15:15:15
dateA88 = Fri Jul 15 15:15:15 CDT 1988
dateG88 = Fri Jul 15 16:15:15 CDT 1988
dateA92 = Wed Jul 15 15:15:15 CST 1992
dateG92 = Wed Jul 15 15:15:15 CST 1992
3) of函数,用于从ID中获取ZoneId的实例,以确保该ID有效并可供使用
示例代码如下:
        ZoneId zoneS = ZoneId.of("Asia/Shanghai");
        ZoneId zoneC = ZoneId.of("Asia/Chongqing");
        ZoneId zoneH = ZoneId.of("Hongkong");
        LocalTime TimeS = LocalTime.now(zoneS);
        LocalTime TimeC = LocalTime.now(zoneC);
        LocalTime TimeH = LocalTime.now(zoneH);
        System.out.println("TimeS : " + TimeS);
        System.out.println("TimeC : " + TimeC);
        System.out.println("TimeH : " + TimeH);
        System.out.println("TimeS.isBefore(TimeC) : " + TimeS.isBefore(TimeC));
结果如下所示:
TimeS : 10:55:36.851
TimeC : 10:55:36.851
TimeH : 10:55:36.851
TimeS.isBefore(TimeC) : false
4) systemDefault函数,用于获取系统默认时区
示例代码如下:
        ZoneId zone = ZoneId.systemDefault();
        System.out.println("zone : " + zone);
结果如下所示:
        zone : Asia/Shanghai
5) ZoneOffset 指定格林威治/ UTC 时间的时区偏移量
示例代码如下,检索出亚洲东八区,字母以H开头的时区:
    public static void main(String[] args) throws ParseException {
        // 获取所有可用的时区
        Set<String> allZones = ZoneId.getAvailableZoneIds();
        // 按自然顺序排序 Create a List using the set of zones and sort it.
        List<String> zoneList = new ArrayList<String>(allZones);
        Collections.sort(zoneList);
        LocalDateTime dt = LocalDateTime.now();
        for (String s : zoneList) {
            // 获取到的字符串可以通过ZoneId.of获取实例
            ZoneId zone = ZoneId.of(s);
            // 把本地时间加时区信息 转换成一个ZonedDateTime
            // 但是这个LocalDateTime不包含时区信息,是怎么计算出来的呢?本地时间与这个时区相差n小时?
            // 这里的偏移量是针对格林威治标准时间来说的 +3 ,就是比标准时间快3个小时
            // 如果说一个时区是 +5;而北京是+8,那么该时区比北京慢3个小时
            // 北京时间是12点,那么该时区12-3 = 9
            ZonedDateTime zdt = dt.atZone(zone);
            ZoneOffset offset = zdt.getOffset();
            if(!zone.getId().contains("Asia/H")){
                continue;
            }
            if(offset.getTotalSeconds() <= 0){
                continue;
            }
            int hour = offset.getTotalSeconds() / (60 * 60);
            String out = String.format("%35s %10s%n", zone, offset);

            if (hour == 8) { //筛选出东八区的时区
                System.out.printf(out);
            }
        }
    }
执行结果如下:
    Asia/Harbin     +08:00
    Asia/Hong_Kong     +08:00
8、ZoneDateTime 带时区的时间类
ZoneDateTime 类用于创建带有时区的时间,可以用于表示一个真实时间的开始时间,比如火箭升空的时间等
示例代码如下:
    public static void main(String[] args) throws ParseException {
        ZonedDateTime paris = ZonedDateTime.now(ZoneId.of("Europe/Paris")); // 欧洲巴黎 +2 时区
        ZonedDateTime shanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); // 亚洲上海 +8 时区
        System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(paris));
        System.out.println("paris.getOffset() : " + paris.getOffset());
        System.out.println("shanghai.getOffset() : " + shanghai.getOffset());
        System.out.println(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(paris));
        System.out.println(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(shanghai));
    }
执行结果如下:
paris.getOffset() : +02:00
shanghai.getOffset() : +08:00
2021-07-30T07:58:26.666+02:00
2021-07-30T13:58:26.683+08:00
9、Clock 时钟类
Clock类用于获取指定时区的日期、时间和时间戳。该类提供了大量的方法获取对应的日期和时间
示例代码如下:
    public static void main(String[] args) throws ParseException {
        Clock clock = Clock.system(ZoneId.ofOffset("UTC",ZoneOffset.of("+8")));
        Instant instant = clock.instant();
        ZoneId zoneId = ZoneId.ofOffset("UTC",ZoneOffset.of("+8"));
        LocalDateTime localDateTime1 = LocalDateTime.now(clock);
        LocalDateTime localDateTime2 = LocalDateTime.ofInstant(instant,zoneId);

        System.out.println("localDateTime1 : " + localDateTime1);
        System.out.println("localDateTime2 : " + localDateTime2);
        System.out.println("clock.millis() : " + clock.millis());
        System.out.println("System.currentTimeMillis() : " + System.currentTimeMillis());
    }
执行结果如下:
localDateTime1 : 2021-07-30T15:30:45.781
localDateTime2 : 2021-07-30T15:30:45.769
clock.millis() : 1627630245782
System.currentTimeMillis() : 1627630245782
10、DateTimeFormatter 时间格式化类
DateTimeFormatter类是final类的,即是不变对象,且是线程安全的(SimpleDateFormat类不是线程安全的)
DateTimeFormatter类提供了时间格式化的类型,即按照指定的格式或jdk默认的格式转换时间类型。
当用来转换Instant类型的时间时,因为Instant类型不带有时区信息,需要在DateTimeFormatter类中加上时区的信息
代码示例如下:
    public static void main(String[] args) throws ParseException {
        Instant localDateTime = Instant.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.systemDefault()); //设定默认的时区信息
        OffsetDateTime offsetDateTime =  localDateTime.atOffset(ZoneOffset.of("+8"));
        ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();// 函数封装有默认时区
        ZonedDateTime zonedDateTime2 = localDateTime.atZone(ZoneId.ofOffset("UTC",ZoneOffset.of("+8")));
        System.out.println("ocalDateTime format : " + formatter.format(localDateTime));
        System.out.println("zonedDateTime format : " + zonedDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        System.out.println("zonedDateTime2 format : " + zonedDateTime2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        System.out.println("offsetDateTime format : " + offsetDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
执行结果如下:
localDateTime format : 2021-07-31 15:05:41
zonedDateTime format : 2021-07-31 15:05:41
zonedDateTime2 format : 2021-07-31 15:05:41
offsetDateTime format : 2021-07-31 15:05:41
原文地址:https://www.cnblogs.com/ITBlock/p/15084616.html