В Java вы действительно знаете преобразование даты?

Java задняя часть

1. Что такое SimpleDateFormat

Объяснение SimpleDateFormat в документе Java выглядит следующим образом:

SimpleDateFormat— это конкретный класс для форматирования и разбора дат в зависимости от локали. (дата → текст), синтаксический анализ (текст → дата) и нормализация.

SimpleDateFormat — это класс сущностей для чувствительного к положению форматирования и анализа дат. Это позволяет форматировать даты в текст, анализировать текст в даты и нормализовать.

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

Использование simpleDateFormat относительно просто:

public static void main(String[] args) throws Exception {

 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd  HH:mm:ss");

 System.out.println(simpleDateFormat.format(new Date()));

 System.out.println(simpleDateFormat.parse("2018-07-09  11:10:21"));
 }

1. Сначала нужно определить шаблон даты, где мы определяем «ггнобуку-мм-дд HH: MM: SS», то есть наша SimpleDateFormat независимо от формата или анализа, необходимо будет следовать этой модели.

2. Для объекта, формат которого должен передавать дату, будет возвращен тип String, и эта строка будет сгенерирована в соответствии с указанным выше форматом.

3. Для синтаксического анализа необходимо передать строку по приведенному выше шаблону, если будет передан неправильный шаблон, будет выброшено исключение java.text.ParseException, а если будет передано правильное, будет сгенерирован объект Date.

附:格式占位符

 G 年代标志符

 y 年

 M 月

 d 日

 h 时 在上午或下午 (1~12)

 H 时 在一天中 (0~23)

 m 分

 s 秒

 S 毫秒

 E 星期

 D 一年中的第几天

 F 一月中第几个星期几

 w 一年中第几个星期

 W 一月中第几个星期

 a 上午 / 下午 标记符

 k 时 在一天中 (1~24)

 K 时 在上午或下午 (0~11)

 z 时区

2. Скрытые опасности SimpleDateFormat

Многие новички или некоторые неопытные инженеры-разработчики Java будут иметь некоторые странные ошибки при использовании SimpleDateFormat.

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

2. Утечка памяти: в результате неправильного значения преобразования некоторые последующие действия, такие как цикл, кумулятивный процесс в какой-то день, но результирующая дата, если аномалии приводят к очень большим, превратит это в цикл, аналогичный к тому же приводит к утечкам системной памяти бесконечного цикла, часто вызывающим GC, что делает систему непригодной для использования.

Почему так много проблем? Поскольку SimpleDateFormat не является потокобезопасным, многие люди пишут класс Util, а затем определяют SimpleDateFormat как глобальную константу, которая используется всеми потоками:

protected static final SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");

public static Date formatDate(String date) throws ParseException {

 return dayFormat.parse(date);

}

Почему поток SimpleDateFormat небезопасен? В исходном коде SimpleDateFormat все форматирование и синтаксический анализ необходимо преобразовывать через промежуточный объект, которым является Calendar, и это также является виновником небезопасности потока. Представьте, когда у нас их много Когда два потока работают одинаково Calendar, более поздний поток перезапишет данные первого потока, и, в конце концов, фактически будут возвращены данные более позднего потока, что приводит к генерации ОШИБКИ, о которой мы упоминали выше:

/

/ Called from Format after creating a FieldDelegate

 private StringBuffer format(Date date, StringBuffer toAppendTo,

 FieldDelegate delegate) {

 // Convert input date to time field list

 calendar.setTime(date);

​

 boolean useDateFormatSymbols = useDateFormatSymbols();

​

 for (int i = 0; i < compiledPattern.length; ) {

 int tag = compiledPattern\[i\] >>> 8;

 int count = compiledPattern\[i++\] & 0xff;

 if (count == 255) {

 count = compiledPattern\[i++\] << 16;

 count |= compiledPattern\[i++\];

 }

​

 switch (tag) {

 case TAG\_QUOTE\_ASCII_CHAR:

 toAppendTo.append((char)count);

 break;

​

 case TAG\_QUOTE\_CHARS:

 toAppendTo.append(compiledPattern, i, count);

 i += count;

 break;

​

 default:

 subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);

 break;

 }

 }

 return toAppendTo;

 }

3. Как избежать ям

Есть несколько решений для SimpleDateFormat:

3.1 Новый простой формат даты

