Java 8:新时间和日期API

目录

一、旧的时间和日期

二、Java 8 新时间和日期API

1、LocalDate 只获取年月日

2、LocalTime 只会获取时分秒

3、LocalDateTime 获取年月日时分秒,相当于 LocalDate + LocalTime

4、Instant 获取秒数,用于表示一个时间戳(精确到纳秒)

5、Duration : 用于计算两个“时间”间隔 ,Period : 用于计算两个“日期”间隔

6、DateTimeFormatter : 解析和格式化日期或时间

7、ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期

8、TemporalAdjuster : 时间校正器


一、旧的时间和日期

  • Java 的 java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,而且都不是线程安全的。
  • Date如果不格式化,打印出的日期可读性差。
Sat Oct 12 17:12:32 CST 2019

使用 SimpleDateFormat 对时间进行格式化,但 SimpleDateFormat 是线程不安全的,SimpleDateFormat 的 format 方法源码如下:

private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
    // Convert input date to time field list
    calendar.setTime(date);
    boolean useDateFormatSymbols = useDateFormatSymbols();
    for (int i = 0; i < compiledPattern.length; ) {
        int tag = compiledPattern[i] >>> 8;
        int count = compiledPattern[i++] & 0xff;
        if (count == 255) {
            count = compiledPattern[i++] << 16;
            count |= compiledPattern[i++];
        }
        switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char) count);
                break;
            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;
            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
        }
    }
    return toAppendTo;
}

其中calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的SimpleDateFormat对象【如用static修饰的 SimpleDateFormat 】调用format方法时,多个线程会同时调用 calendar.setTime 方法,可能一个线程刚设置好 time 值另外的一个线程马上把设置的 time 值给修改了导致返回的格式化时间可能是错误的。

在多并发情况下使用 SimpleDateFormat 需注意。例如如下代码:

 public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("20191012");
            }

        };
        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<Date>> lists = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            lists.add(pool.submit(task));
        }

        for (Future<Date> future : lists) {
            System.out.println(future.get());
        }
        pool.shutdown();
}

Caused by: java.lang.NumberFormatException: multiple points

解决线程安全问题的办法:

public static void main(String[] args) throws Exception {
    Callable<Date> task = new Callable<Date>() {
			@Override
			public Date call() throws Exception {
				return DateFormatThreadLocal.convert("20191012");
			}
		};
	ExecutorService pool = Executors.newFixedThreadPool(10);
	List<Future<Date>> lists = new ArrayList<>();	
	for (int i = 0; i < 10; i++) {
		lists.add(pool.submit(task));
	}	
	for (Future<Date> future : lists) {
		System.out.println(future.get());
	}	
	pool.shutdown();
}
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
	protected DateFormat initialValue(){
		return new SimpleDateFormat("yyyyMMdd");
	}
		
};	
public static final Date convert(String source) throws ParseException{
	return df.get().parse(source);
}

二、Java 8 新时间和日期API

Java 8的日期和时间类包含 LocalDate、LocalTime、Instant、Duration 以及 Period,这些类都包含在 java.time 包中,Java 8 新的时间API的使用方式,包括创建、格式化、解析、计算、修改,下面我们看下如何去使用。

1、LocalDate 只获取年月日

// 创建 LocalDate
// 获取当前年月日
LocalDate ld = LocalDate.now();
System.out.println(ld);
//构造指定的年月日
LocalDate ld1 = LocalDate.of(2019, 10, 12);
System.out.println(ld1);

// 获取年、月、日、星期几
int year = ld.getYear();
int year1 = ld.get(ChronoField.YEAR);
System.out.println(year + "," + year1);
// 获取月
Month month = ld.getMonth();
int month1 = ld.get(ChronoField.MONTH_OF_YEAR);
System.out.println(month + "," + month1);
// 获取日
int day = ld.getDayOfMonth();
int day1 = ld.get(ChronoField.DAY_OF_MONTH);
System.out.println(day + "," + day1);
// 获取星期几
DayOfWeek dayOfWeek = ld.getDayOfWeek();
int dayOfWeek1 = ld.get(ChronoField.DAY_OF_WEEK);
System.out.println(dayOfWeek + "," + dayOfWeek1);

运行结果:

2、LocalTime 只会获取时分秒

