предисловие
В прошлой статье я разобрался с часовым поясом, в этой статье в основном речь идет о том, какой 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
из. Так тебе нужно, чтобы я сказал тебе, кого выбрать~~
Эпилог
Люди всегда склонны к тому, что им знакомо, и склонны сопротивляться новому, что незнакомо.Я не раз сопротивлялся, мой дорогой архитектор, и просил нас заменить новые технические компоненты, но потом я полюбил их все новые технологии .
Сопротивление новым технологиям — это не то, что должен иметь хороший программист, и это будет препятствовать вашему росту. Появление новых технологий часто должно компенсировать недостатки старых технологий, и ни одна организация не будет тратить трудовые и материальные ресурсы на производство компонента отходов... Поэтому всякий раз, когда появляется новый компонент технологии, попробуйте его, и может быть неожиданная прибыль!