saz 发表于 2021-11-29 10:50

Java 8 笔记 全新的日期时间API

# 全新的日期、时间API

## 线程安全问题

### Date线程不安全问题

多线程操作,解析同一个日期字符串

```java
public class Demo01 {
    /**
   * Date 线程不安全
   */
    @Test
    public void test01() throws ExecutionException, InterruptedException {
      List<Future<Date>> dates = new ArrayList<>();
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      ExecutorService pool = Executors.newFixedThreadPool(10);
      Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("2020-09-08");
            }
      };
      for (int i = 0; i < 100; i++) {
            dates.add(pool.submit(task));

      }
      for (Future<Date> date : dates) {
            System.out.println(date.get());
      }

    }

}
```

抛出`NumberFormatException`异常

```tex
Tue Sep 08 00:00:00 GMT+08:00 2020
Tue Sep 08 00:00:00 GMT+08:00 2020

java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "220022.E220022E44"
```

### Date线程解决方案-ThreadLocal

`ThreadLocal`处理

```java
/**
* @description: Date 线程不安全的解决方案-ThreadLocal
*/
public class Demo02 {

    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){

      @Override
      protected DateFormat initialValue(){
            return new SimpleDateFormat("yyyy-MM-dd");
      }
    };

    public static Date convert(String dateStr) throws ParseException {
      return df.get().parse(dateStr);
    }

    /**
   * Date 线程不安全,处理现场
   */
    @Test
    public void test01() throws ExecutionException, InterruptedException {
      List<Future<Date>> dates = new ArrayList<>();
      ExecutorService pool = Executors.newFixedThreadPool(10);
      Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return convert("2020-09-08");
            }
      };
      for (int i = 0; i < 100; i++) {
            dates.add(pool.submit(task));

      }

      pool.shutdown();

      for (Future<Date> date : dates) {
            System.out.println(date.get());
      }

    }
}
```

### 线程安全的类-LocalDate

```java
/**
* @description: 使用LocalDate DateTimeFormatter 线程安全的类解决线程安全问题
*/
public class Demo03 {
    /**
   * LocalDate 线程安全的类
   */
    @Test
    public void test01() throws ExecutionException, InterruptedException {
      List<Future<LocalDate>> dates = new ArrayList<>();
      DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
      ExecutorService pool = Executors.newFixedThreadPool(10);
      Callable<LocalDate> task = new Callable<LocalDate>() {
            @Override
            public LocalDate call() throws Exception {
                return LocalDate.parse("2020-09-08",dtf);
            }
      };
      for (int i = 0; i < 100; i++) {
            dates.add(pool.submit(task));

      }
      for (Future<LocalDate> date : dates) {
            System.out.println(date.get());
      }

    }
}
```


## 部分API示例

```java
/**
* @description: 新的类API
*/
public class TestLocalDateTime {

    // 创建对象
    static final LocalDateTime now = LocalDateTime.now();
    static final LocalDateTime point = LocalDateTime.of(2020, 9, 8, 15, 48, 35);
    @Test
    public void test01(){

      System.out.println(point); // 2020-09-08T15:48:35
      System.out.println(point.plusDays(1)); // 2020-09-09T15:48:35
      System.out.println(point.minusDays(1)); // 2020-09-07T15:48:35
      System.out.println(point.getMonth()); // SEPTEMBER
      System.out.println(point.getMonthValue()); // 9

    }

    /**
   * Unix元年
   */
    @Test
    public void test02(){
      Instant now = Instant.now(); // 默认获取UTC时区,世界协调时间
      System.out.println(now); // 2020-09-08T08:35:19.255Z
      System.out.println(now.toEpochMilli()); // 1599554269206
      OffsetDateTime fdt = now.atOffset(ZoneOffset.ofHours(8));
      System.out.println(fdt); // 2020-09-08T16:35:19.255+08:00
      System.out.println(fdt.toInstant()); // 2020-09-08T08:37:49.206Z
      System.out.println(fdt.toInstant().toEpochMilli()); // 1599554269206
    }

}
```



## 求始末日期工具类

