API даты и времени Java 8 в действии

Java

1. Дата? Календарь?

  • В Java 1.0 поддержка даты и времени может зависеть только от класса java.util.Date. Из-за плохой простоты использования Date многие методы Date в Java 1.1 устарели и заменены классом Calendar.

  • Однако Календарь не прост в использовании, например, месяц начинается с 0, что не является потокобезопасным, что делает код очень подверженным ошибкам. Мы считаем, что наиболее часто используемый SimpleDateFormat есть в каждом проекте. Многие люди не очень интуитивно понимают потокобезопасность этого SimpleDateFormat. Давайте рассмотрим такой сценарий.

    //相信每个人项目中都有这么一个DateUtil、SimpleDateFormat
    public class DateUtil{
       private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd HH");
    }
    
    //调用方
    public class Test{
      //20191228 14
        System.out.println(DateUtil.simpleDateFormat.format(new Date()));
      
      //某个线程按照自己的时区,影响了全局的simpleDateFormat,其他调用者就得出错误结果
      //20191228 06
        new Thread(() -> {
    				DateUtil.simpleDateFormat.setTimeZone(TimeZone.getTimeZone("+7"));
    	      System.out.println(DateUtil.simpleDateFormat.format(new Date()));
        }).start();
    
      //20191228 06
        new Thread(() -> {
          System.out.println(DateUtil.simpleDateFormat.format(new Date()));
        }).start();
    }
    
  • Мало того, в коде есть и Дата, и Календарь, а большинство Java-программистов еще больше запутались. Поэтому, когда речь идет о дате и времени, в большинстве случаев функции даты и времени реализуются с помощью сторонних библиотек, таких как joda-time. Фактически, API даты и времени Java 8 может видеть многие функции joda-time.

  • Следуя этой тенденции, в основной версии Java 8 предоставляется ряд API-интерфейсов поддержки даты и времени. В этой статье в основном представлены эти новые API, поэтому вам не нужно перемещать new Date() сверху вниз.

2. Дата и время в Java 8

  • В этой статье в основном представлены эти новые API из даты и времени в Java 8.

LocalDate

  • Экземпляр класса LocalDate — это неизменяемый объект, который содержит только дату, но не информацию о времени и не информацию о часовом поясе.
//今天
LocalDate now = LocalDate.now();
System.out.println(now);
//年、月、日构造
LocalDate date = LocalDate.of(2018, 12, 12);
System.out.println(date);

LocalTime

  • LocalTime содержит только информацию о времени, а не информацию о дате.
LocalTime time = LocalTime.now();
LocalTime time1 = LocalTime.parse("12:4:20");

LocalDateTime

  • Комбинация LocalDate и LocalTime содержит как информацию о времени, так и информацию о дате.
//今天当前时间
LocalDateTime ltime1 = LocalDateTime.now();

//指定年、月、日、时、分、秒
LocalDateTime time = LocalDateTime.of(2019, 12, 26, 12, 11, 34);

//LocalDate + 时间信息
LocalDateTime ltime2= LocalDate.now().atTime(12,11,23);

//LocalTime + 日期信息
LocalTime.now().atDate(LocalDate.now());

Продолжительность, Период

  • Продолжительность/Период - это не момент времени, а временной интервал. Класс Period представляет временные интервалы года, месяца и дня, а Duration — временной интервал, представляющий секунды/наносекунды. т. е. период основан на значениях даты, а продолжительность основана на значениях времени.
  • Эти два класса предназначены для служб измерения даты и времени.
//几天前的实现
LocalDate date1 =LocalDate.now();
LocalDate date2 = LocalDate.of(2019, 12, 11);
Period period = Period.between(date1, date2);

//-17
System.out.println(period.getDays());

TemporalAdjuster

  • Вспомогательный класс, встроенный в Java 8, который обеспечивает более гибкую обработку дат.
  • Java 8 предоставляет большое количество предопределенных TemporalAdjusters, которые очень полезны для некоторых сложных операций со временем.
