В каком возрасте вы все еще используете Date

Java задняя часть Архитектура

предисловие

В прошлой статье я разобрался с часовым поясом, в этой статье в основном речь идет о том, какой API лучше для работы с датой и временем в Java. Я не собирался писать эту статью, потому что я думаю, что Java17 вышла, и все знакомы с API времени и даты, предоставляемым Java8. Но после моего исследования многие малые и средние компании все еще используют старую версию.DateЧтобы иметь дело со временем и датой, это зависит от API времени и даты, предоставляемого Java8, поэтому я все же хочу порекомендовать новое поколение API времени и даты, надеясь помочь всем.

Традиционная дата

старая версияDateЯ думаю, что все знакомы с ним, вот несколько простых моментов

Значительный неприкосновенный часовой пояс

для старых версийDate,SimpleDateFormatЯ думаю, что все знакомы с ним.Стоит отметить, что вы не можете напрямую установитьDateИнформация о часовом поясе, но противоречит ей то, что мы читаем время с часовым поясом из базы данных в коде, например:2021-11-01 13:50:47.138494+00, но его можно автоматически преобразовать во время часового пояса, в котором находится текущий сервер.Dateв объекте. Фактически, это его переменная-членprivate transient BaseCalendar.Date cdateЧто нужно сделать, так как временная метка Unix не зависит от часового пояса, она будет преобразовывать время часового пояса базы данных в временную метку Unix при чтении из базы данных, а затем использовать эту временную метку для преобразования часового пояса текущего сервера. и несет информацию о часовом поясе.

На самом деле, мы можем посмотреть наDateИсходный код метода построения, представляющий собой полученную текущую отметку времени Unix.

public Date() {
    this(System.currentTimeMillis());
}

Сложные расчеты преобразования часовых поясов

Если нам нужно показатьDateВремя объекта в разных часовых поясах, то что нам нужно передатьSimpleDateFormatреализовать

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = new Date();//默认北京时区的时间
    System.out.println("北京时区:" + sdf.getTimeZone());
    System.out.println("北京时区时间:" + sdf.format(date));
    sdf.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Jakarta")));
    System.out.println("雅加达时区:" + sdf.getTimeZone());
    System.out.println("雅加达时区时间:" + sdf.format(date));

Если вам нужна не строка, а преобразование в другой часовой поясDateобъект, то вам также нужно перейти от строки кDate...что, несомненно, очень хлопотно

Комплексный расчет интервала времени

сказатьDateОдним из самых неудобных мест является вычисление разницы во времени между двумя датами.Раньше JDK не предоставлял прямого API для вычисления разницы между двумя датами.Dateинтервал. Обычно мы ставимDateОперация после преобразования в метку времени

    Date date1 = new Date();
    TimeUnit.SECONDS.sleep(10);
    Date date2 = new Date();
    long time1 = date1.getTime();
    long time2 = date2.getTime();
    System.out.println("间隔秒数:" + (time2 - time1) / 1000);
    System.out.println("间隔小时数:" + (time2 - time1) / (1000 * 3600));
    //...

Это хлопотно? Если вас это не напрягает, значит, вы просто не видели лучшего способа. Давайте посмотрим, как это делает новый API (подробнее об этом позже)

    Duration duration = Duration.between(time2, time1);
    long days = duration.toDays();//获取天数间隔
    long hours = duration.toHours();//获取小时间隔
    //...

Разве это не очень просто!

Должна ли база данных хранить информацию о часовом поясе?

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

часовой пояс хранения

В настоящее время мы получаем текущий сервер в коде (наш сервер находится в AmazonUTC-3) в часовом поясеDateобъекта, при сохранении в базе данных он преобразуется на уровне базы данных вUTC+8Время в часовом поясе хранится в базе данных, например.2021-10-24 15:47:47.138494-03хранится в базе данных2021-10-25 02:47:47.138494+08, а затем на странице внешнего интерфейса вы можете напрямую вызвать API для расчета времени в местном часовом поясе внешнего устройства в соответствии со временем с часовым поясом.

    xxx//假如设备在印度尼西亚,那么前端 API 获得的时间就是 2021-10-25 01:47:47.138494

Но я не очень понимаю, так как мы сохраняем часовой пояс, мы должны также сохранить часовой пояс Индонезии... Ведь наши дела в Индонезии, а не в Пекине...

Не сохранять часовой пояс

Как правило, местоположение сервера обычно уникально, даже если100Невозможно иметь половину серверов в США и половину в Китае, верно? Затем операции времени и даты в коде по умолчанию используют часовой пояс местоположения сервера (UTC-3), в этом случае наша база данных не может хранить информацию о часовом поясе, а может хранить только описание времени, такое как2021-10-24 13:50:47.138494, у которого нет самого часового пояса, но мы все знаем, что его часовой пояс по умолчаниюUTC-3.

Таким образом, при вызове API во внешнем коде нужно привести только часовой пояс, к которому относится время.UTC-3Можно рассчитать время в местном часовом поясе внешнего устройства.

xxx//假如设备在印度尼西亚,那么前端 API 获得的时间就是 2021-10-25 01:47:47.138494

И это время без часового пояса не будет зависеть от часового пояса базы данных, независимо от того, какой часовой пояс вы установили для базы данных, он не изменится. Этот метод заключается в том, что мы убиваем все часовые пояса во всем бизнесе, оставляя только один часовой пояс сервера.Когда есть какие-либо бизнес-функции, связанные с часовыми поясами, нам нужно только源时间Преобразуйте его во время часового пояса, в котором расположен сервер.

Суммировать