//创建 LocalTime
LocalTime localTime = LocalTime.of(12, 14, 14);
LocalTime localTime1 = LocalTime.now();
System.out.println(localTime + "," + localTime1);
//获取小时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
System.out.println(hour + "," + hour1);
//获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
System.out.println(minute + "," + minute1);
//获取秒
int second = localTime.getMinute();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
System.out.println(second + "," + second1);

 运行结果:

3、LocalDateTime 获取年月日时分秒,相当于 LocalDate + LocalTime

// 创建 LocalDateTime
LocalDateTime ldt = LocalDateTime.now();
LocalDate ld = LocalDate.now();
LocalTime lt = LocalTime.now();
LocalDateTime ldt1 = LocalDateTime.of(2019, Month.OCTOBER, 10, 14, 46, 56);
LocalDateTime ldt2 = LocalDateTime.of(ld, lt);
LocalDateTime ldt3 = ld.atTime(lt);
LocalDateTime ldt4 = lt.atDate(ld);
// 获取LocalDate
LocalDate localDate2 = ldt.toLocalDate();
// 获取LocalTime
LocalTime localTime2 = ldt.toLocalTime();
System.out.println(ldt);
System.out.println(ldt1);
System.out.println(ldt2);
System.out.println(ldt3);
System.out.println(ldt4);
System.out.println(localDate2);
System.out.println(localTime2);

 运行结果:

4、Instant 获取秒数,用于表示一个时间戳(精确到纳秒)

     如果只是为了获取秒数或者毫秒数,可以使用System.currentTimeMillis()。

// 创建Instant对象。默认使用UTC时区
Instant instant = Instant.now();
System.out.println(instant);
// 获取秒数
long currentSecond = instant.getEpochSecond();
System.out.println(currentSecond);
// 获取毫秒数
long currentMilli = instant.toEpochMilli();
System.out.println(currentMilli);

 运行结果:

5、Duration : 用于计算两个“时间”间隔 ,Period : 用于计算两个“日期”间隔

Instant ins1 = Instant.now();
System.out.println("--------------------");
try {
	Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
System.out.println("所耗费时间为:" + Duration.between(ins1, ins2).toMillis());
System.out.println("----------------------------------");
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());

 运行结果:

修改 LocalDate、LocalTime、LocalDateTime、Instant。

LocalDate、LocalTime、LocalDateTime、Instant 为不可变对象,修改这些对象对象会返回一个副本。

增加、减少年数、月数、天数等,以LocalDateTime为例:

LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 32, 0);
// 增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
// 减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);  
// 通过with修改某些值
// 修改年为2020
localDateTime = localDateTime.withYear(2020);
localDateTime = localDateTime.with(ChronoField.YEAR, 2020);
// 时间计算
// 获取该年的第一天
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(firstDayOfYear());

6、DateTimeFormatter : 解析和格式化日期或时间

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);

 运行结果:

7、ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期

LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);

运行结果:

8、TemporalAdjuster : 时间校正器

public void test() {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);
    LocalDateTime ldt2 = ldt.withDayOfMonth(10);
    System.out.println(ldt2);
    LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(ldt3);
    //自定义:下一个工作日
    LocalDateTime ldt5 = ldt.with((l) -> {
        LocalDateTime ldt4 = (LocalDateTime) l;
        DayOfWeek dow = ldt4.getDayOfWeek();
        if (dow.equals(DayOfWeek.FRIDAY)) {
            return ldt4.plusDays(3);
        } else if (dow.equals(DayOfWeek.SATURDAY)) {
            return ldt4.plusDays(2);
        } else {
            return ldt4.plusDays(1);
        }
    });
    System.out.println(ldt5);
}

运行结果:

TemporalAdjusters 包含许多静态方法,可以直接调用,以下列举一些:

方法名描述
dayOfWeekInMonth返回同一个月中每周的第几天
firstDayOfMonth返回当月的第一天
firstDayOfNextMonth返回下月的第一天
firstDayOfNextYear返回下一年的第一天
firstDayOfYear返回本年的第一天
firstInMonth返回同一个月中第一个星期几
lastDayOfMonth返回当月的最后一天
lastDayOfNextMonth返回下月的最后一天
lastDayOfNextYear返回下一年的最后一天
lastDayOfYear返回本年的最后一天
lastInMonth返回同一个月中最后一个星期几
next / previous返回后一个/前一个给定的星期几
nextOrSame / previousOrSame返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值