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

Java

До Java 8 обработку даты и времени можно было делать через Date и Calendar, потому что я давно не связывался с Java, и моя обработка даты все еще на них.Новый набор API обработки даты Давайте обсудим, чем они отличаются от предыдущих классов обработки данных, каковы преимущества нового API и как их использовать.

Эта статья будет расширяться в следующем порядке:

  1. Почему появился новый API для обработки дат и какие проблемы с обработкой дат были в прошлом?
  2. Какие оптимизации были сделаны в API даты в Java 8 и каковы новые функции?
  3. Использование API даты Java 8.

Проблемы с датой

Давайте сначала рассмотрим некоторые проблемы с Date.Я проверил некоторую информацию в Интернете, и все они говорят, что у Date есть проблемы с потокобезопасностью и простотой использования. Давайте сначала посмотрим, в чем проблема.

проблема безопасности потоков

Напишите программу для запуска форматирования даты в многопоточном режиме.

public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        for(int i=0; i < 5; i++) {
            new Thread(() -> {
                for(;;) {
                    try{
                        System.out.println(Thread.currentThread() + ":" + simpleDateFormat.format(new Date(Math.abs(new Random().nextLong()))));
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                }
            }).start();
        }
    }

Как видите, в середине работы программы сообщается об ошибке.

run.png

мы нажимаем вSimpleDateFormatпосмотреть исходный код,SimpleDateFormatунаследовано отDateFormat,а такжеDateFormatвнутренне содержит глобальнуюcalendarобъекта, а форматирование или синтаксический анализ даты должен работать с этим объектом. Тогда давайте посмотримformatИсходный код этого метода.format.png

Вы можете видеть это в строке 943, даcalendarНазначается необходимость форматированияdateобъект, если в многопоточной среде установлен поток 1calendarраз, но логика форматирования не доработана, тут 2-й тред снова правcalendarПри установке времени настройка потока 1 перезаписывается, затем поток 1 считывает его позжеcalendarВремя объекта вызовет ошибку. Чтобы решить эту проблему, для каждого потока можно создать только один поток.SimpleDateFormat, изолированный от других потоков.

Точно так же дляparseМетод, каждый раз, когда строка даты анализируется в объекты,calendarОн очистит всю последнюю сохраненную информацию о дате и времени, а затем сохранит последнюю информацию о дате и времени, которая выйдет при многопоточности.После того, как поток 1 установит дату и время, поток 2 снова очистит их, и, наконец, программа сообщит об ошибке.

parseМетод, наконец, вызовет следующий метод в первом114Строка очищает информацию о дате.parse.png

Простота использования

Я считаю, что все знают сложность даты и календаря. Всего несколько примеров.

Dateмесяц начинается с0начать с11В конце каждой операции месяц должен быть увеличен на 1, чтобы соответствовать текущему месяцу.

Если вы хотите выполнить операции с датами, добавить один день, вычесть один день, добавить один месяц, вычесть один месяц и т. д., вы можете только передатьCalendarпродолжать,Dateа такжеCalendarПреобразование туда и обратно довольно хлопотно.

DateОбъединение даты и времени при столкновении со сценой, в которой нужно иметь дело только с датой или временем,DateЭто немного раздутый, аDateВывод также не очень читабелен, и не форматировать его выглядит мучительно.date.png

API новой даты Java 8

Основываясь на вышеуказанных проблемах, дата оптимизирована в Java 8. Во-первых, дата и время спроектированы как неизменяемые типы, как и тип String, что позволяет избежать проблемы безопасности потоков, вызванной изменением даты при многопоточности. Операция над датой создаст новый объект даты, кроме того, функция доработана, работа с датой стала более удобной, а вывод более удобным для пользователя.

Давайте взглянем на некоторые часто используемые API-интерфейсы для работы с датами, которые в основном предоставляет Java 8.new.png

Как видно из рисунка выше, Java 8 разделяет дату и время,LocalDateTimeсодержит дату и время,LocalDateТолько часть даты,LocalTimeсодержит только временную часть,Instantпредставляет мгновенный момент времени на временной шкале, но часовой пояс по умолчаниюUTC+0из.

public static void main(String[] args) {
        System.out.println("LocalDateTime: " + LocalDateTime.now());
        System.out.println("LocalDate: " + LocalDate.now());
        System.out.println("LocalTime: " + LocalTime.now());
        System.out.println("Instant: (UTC+0)" + Instant.now());
        System.out.println("Instant: (UTC+8)" + Instant.now().atZone(ZoneId.systemDefault()));
    }

