你还在用SimpleDateFormat格式化时间么?老板叫你明天不要来了,,,,,,,,,,

你在项目中写如下一行代码来格式化时间会有啥危险?

/** * 定义一个全局的SimpleDateFormat */private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

1.在里巴巴Java开发手册中,有如下明确规定:


2.SimpleDateFormat的线程安全性:


3.SimpleDateFormat为神马会有线程安全性问题?

找到SimpleDateFormat类的format方法一看便知,他使用了一个全局的Calendar 变量导致:
protected Calendar calendar;
由于我们在声明SimpleDateFormat的时候,使用的是static定义的。那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就可以被多个线程访问到

4.如何解决

 4.1使用局部变量(略)

 4.2加同步锁

synchronized (simpleDateFormat) {   ,,,,,,,,,,,,,,,,,,}可以只对simpleDateFormat.format这一行加锁,这样效率更高一些

4.3使用ThreadLocal

/** * 使用ThreadLocal定义一个全局的SimpleDateFormat */private static ThreadLocal simpleDateFormatThreadLocal = new ThreadLocal() {    @Override    protected SimpleDateFormat initialValue() {        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    }};
//用法String dateString = simpleDateFormatThreadLocal.get().format(calendar.getTime());

4.4使用DateTimeFormatter

可以使用DateTimeFormatter代替SimpleDateFormat,这是一个线程安全的格式化工具类。就像官方文档中说的,这个类 simple beautiful strong immutable thread-safe

5.工具类