Еще один случай не хранения часовых поясов в том, что некоторые программисты любят хранить временные метки, и я должен пожаловаться, что считаю это одним из самых низких способов (может быть, можно назвать только его отдельные плюсы, но я не приемлю опровержения). Хотя временная метка не зависит от часового пояса, ее очень плохо читать... и ею нелегко манипулировать в коде.

На самом деле, при сравнении можно обнаружить, что инвентаризация данных не хранит информацию о часовом поясе, и разница между интерфейсными и внутренними операциями незначительна. И я думаю, что в большинстве бизнес-сценариев относительно проще не хранить часовой пояс!

API времени и даты Java8

LocalDate, LocalTime, LocalDateTime

Чтение исходного кода — хорошая привычка, а чтение комментариев к исходному коду — даже хорошая привычка. Аннотации этих трех классов очень ясны.Во-первых, эти классы потокобезопасны, и любая операция над ними будет генерировать новый экземпляр, который аналогиченStringКлассы одинаковые. Во-вторых, он не хранит и не представляет часовой пояс. Вместо этого это описание даты и времени.

Стоит отметить, что он не хранит часовой пояс, но это не значит, что у него нет часового пояса, посмотрите на это предложение! Поймите отношения между тремя через картину

Ниже сLocalDateTimeВ качестве примера кратко представим использование

LocalDateTime time1 = LocalDateTime.now();
LocalDateTime time2 = LocalDateTime.now();

time1.isAfter(time2); time1.isBefore(time2);//比较时间
time1.plusDays(1L); time1.minusHours(1L);//加减时间日期
LocalDateTime.parse("2021-11-19T15:16:17");//解析时间
LocalDateTime.of(2019, 11, 30, 15, 16, 17);//指定日期时间
LocalDateTime.now(ZoneId.of("Asia/Jakarta"));//其他时区相对此服务器时区的时间
//...

Используйте этот класс, если вы не можете хранить информацию о часовом поясе в базе данных. Если вы должны хранить, то также используйте следующиеOffsetDateTimeВместо того, чтобы использоватьDate.

OffsetDateTime

Класс Datetime со смещением часового пояса, эквивалентныйOffsetDateTime = LocalDateTime + ZoneOffset

В большинстве случаев мы можем использовать дату и время со смещением, чтобы удовлетворить наши потребности.

ZonedDateTime

Действительно класс datetime с полной информацией о часовом поясе

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

Эти два класса представляют период времени или интервал между двумя моментами времени, причемDurationНапример, представьте, что до Java8 у вас был бизнес по представлению токена со сроком действия7дней, то обычной практикой может быть сохранение времени создания токена, а затем вычитание времени создания токена и7дней для сравнения, например.

long duration = System.currentTimeMillis() - token.getCreateTime().getTime();
if (duration < 7 * 24 * 60 * 60 * 1000) { //令牌合法}

Но после использования Duration ,

Duration duration = Duration.between(token.getCreateTime(), LocalDateTime.now());
boolean negative = duration.minus(effective).isNegative();//是否过期

ВторойDurationВ проекте SpringBoot конфигурация тоже очень удобная

token:
  effective-time: 7d  # d:天 , h:小时 , m:分钟 , s:秒

В классе сущностей вы можете использовать@ConfigurationPropertiesили@Valueсопоставить его непосредственно сDurationобъекта, конечно, это зависит отSpringBootбогат впреобразователь типов. Следующая статья познакомит

TemporalAdjuster и TemporalAdjusters

Когда вы впервые увидели эти два класса, вы подумали о знакомых?Collectionа такжеCollections, аналогично этим двум классам являются интерфейс корректора времени и класс инструмента корректора времени. Почти все новые классы даты и времени реализованыTemporalAdjuster, чтобы его можно было вычислить, чтобы получить другое время для всех дат и времени, например.

LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth());//当月第一天
LocalDateTime.now().with(TemporalAdjusters.firstDayOfNextMonth());//下个月第一天
LocalDateTime.now().with(TemporalAdjusters.dayOfWeekInMonth(2,DayOfWeek.MONDAY));//第N个星期几
LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.MONDAY));//下个星期几
//...

С таким богатым API вам все еще нужно писать кучу классов инструментов для работы с датой и временем?

Преобразование даты и LocalDateTime

Нельзя отрицать, что есть явление, которое использует ваш проект.Date,а такжеleaderЕсли вы не хотите тратить время и энергию на обновление или если старый бизнес ограничен, то в некоторых сценариях вы можете использоватьInstantПреобразуйте их друг в друга, чтобы упростить некоторые операции бизнес-кода.

LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());//Date 转 LocalDateTime

Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());// LocalDateTime 转 Date

Суммировать

Существует множество применений инструментов времени и даты Java8, поэтому я не буду представлять их здесь по одному.Dateнекоторые функцииLocalDateTimeимеют,DateНе работает,LocalDateTimeЕсть намного больше. Используя новую версию даты и времени, оригинала почти не существуетDateUtilиз. Так тебе нужно, чтобы я сказал тебе, кого выбрать~~

Эпилог

Люди всегда склонны к тому, что им знакомо, и склонны сопротивляться новому, что незнакомо.Я не раз сопротивлялся, мой дорогой архитектор, и просил нас заменить новые технические компоненты, но потом я полюбил их все новые технологии .

Сопротивление новым технологиям — это не то, что должен иметь хороший программист, и это будет препятствовать вашему росту. Появление новых технологий часто должно компенсировать недостатки старых технологий, и ни одна организация не будет тратить трудовые и материальные ресурсы на производство компонента отходов... Поэтому всякий раз, когда появляется новый компонент технологии, попробуйте его, и может быть неожиданная прибыль!

Если эта статья была вам полезна, не забудьте поставить лайк и подписаться, ваша поддержка является движущей силой для меня, чтобы продолжать творить!