Причина вышеуказанной ошибки в том, что все потоки используют формат SimpleDateFormat. Вот лучшее решение. Создавайте новый формат SimpleDateFormat каждый раз, когда вы его используете. Мы можем создать SimpleDateFormat внутри метода в DateUtils:

public static Date formatDate(String date) throws ParseException {

 SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd");

 return dayFormat.parse(date);

}

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

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

Использование ThreadLocal может избежать частого появления Young gc выше. Мы используем ThreadLocal для сохранения каждого потока. Поскольку ThreadLocal изолирован между потоками, не будет проблем с безопасностью потоков:

private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>();

 public static Date formatDate(String date) throws ParseException {

 SimpleDateFormat dayFormat = getSimpleDateFormat();

 return dayFormat.parse(date);

 }

​

 private static SimpleDateFormat getSimpleDateFormat() {

 SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();

 if (simpleDateFormat == null){

 simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd  HH:mm:ss")

 simpleDateFormatThreadLocal.set(simpleDateFormat);

 }

 return simpleDateFormat;

 }

3.3 Использование сторонних инструментов

Хотя вышеприведенный ThreadLocal может решить наши проблемы, функции, предоставляемые сторонними инструментами, более мощные.В java есть две хорошо известные библиотеки классов, одна - Joda-Time, а другая - общий пакет Apache.

3.3.1 Joda-Time (рекомендуется)

Joda-Time позволяет легко управлять значениями времени и даты, манипулировать ими и понимать их. Для наших сложных операций мы можем использовать операции Joda-Time. Ниже я приведу два примера. Чтобы добавить 90 дней к дате, если мы используем собственный Jdk, нам нужно написать:

Calendar calendar = Calendar.getInstance();

calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);

SimpleDateFormat sdf =

 new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");

calendar.add(Calendar.DAY\_OF\_MONTH, 90);

System.out.println(sdf.format(calendar.getTime()));

Но в нашем джода-тайме нужно всего два слова, а апи относительно легко понять, так почему бы вам не сделать джода-тайм?

DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);

System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");

3.3.2 пакет общего языка

В пакете common-lang есть класс FastDateFormat. Поскольку на пакет common-lang в основном ссылаются многие проекты Java, вы можете обрабатывать время, не обращаясь к пакету времени обработки. создан, использование относительно просто.Код выглядит следующим образом:

FastDateFormat.getInstance().format(new Date());

3.4 Обновление jdk8 (рекомендуется)

В java8 многие методы в классе Date, включая конструктор, были аннотированы устаревшими аннотациями @Deprecated, замененными тремя классами LocalDateTime, LocalDate LocalTime:

  • LocalDate не может содержать время;

  • LocalTime не может содержать даты;

  • LocalDateTime может содержать как дату, так и время.

Если вы используете Java8, вы должны использовать его. Вам не нужно учитывать безопасность потоков при форматировании и анализе даты. Код выглядит следующим образом:

public static String formatTime(LocalDateTime time,String pattern) {

 return time.format(DateTimeFormatter.ofPattern(pattern));

 }

Конечно, localDateTime — это изюминка java 8. Конечно, он не только решает проблему безопасности потоков, но и предоставляет некоторые другие операции, такие как добавление и вычитание дней:

//日期加上一个数,根据field不同加不同值,field为ChronoUnit.*

 public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {

 return time.plus(number, field);

 }

​

 //日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*

 public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field){

 return time.minus(number,field);

 }

Наконец, если вы обеспокоены тем, что использование LocalDateTime радикально изменит ваш существующий код, вы можете преобразовать два:

//Date转换为LocalDateTime

 public static LocalDateTime convertDateToLDT(Date date) {

 return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

 }

​

 //LocalDateTime转换为Date

 public static Date convertLDTToDate(LocalDateTime time) {

 return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());

 }

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

Наконец, эта статья была включена в JGrowing, всеобъемлющий и отличный маршрут изучения Java, совместно созданный сообществом.Если вы хотите участвовать в обслуживании проектов с открытым исходным кодом, вы можете создать его вместе.Адрес github:GitHub.com/Java растет…Пожалуйста, дайте мне маленькую звезду.

Если вам понравилась эта статья, пожалуйста, поставьте лайк и перешлите ее. Вы можете отсканировать официальную учетную запись, чтобы получить мой VIP-сервис 1 на 1.