datestr.png

LocalDateTime,LocalDate,LocalTime,Instantвсе сбудетсяTemporalа такжеTemporalAdjusterинтерфейс,TemporalПредоставляет некоторые интерфейсы для операций с датами, таких как сложение и вычитание дат,TemporalAdjusterПредоставляет только интерфейс для настройки объектов даты/времени.

Кроме тогоLocalDateTimeВнутри просто инкапсулированLocalDateа такжеLocalTime, когда верноLocalDateTimeКогда операция выполняется, операция выполняется для указанной даты или времени.

Кроме тогоLocalDateTime,LocalDate,LocalTime,InstantКаждая операция имеет свой ограниченный диапазон,LocalDateTimeМожет поддерживать операции даты и времени;LocalDateПоддерживает только операции с минимальной детализацией 1 день, а неLocalDateдействовать вовремя;LocalTimeОн поддерживает операции от наносекунд до часов и не может работать с датами;InstantПоддерживаются только операции от наносекунд до секунд. Неподдерживаемое исключение выдается, если выполняется операция за пределами указанного диапазона.

ноLocalTimeа такжеInstantизplusЕсть специальные, которые поддерживают операции до неба, т.к.plusВнутренне конвертируйте дни в единицы в пределах диапазона и выполняйте вычисления.time.png

instant.png

Использование нового API даты Java 8

Выше упоминалась оптимизация API даты Java 8 с точки зрения безопасности и простоты использования. Теперь давайте подытожим, как использовать часто используемый API в повседневной жизни.

здесь сLocalDateНапример,LocalDateTimeа такжеLocalTimeиспользование аналогично.

public static void main(String[] args) {
        // 获取当天日期时间
        LocalDate today = LocalDate.now();
        print("获取当天日期时间: ", today);

        // 加一天
        LocalDate tomorrow = today.plusDays(1);
        print("加一天: ", tomorrow);

        // 加一个月
        LocalDate nextMonth = today.plusMonths(1);
        print("加一个月: ", nextMonth);

        // 减一天
        LocalDate yesterday = today.minusDays(1);
        print("减一天: ", yesterday);

        // 减一个月
        LocalDate lastMonth = today.minusMonths(1);
        print("减一个月: ", lastMonth);

        // 获取今天是本月第几天
        int dayOfMonth = today.getDayOfMonth();
        print("获取今天是本月第几天: ", dayOfMonth);

        // 获取今天是本周第几天
        int dayOfWeek = today.getDayOfWeek().getValue();
        print("获取今天是本周第几天: ", dayOfWeek);

        // 获取今天是本年第几天
        int dayOfYear = today.getDayOfYear();
        print("获取今天是本年第几天: ", dayOfYear);

        // 获取本月天数。
        int daysOfMonth = today.lengthOfMonth();
        print("获取本月天数: ", daysOfMonth);

        // 获取本年天数
        int daysOfYear = today.lengthOfYear();
        print("获取本年天数: ", daysOfYear);

        // 获取本月指定的第n天
        LocalDate date1 = today.withDayOfMonth(15);
        print("获取本月指定的第n天: ", date1);

        // 获取本月的最后一天
        LocalDate lastDaysOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
        print("获取本月的最后一天: ", lastDaysOfMonth);

        // 日期字符串解析。 严格按照ISO yyyy-MM-dd 验证
        LocalDate date = LocalDate.parse("2021-01-17");
        print("日期字符串解析: ", date);

        // 日期字符串解析。 自定义格式
        DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-M-dd");
        LocalDate date2 = LocalDate.parse("2021-1-17", dft);
        print("日期字符串解析(日期字符串解析): ", date2);

        // 格式化日期
        String dateStr = today.format(dft);
        print("格式化日期: ", dateStr);

        // 自定义日期
        LocalDate cusDate = LocalDate.of(2020, 8, 14);
        print("自定义日期: ", cusDate);

        // 日期比较
        boolean before = today.isBefore(tomorrow);
        print("今天是否比明天早: ", before);

        boolean before1 = today.isBefore(yesterday);
        print("今天是否比昨天早: ", before1);

        boolean after = today.isAfter(tomorrow);
        print("今天是否比明天晚: ", after);

        boolean after1 = today.isAfter(yesterday);
        print("今天是否比昨天晚: ", after1);
        
        // 获取两个时间相差多少天/周/月...  根据单位不同返回不同
        long until = today.until(nextMonth, ChronoUnit.WEEKS);
        print("今天到下个月相差几周: ", until);

        Month month = today.getMonth();
        print("月份:", month);
        print("月份: ", month.getValue());
    }