```java
/**
* @description: 给定一个时间段的始末日期
*                                 使用1.8的静态方法和全新的API
*/
public interface RangeUtils {

   /*
    *        昨天
    */
    static Range lastDay() {
      LocalDate now = LocalDate.now();
      return new Range(now.plusDays(-1), now.plusDays(-1));
    }

   /*
    *        本周,是否包含未来
    */
    static Range currentWeek(boolean includeFuture) {
      LocalDate now = LocalDate.now();
      LocalDate start = now.with(WeekFields.of(Locale.CHINESE).dayOfWeek(), 1);
      LocalDate end = includeFuture ? now.with(WeekFields.of(Locale.CHINESE).dayOfWeek(), 7) : now;
      return new Range(start, end);

    }

    /**
    *        本月,是否包含未来
    */
    static Range currentMonth(boolean includeFuture) {
      LocalDate now = LocalDate.now();
      LocalDate start = now.with(TemporalAdjusters.firstDayOfMonth());
      LocalDate end = includeFuture ? now.with(TemporalAdjusters.lastDayOfMonth()) : now;
      return new Range(start, end);
    }

   /*
    *        前 month 个月的始末
    */
    static Range lastMonth(int month) {
      LocalDate now = LocalDate.now();
      LocalDate start = now.plusMonths(-month).with(TemporalAdjusters.firstDayOfMonth());
      LocalDate end = now.plusMonths(-month).with(TemporalAdjusters.lastDayOfMonth());
      return new Range(start, end);
    }

   /*
    *        前 week 个周的始末
    */
    static Range lastWeek(int week) {
      LocalDate now = LocalDate.now();
      LocalDate start = now.plusWeeks(-week).with(WeekFields.of(Locale.CHINESE).dayOfWeek(), 1);
      LocalDate end = now.plusWeeks(-week).with(WeekFields.of(Locale.CHINESE).dayOfWeek(), 7);
      return new Range(start, end);
    }

   /*
    * 指定日期到今天的始末
    */
    static Range untilToday(String yyyyMMdd) {
      LocalDate start = LocalDate.parse(yyyyMMdd, DateTimeFormatter.ofPattern("yyyy-MM-DD"));
      if (start.isAfter(LocalDate.now())) {
            throw new IllegalStateException(start + " is after today.");
      }
      return new Range(start, LocalDate.now());
    }

   /*
    * 转换指定起始日期
    */
    static Range range(String st1, String st2) {
      DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
      LocalDate start = LocalDate.parse(st1, dateTimeFormatter);
      LocalDate end = LocalDate.parse(st2, dateTimeFormatter);
      if (start.isAfter(end)) {
            throw new IllegalStateException(start + " is after " + end + ".");
      }
      return new Range(start, end);
    }

    /*
   * 日期的区间内部类
   */
    class Range {
      private final LocalDate start;
      private final LocalDate end;

      public Range(LocalDate start, LocalDate end) {
            this.start = start;
            this.end = end;
      }

      @Override
      public String toString() {
            return "Range{" +
                  "start=" + start +
                  ", end=" + end +
                  '}';
      }
    }
}
```

## 时间转化与比较
- rstFulBody 为返回对象

```java
// 确认验证码是否过期
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime createdTime = smsCheckCode.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
if (Duration.between(createdTime, now).toMinutes() > 5) {
    // 过期提示已过期
    rstFulBody.fail("该验证码已过期,请重新获取验证码!");
    smsCheckCodeService.deleteByPrimaryKey(smsCheckCode.getCkCodeId());
    return rstFulBody;
}
```



```java
LocalDateTime localDateTime=LocalDateTime.now()
Date date = Date.from(localDateTime.atZone( ZoneId.systemDefault()).toInstant());


Date startDate=new Date();
LocalDateTime localDateTime = startDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime()
```

## 当前所处在一年中的周数

```java
LocalDateTime now = LocalDateTime.now();
DayOfWeek dayOfWeek = DayOfWeek.MONDAY;
WeekFields weekFields = WeekFields.of(dayOfWeek,1);
int weekInYear = now.get(weekFields.weekOfWeekBasedYear());
```

```js
getWeekIndex(){
    // 截掉时分秒保留整数天
    var date=new Date(new Date().toLocaleDateString());
    //设置日期为当前周周四
    date.setDate(date.getDate()+(4-(date.getDay()||7)));
    var year=date.getFullYear();
    var firstDate=new Date(year,0,1);
    firstDate.setDate(firstDate.getDate()+(4-(firstDate.getDay()||7)));
    //当年第一天早于周四第一周延后七天
    if(firstDate.getFullYear()<year){
      firstDate.setDate(firstDate.getDate()+7);
    }
    //计算当前周和第一周之间周数差
    return Math.ceil(((date-firstDate)/86400000+1)/7);
},
```

nnfengxi 发表于 2021-11-29 13:49

学习,不知道java12有什么变化

saz 发表于 2021-11-29 14:15

nnfengxi 发表于 2021-11-29 13:49
学习,不知道java12有什么变化

jdk 到了17了=-=

wiliao123 发表于 2021-11-29 14:25

感谢&#128591;大佬
页: [1]
查看完整版本: Java 8 笔记 全新的日期时间API