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.