package xxxxx(自己的项目路径);
import cn.hutool.core.collection.CollectionUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;
import java.time.LocalDate;import java.time.LocalDateTime;import java.time.Period;import java.time.format.DateTimeFormatter;import java.time.temporal.TemporalAdjusters;import java.util.ArrayList;import java.util.List;
/** * @author zlf * @description: * @time: 2021/10/10 16:27 */@Component@Slf4jpublic class DateUtils {
private static final DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter df2 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter df3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); private static final DateTimeFormatter df4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
   /**     * 传入一个开始日期和结束日期(开始日期小于结束日期),返回这之间的日期天数,可以结合xxl-job等定时任务跑批处理开始时间至结束时间段的每一天额数据 * @param startTime * @param endTime * @return */ public List calculationDays(String startTime, String endTime) { List allDays = new ArrayList<>(); DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate startDt = LocalDate.parse(startTime, dateTimeFormatter1); int start_Y = startDt.getYear(); int start_M = startDt.getMonth().getValue(); int start_D = startDt.getDayOfMonth(); log.info("Y=" + start_Y + ",M=" + start_M + ",D=" + start_D);
LocalDate endDt = LocalDate.parse(endTime, dateTimeFormatter1); int start_Y1 = endDt.getYear(); int start_M1 = endDt.getMonth().getValue(); int start_D1 = endDt.getDayOfMonth(); log.info("Y1=" + start_Y1 + ",M1=" + start_M1 + ",D1=" + start_D1);
if (startDt.compareTo(endDt) > 1) { //开始时间大于结束时间返回空! return null; } String endTimeStr = dateTimeFormatter1.format(endDt);
Period period = Period.between(LocalDate.parse(startTime), endDt);
StringBuffer sb = new StringBuffer(); sb.append(period.getYears()).append(",") .append(period.getMonths()).append(",") .append(period.getDays()); log.info("=======时间分量:=======" + sb.toString());
int py = start_Y1 - start_Y; int Y = 12; detailYear(allDays, start_Y, py, Y); log.info("=======allDays--size()=======" + allDays.size()); if (CollectionUtil.isNotEmpty(allDays)) { for (int i = 0; i < allDays.size(); i++) { log.info(allDays.get(i) + "--------allDays------>第" + (i + 1) + "天"); } List okResult = getOkResult(allDays, startTime, endTimeStr); System.out.println("=======okResult--size()=======" + okResult.size()); for (int i = 0; i < okResult.size(); i++) { log.info(okResult.get(i) + "--------okResult-------->第" + (i + 1)); } return okResult; } return null; }
/** * 获取正确的List * * @param startTime * @param allDays * @return */ private static List getOkResult(List allDays, String startTime, String endTime) { List result = new ArrayList<>(); int indexStart = 0; int indexEnd = 0; for (int i = 0; i < allDays.size(); i++) { if (allDays.get(i).equals(startTime)) { indexStart = i; } } for (int i = 0; i < allDays.size(); i++) { if (allDays.get(i).equals(endTime)) { indexEnd = i; } } result = allDays.subList(indexStart, indexEnd + 1); return result; }
/** * 处理整年 * * @param allDays * @param start_Y * @param py * @param y */ private void detailYear(List allDays, int start_Y, int py, int y) { //处理年的天 for (int i = start_Y; i < start_Y + py + 1; i++) { for (int j = 1; j <= y; j++) { String fst = ""; if (j <= 9) { fst = i + "-0" + j + "-01"; } else { fst = i + "-" + j + "-01"; } int diff_day = getDiff_day(fst); for (int k = 1; k <= diff_day + 1; k++) { String d = ""; if (j <= 9) { d = i + "-0" + j; if (k <= 9) { d += "-0" + k; } else if (k > 9) { d += "-" + k; } } else if (j > 9) { d = i + "-" + j; if (k <= 9) { d += "-0" + k; } else if (k > 9) { d += "-" + k; } } allDays.add(d); } } } }
/** * 根据当月第一天计算本月的开始天和结束天 * * @param fst * @return */ private int getDiff_day(String fst) { LocalDate fstLd = LocalDate.parse(fst); //获取月的第一天 LocalDate fstLd_fd = fstLd.with(TemporalAdjusters.firstDayOfMonth()); //获取月的最后一天 LocalDate fstLd_ld = fstLd.with(TemporalAdjusters.lastDayOfMonth()); Period period2 = Period.between(fstLd_fd, fstLd_ld); int diff_day = period2.getDays(); return diff_day; }
/**     * LocalDateTime转String,格式是:yyyy-MM-dd HH:mm:ss * @param localDateTime * @return */ public static String localDateTimeToString(LocalDateTime localDateTime) { String format = df1.format(localDateTime); return format; }
/**     * LocalDateTime转String,格式是:yyyy-MM-dd * @param localDateTime * @return */ public static String localDateTimeToStringToYMD(LocalDateTime localDateTime) { String format = df2.format(localDateTime); return format; } /**     * String转LocalDateTime,格式是:yyyy-MM-dd HH:mm:ss * @param dateTimeStr * @return */ public static LocalDateTime stringToLocalDateTime(String dateTimeStr) { LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df1); return localDateTime; } /**     * String转LocalDateTime,格式是:yyyy-MM-dd * @param dateTimeStr * @return */ public static LocalDateTime stringIncludeTStrToLocalDateTime(String dateTimeStr) { LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df3); return localDateTime; } /** * LocalDateTime转String,格式是:yyyy-MM-dd'T'HH:mm:ss.SSS * @param localDateTime * @return */ public static String localDateTimeToStringT(LocalDateTime localDateTime) { String format = df3.format(localDateTime); return format; } /** * LocalDateTime转String,格式是:yyyy-MM-dd'T'HH:mm:ss * @param localDateTime * @return */ public static String localDateTimeToStringT2(LocalDateTime localDateTime) { String format = df4.format(localDateTime).replace("T", " "); return format; } /**     * data转String     * @param date * @return */ public static String DateToString(Date date) { Instant instant = date.toInstant(); ZoneId zoneId = ZoneId.systemDefault(); ZonedDateTime zonedDateTime = instant.atZone(zoneId); LocalDateTime localDateTime = zonedDateTime.toLocalDateTime(); // 或者直接使用LocalDateTime.ofInstant LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant, zoneId); DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return dateTimeFormatter1.format(localDateTime); }
}
这个DateUtils工具类是我在项目中每次都会遇到JDK8新日期的API转换操作的接口代码整理,日积月累还是挺方便的。


请使用浏览器的分享功能分享到微信等