java8时间简介
在jdk1.8之前,我们使用时间Calendar或者SimpleDateFormat,但是Calendar的日历格式从0开始,容易给人误解。SimpleDateFormat非线程安全。不过我们可以借助joda库来简化开发。
joda-time
joda-time
2.9.2
好在jdk1.8在java.time.*包下给我们提供了简便的时间工具类,下面我们就先从SimpleDateFormat非线程安全的问题之后再到jdk1.8之前的解决方案和jdk1.8的解决方案。最后我们来学习使用jdk1.8的时间相关的API。
SimpleDateFormat线程不安全问题
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return sdf.parse("20210204");
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List> results = new ArrayList<>();
for(int i = 0;i <10;i++) {
results.add(pool.submit(task));
}
for(Future future : results) {
System.out.println(future.get());
}
pool.shutdown();
}
}
以上程序在多线程下执行会抛出类似如下所示的错误:
image.png
在jdk1.8之前的解决方案是使用ThreadLocal的方式
public static final ThreadLocal dateFormatThreadLocal = new ThreadLocal(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable task = new Callable() {
@Override
public Date call() throws Exception {
return dateFormatThreadLocal.get().parse("20210204");
}
};
// 省略了后面的调用部分
}
jdk1.8的解决方案如下:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable task= new Callable() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20210204", dtf);
}
};
ExecutorService pool = Executors.newFixedThreadPool(10);
List> results = new ArrayList<>();
for(int i = 0;i < 10;i++) {
results.add(pool.submit(task));
}
for (Future future : results) {
System.out.println(future.get());
}
pool.shutdown();
从以上看出jdk1.8的解决方案更加优雅。
jdk1.8的时间相关的api使用
/**
* Local(本地):简化了日期时间的处理,没有时区的问题。
* Zoned(时区):通过制定的时区处理日期时间。
* 具体的API:
* LocalDate:存储不包含时间的日期,比如2020-01-11。可以用来存储生日,周年纪念日,入职日期等。
* LocalTime:存储不包含日期的时间,比如11:07:03.580
*/
public class DateTimeTest {
public static void main(String[] args) {
//通过字符串指定
LocalDate towDay = LocalDate.parse("2021-02-24");
System.out.println(towDay); // 2021-02-24
//比较日期是否相等
LocalDate date1 = LocalDate.parse("2021-02-11");
LocalDate date2 = LocalDate.parse("2021-02-04");
System.out.println(date1.equals(date2)); // false
//日期前后比较
boolean before = LocalDate.parse("2021-02-11").isBefore(LocalDate.now());
System.out.println(before); // true
Clock clock = Clock.systemUTC();
System.out.println(clock); // SystemClock[Z]
System.out.println(clock.instant()); // 获取UTC时区转换的当前时间 2021-02-24T04:37:26.632Z
System.out.println(clock.millis()); // 获取UTC时区转换的毫秒数 1614141446633
}
private static void formatterTest() {
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(isoDateTime);
System.out.println(strDate); // 2021-02-24T12:05:15.447
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format = dtf.format(ldt);
System.out.println(format); // 2021年02月24日 12:02:05
LocalDateTime newDate = ldt.parse(format,dtf);
System.out.println(newDate); //2021-02-24T12:05:15
Set availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(System.out::println); // 打印所有的时区,如 Asia/Shanghai
LocalDateTime now = LocalDateTime.now(ZoneId.of("America/Jujuy"));
System.out.println(now); // 2021-02-24T01:05:15.463
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("America/Jujuy"));
ZonedDateTime atZone = ldt2.atZone(ZoneId.of("America/Jujuy"));
System.out.println(atZone); // 2021-02-24T01:05:15.463-03:00[America/Jujuy]
}
private static void temporalAdjsuterTest() {
/**
* 时间矫正器 TemporalAdjuster 有时我们可能需要获取
* 例如: 将日期调整到“下个周日”等操作
* TemporalAdjusters: 该类通过静态方法提供了大量的常用TemporalAdjuster操作
*/
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt); // 2021-02-24T11:37:23.442
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2); // 2021-02-10T11:37:23.442
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3); // 2021-02-28T11:37:23.442
//自定义: 下一个工作日
LocalDateTime ldt5 = ldt.with((l) ->{
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
if(dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(3);
}else if(dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
}else {
return ldt4.plusDays(1);
}
});
System.out.println(ldt5); // 2021-02-25T11:37:23.442
}
private static void durationAndPeriodTest() {
/**
* Duration: 计算两个时间之间的间隔
* Period: 计算两个日期之间的间隔
*/
Instant ins1 = Instant.now();
Instant ins2 = Instant.now();
Duration duration = Duration.between(ins1,ins2);
System.out.println(duration.toMillis()); // 0
LocalTime lt1 = LocalTime.now();
LocalTime lt2 = LocalTime.now();
System.out.println(Duration.between(lt1,lt2).toMillis()); // 0
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2015,1,1);
Period period = Period.between(ld1,ld2);
System.out.println(period); // P-6Y-1M-23D
System.out.println(period.getYears()); // -6
}
private static void instantTest() {
/**
* Instant : 时间戳 (以unix元年 1970-01-01 00:00:00 到某个时间之间的毫秒值)
*/
Instant inst1 = Instant.now(); // 默认获取UTC时区
System.out.println(inst1); // 2021-02-24T03:27:37.359Z
// 获取东8区,即北京时间
OffsetDateTime odt = inst1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt); // 2021-02-24T11:27:37.359+08:00
// 转为毫秒值
System.out.println(inst1.toEpochMilli()); // 1614137257359
// 给定秒获取时间
Instant ofEpochSecond = Instant.ofEpochSecond(1000);
System.out.println(ofEpochSecond); // 1970-01-01T00:16:40Z
}
/**
* LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,
* 分别表示使用ISO-8601日历、日期和时间。他们提供了简单的日期或
* 时间,不包含与时区相关的信息。
* ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
*/
private static void localDateTimeTest() {
// 当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt); // 2021-02-24T11:22:14.701
// 给定时间的LocalDateTime
LocalDateTime ldt2 = LocalDateTime.of(2020,2,24,11,18);
System.out.println(ldt2); // 2020-02-24T11:18
// 当前时间加两年后的时间
LocalDateTime plusYears = ldt.plusYears(2);
System.out.println(plusYears); // 2023-02-24T11:22:14.701
// 减去两个月的时间
LocalDateTime minusMonths = ldt.minusMonths(2);
System.out.println(minusMonths); // 2020-12-24T11:22:14.701
System.out.println(ldt.getYear()); // 2021
System.out.println(ldt.getMonthValue()); // 2
System.out.println(ldt.getMonth()); // FEBRUARY
System.out.println(ldt.getDayOfMonth()); // 24
System.out.println(ldt.getDayOfWeek()); // WEDNESDAY
System.out.println(ldt.getMinute()); // 22
}
}