//可以看出这个类的作用是将一个Temporal转成另一个Temporal
//LocalDate、LoclaTime、LocalDateTime等都实现了Temporal接口
@FunctionInterface
public function TemporalAdjuster{
  Temporal adjustInto(Temporal temporal);
}
имя метода описывать
dayOfWeekInMonth Возвращает день недели в том же месяце
firstDayOfMonth Возвращает первый день месяца
firstDayOfNextMonth Возврат к первому дню следующего месяца
firstDayOfNextYear Возврат к первому дню следующего года
firstDayOfYear Вернуться к первому дню года
firstInMonth Возвращает первый день недели в том же месяце
lastDayOfMonth Возвращает последний день текущего месяца
lastDayOfNextMonth Возвращает последний день следующего месяца
lastDayOfNextYear Вернуться к последнему дню следующего года
nextOrSame / previousOrSame Возвращает следующий/предыдущий заданный день недели, если это значение удовлетворяет условию, возвращает напрямую

DateTimeFormatter

  • В основном используется для операций форматирования даты, format()/parse().
//format
LocalDateTime dateTime = LocalDateTime.now();
String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE);    // 20170105
String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);    // 2017-01-05
String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME);    // 14:20:16.998
String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));// 2017-01-05


//parse
String strDate6 = "2017-01-05";
String strDate7 = "2017-01-05 12:30:05";

LocalDate date = LocalDate.parse(strDate6, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDateTime dateTime1 = LocalDateTime.parse(strDate7, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

3. Общие функции

Местная дата, местное время

//今天12:00:00时间戳
LocalDate.now().atTime(12,0,0).toInstant(ZoneOffset.of("+8")).getEpochSecond());

//今天0:10:00时间戳
LocalDate.now().atTime(0,10,0).toInstant(ZoneOffset.of("+8")).getEpochSecond());


//几天/周/年前、后
//5天前
LocalDate.now().minusDays(5);

//1天后
LocalDate.now().plusDays(1);

//2周后
LocalDate.now().plusWeeks(2);


//几小时/分钟前、后
//2小时前
LocalTime.now().minusHours(2);

//5分钟后
LocalTime.now().plusMinutes(5);

Дата, LocalDate, LocalTime, LocalDateTime

//Date转LocalDateTime
Date date = new Date();
LocalDateTime time1 = LocalDateTime.ofInstant(date.toInstant(),ZoneOffset.of("+8"))
  								
//Date转LocalDate
LocalDate date1 = LocalDateTime.ofInstant(date.toInstant(),ZoneOffset.of("+8"))
  								.toLocalate()

//LocalDate无法直接转Date,因为LocalDate不含时间信息

//LocalDateTime转Date
LocalDateTime localDateTime3 = LocalDateTime.now();
Instant instant3 = LocalDateTime3.atZone(ZoneId.systemDefault()).toInstant();
Date date3 = Date.from(instant);

Использование DateTimeFormatter

//前端传递过来的字符串20191212,后端需要 2019-12-12
//除了replace(),更优雅的方式

DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
LocalDate formatted = LocalDate.parse("20191212",formatter);
//2019-12-12
System.out.println(formatted);

//前端传递过来的字符串2019-12-12,后端需要 20191212
LocalDate.parse("2019-12-12").format(DateTimeFormatter.BASIC_ISO_DATE));



//前端传递过来的字符串2019年12月12日,后端需要 2019-12-12
System.out.println(LocalDate.parse("2019年12月12日",DateTimeFormatter.ofPattern("yyyy年MM月dd日")));

TemporalAdjuster

//场景中需要使用距当前最近的周一
//返回上一个周一,如果今天是周一则返回今天
LocalDate date1 =LocalDate.now().with(previousOrSame(DayOfWeek.MONDAY));

//下一个周一/当天
LocalDate date1 =LocalDate.now().with(nextOrSame(DayOfWeek.MONDAY));


//本月最后一天
System.out.println(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()));


//明年第一天
System.out.println(LocalDate.now().with(TemporalAdjusters.firstDayOfNextYear()));



4. Резюме

  • В этой статье в основном представлены LocalDate, LocalTime, LocalDateTime, DateTimeFormatter, TemporalAdjuster, которые обычно используются в API даты и времени в Java 8, а также преобразование с помощью Date. Это значительно расширяет возможности выбора при разработке даты и времени и решает проблемы безопасности потоков, такие как Date и SimpleDateFormat.
  • Хорошо использует TemporalAdjuster в сложных сценариях даты и времени, действительно ароматный.