outpu.png

Как видно из вывода за последний месяц, Java 8 оптимизирует месяц в класс перечисления, а также настраивает диапазон месяцев от 1 до 12.

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

Java 8 также предоставляет 2 API для вычисления разницы между двумя временами/датами.

Durationупакованный внутриsecondsа такжеnanos, первое - секунды, а второе - наносекунды, представляющие разницу между двумя временами;

а такжеPeriodупакованный внутриday,month,years3 атрибута, представляющих разницу между двумя периодами дат.

так,DurationТолько объекты, которые содержат время, могут быть вычислены, такие какLocalDateTime,LocalTime,Instant, если вычислитьLocalDateЕсли это так, будет неподдерживаемое исключение.

По аналогии,Periodкоторый поддерживает толькоLocalDateрасчет.

Давайте посмотрим, как они используются.

Duration
public static void main(String[] args) {
        LocalDateTime today = LocalDateTime.now();
        LocalDateTime tomorrow = today.plusDays(1);
        
        // 根据两个时间获取 Duration
        Duration duration = Duration.between(today, tomorrow);
        print("获取纳秒数差值:", duration.toNanos());
        print("获取毫秒数差值:", duration.toMillis());
        print("获取秒数差值: ", duration.getSeconds());
        print("获取分钟数差值:", duration.toMinutes());
        print("获取小时数差值:", duration.toHours());
        print("获取天数差值:", duration.toDays());

        // 当第1个时间比第2个时间小时为false, 反之true。可以用来判断2个时间的大小。
        boolean negative = duration.isNegative();
        print("isNegative: ", negative);
        
        // 以1天的差值创建Duration
        Duration duration1 = Duration.ofDays(1);
        print("以1天的差值创建Duration: ", duration1.getSeconds());
    }

duration.png

Durationтакже поддерживаетplusа такжеminusОперация, здесь не продемонстрированная.

Кроме тогоDurationСуществует также функция, которая может генерировать объекты путем разбора строк. Правила для строк таковы:PnDTnHnMn.nS.Pдля фиксированного запуска,nэто число,Dэто количество дней,TЗа представителем следует временная часть,H,M,SЧасы, минуты и секунды соответственно. Буквы не чувствительны к регистру, прописные или строчные. Также поддерживает+а также-.+Чтобы добавить время,-уменьшить время.

public static void main(String[] args) {
        Duration duration = Duration.parse("P1DT1H1M1S");
        print("当前时间加上1天1小时1分钟1秒的差值: ", duration.getSeconds());

        Duration duration1 = Duration.parse("P2D");
        print("当前时间加上2天的差值: ", duration1.getSeconds());

        Duration duration2 = Duration.parse("PT2H");
        print("当前时间加上2小时的差值: ", duration2.getSeconds());

        Duration duration3 = Duration.parse("PT-2H");
        print("当前时间减去2小时的差值: ", duration3.getSeconds());

        Duration duration4 = Duration.parse("PT-2H30M");
        print("当前时间减去1小30分的差值: ", duration4.getSeconds());

        Duration duration5 = Duration.parse("PT-2H-30M");
        print("当前时间减去2小30分的差值: ", duration5.getSeconds());
        
        // 上面的也可以写成这样
        Duration duration6 = Duration.parse("-PT2H30M");
        print("当前时间减去2小30分的差值: ", duration6.getSeconds());
    }

dtext.png

каждыйnФронт неявно добавляется+,картина-2H30Mэто значитминус 2 часа плюс 30 минут, то есть 1 час 30 минут, но еслиPдобавить один впереди-Если это так, это повлияет на все числа в нем. На самом деле, как и в начальной школе по математике, используйте круглые скобки, чтобы поместитьPnDTnHnMn.nSскобка, (PnDTnHnMn.nS), добавить-, все символы в нем перевернуты, но если вы добавите его перед одним числом, он повлияет только на себя.

Period

Periodа такжеDurationНа самом деле употребление почти одинаковое, и все они выражают разницу во времени. Просто один отображает дату, а другой — время с разной степенью детализации. Не для демонстрации.

Суммировать

Почти подведены итоги наиболее часто используемых функций и методов API обработки данных в Java 8. По сути, их очень много для повседневного использования. Этот набор API-интерфейсов для работы с датами очень удобен в использовании после выяснения их различий и логики, стоящей за ними. не говорить об этом.Использование, при разработке и использовании, проверьте документацию, и вы можете использовать ее сразу.Нижний слой эти базовые знания.