java工具类,字符串转时间

java工具类,字符串转时间

java 复制代码
package com.tomato.test2;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TimeUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(TimeUtil.class);
    private static final Map<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>();

    // 常用格式常量
    public static final String DEFAULT_YMDHMS_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DMYHMS_FORMAT = "dd/MM/yyyy HH:mm:ss";
    public static final String DEFAULT_YMD_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd";
    public static final String SOLR_TDATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
    public static final String SOLR_TDATE_RETURN_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public static final String ORACLE_TDATE_RETURN_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String JS_TDATE_RETURN_FORMAT = "yyyy-MM-dd HH:mm:ss.S";
    public static final String COMPACT_YMDHMS_FORMAT = "yyyyMMddHHmmss";
    public static final String COMPACT_YMD_FORMAT = "yyyyMMdd";
    public static final String COMPACT_YMDH_FORMAT = "yyyyMMddHH";
    public static final String US_TIME_FORMAT = "EEE MMM d HH:mm:ss z yyyy";
    public static final String TDATE_RETURN_FORMAT = "yyyy-MM-dd HH:mm:ss.nnnnnnnnn";
    public static final String COMPACT_CHINESE_FORMAT = "yyyy年MM月dd日";
    public static final String YMDHS_FORMAT = "yyyy/MM/dd HH:mm";
    public static final String YMDHS_FORMAT_WITHOUT_SPLIT_TAG = "yyyyMMddHHmm";


    private static final Pattern DATE_ONLY_PATTERN = Pattern.compile("(\\d{4})-(\\d{1,2})-(\\d{1,2})");
    private static final Pattern DATE_TIME_PATTERN = Pattern.compile(
            "(\\d{4}-\\d{2}-\\d{2})(?:[T ](\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2}))?)?)?"
    );

    static {
        Arrays.asList(
                DEFAULT_YMDHMS_FORMAT,
                DMYHMS_FORMAT,
                DEFAULT_YMD_FORMAT,
                DEFAULT_DATE_FORMAT,
                SOLR_TDATE_FORMAT,
                SOLR_TDATE_RETURN_FORMAT,
                ORACLE_TDATE_RETURN_FORMAT,
                JS_TDATE_RETURN_FORMAT,
                COMPACT_YMDHMS_FORMAT,
                COMPACT_YMD_FORMAT,
                COMPACT_YMDH_FORMAT,
                COMPACT_CHINESE_FORMAT,
                YMDHS_FORMAT,
                YMDHS_FORMAT_WITHOUT_SPLIT_TAG,
                "yyyy-MM-dd HH:mm:ss.S",
                "yyyy-MM-dd HH:mm:ss.SSS",
                "yyyy-MM-dd HH:mm:ss.nnnnnnnnn",
                "dd/MM/yyyy HH:mm:ss"
        ).forEach(pattern -> FORMATTER_CACHE.put(pattern, DateTimeFormatter.ofPattern(pattern)));

        FORMATTER_CACHE.put(US_TIME_FORMAT, DateTimeFormatter.ofPattern(US_TIME_FORMAT, Locale.US));
    }

    private static DateTimeFormatter getFormatter(String pattern) {
        if (StringUtils.isBlank(pattern)) {
            throw new IllegalArgumentException("DateTime pattern must not be blank");
        }
        return FORMATTER_CACHE.computeIfAbsent(pattern, p -> {
            try {
                return DateTimeFormatter.ofPattern(p);
            } catch (Exception e) {
                LOGGER.error("Failed to create DateTimeFormatter for pattern: {}", p, e);
                throw new IllegalArgumentException("Invalid date pattern: " + p, e);
            }
        });
    }

    public static LocalDateTime parseTime(String timeStr, String pattern) {
        if (StringUtils.isBlank(timeStr) || "null".equalsIgnoreCase(timeStr.trim())) {
            return null;
        }
        timeStr = timeStr.trim();
        timeStr = preprocessTimeStr(timeStr, pattern);

        if (StringUtils.isNotBlank(pattern)) {
            try {
                DateTimeFormatter formatter = getFormatter(pattern);
                String input = timeStr;

                if (isDateOnlyPattern(pattern)) {
                    // 处理纯日期模式:截取日期部分并解析为 LocalDate → LocalDateTime
                    if (input.length() > 10 && input.matches("\\d{4}-\\d{2}-\\d{2}.*")) {
                        input = input.substring(0, 10);
                    }
                    java.time.LocalDate date = java.time.LocalDate.parse(input, formatter);
                    return date.atStartOfDay();
                } else {
                    // 含时间的模式:直接解析 LocalDateTime
                    return LocalDateTime.parse(input, formatter);
                }
            } catch (Exception e) {
                LOGGER.debug("指定格式解析失败:{},pattern:{}", timeStr, pattern, e);
            }
        }

        if (timeStr.contains("长期")) {
            return LocalDateTime.of(2999, 12, 31, 0, 0, 0);
        }

        return parseByAutoDetect(timeStr);
    }

    private static LocalDateTime parseByAutoDetect(String timeStr) {
        int len = timeStr.length();

        if (len == 10 && StringUtils.isNumeric(timeStr)) {
            try {
                return Instant.ofEpochSecond(Long.parseLong(timeStr))
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();
            } catch (Exception ignored) {
            }
        }

        if (len == 8) return tryParse(timeStr, COMPACT_YMD_FORMAT);
        if (len == 12) return tryParse(timeStr, YMDHS_FORMAT_WITHOUT_SPLIT_TAG);
        if (len == 14) return tryParse(timeStr, COMPACT_YMDHMS_FORMAT);

        if (len == 10) {
            if (timeStr.contains("-")) return tryParse(timeStr, DEFAULT_YMD_FORMAT);
            if (timeStr.contains("/")) return tryParse(timeStr, DEFAULT_DATE_FORMAT);
        }

        if (len >= 16 && len <= 19) {
            return tryParse(timeStr, DEFAULT_YMDHMS_FORMAT);
        }

        if (len == 21) return tryParse(timeStr, JS_TDATE_RETURN_FORMAT);
        if (len == 23) return tryParse(timeStr, ORACLE_TDATE_RETURN_FORMAT);
        if (len == 20) return tryParse(timeStr, SOLR_TDATE_RETURN_FORMAT);

        // 🔥 重点修复:SOLR UTC 格式必须用 Instant.parse(JDK 8 支持)
        if (len == 24 && timeStr.endsWith("Z")) {
            try {
                Instant instant = Instant.parse(timeStr);
                return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
            } catch (Exception ignored) {
            }
        }

        if (timeStr.contains("年") && timeStr.contains("月") && timeStr.contains("日")) {
            return tryParse(timeStr, COMPACT_CHINESE_FORMAT);
        }

        if (timeStr.matches("^[A-Za-z]{3} [A-Za-z]{3} \\d{1,2} .* \\d{4}$")) {
            try {
                ZonedDateTime zdt = ZonedDateTime.parse(timeStr, getFormatter(US_TIME_FORMAT));
                return zdt.toLocalDateTime();
            } catch (Exception ignored) {
            }
        }

        LOGGER.warn("无法自动解析时间字符串: '{}'", timeStr);
        return null;
    }

    private static LocalDateTime tryParse(String text, String pattern) {
        try {
            return LocalDateTime.parse(text, getFormatter(pattern));
        } catch (DateTimeParseException e) {
            return null;
        }
    }

    public static String format(LocalDateTime dateTime, String pattern) {
        if (dateTime == null || StringUtils.isBlank(pattern)) return "";
        return dateTime.format(getFormatter(pattern));
    }

    private static boolean isDateOnlyPattern(String pattern) {
        if (pattern == null) return false;
        String p = pattern;
        return !p.contains("H") && !p.contains("h") &&
                !p.contains("m") && !p.contains("s") &&
                !p.contains("S") && !p.contains("n");
    }

    private static String padToTwoDigits(String part) {
        if (part == null) return "00";
        try {
            int num = Integer.parseInt(part);
            if (num < 0 || num > 59) return "00";
            return String.format("%02d", num);
        } catch (NumberFormatException e) {
            LOGGER.debug("非法时间数字部分: '{}'", part);
            return "00";
        }
    }

    private static String preprocessTimeStr(String timeStr, String pattern) {
        if (StringUtils.isBlank(timeStr)) {
            return timeStr;
        }

        String standardized = timeStr.replaceAll("[./]", "-");

        // 显式声明 Matcher 类型(JDK 8 不支持 var)
        Matcher dateMatcher = DATE_ONLY_PATTERN.matcher(standardized);
        if (dateMatcher.find()) {
            String year = dateMatcher.group(1);
            String month = padToTwoDigits(dateMatcher.group(2));
            String day = padToTwoDigits(dateMatcher.group(3));
            standardized = dateMatcher.replaceFirst(year + "-" + month + "-" + day);
        }

        if (StringUtils.isNotBlank(pattern)) {
            boolean needsHour = pattern.contains("H") || pattern.contains("k") || pattern.contains("K") || pattern.contains("h");
            boolean needsMinute = pattern.contains("m");
            boolean needsSecond = pattern.contains("s");

            if (needsHour || needsMinute || needsSecond) {
                // 显式声明 Matcher
                Matcher dtMatcher = DATE_TIME_PATTERN.matcher(standardized);
                if (dtMatcher.matches()) {
                    String datePart = dtMatcher.group(1);
                    String hour = dtMatcher.group(2);
                    String minute = dtMatcher.group(3);
                    String second = dtMatcher.group(4);

                    StringBuilder result = new StringBuilder(datePart);

                    if (needsHour) {
                        result.append(" ").append(padToTwoDigits(hour));
                    } else {
                        return datePart;
                    }

                    if (needsMinute) {
                        result.append(":").append(padToTwoDigits(minute));
                    }

                    if (needsSecond) {
                        result.append(":").append(padToTwoDigits(second));
                    }

                    standardized = result.toString();
                }
            }
        }

        return standardized;
    }

    public static void main(String[] args) {

//        年月日,没有时分秒
//        String pattern = "yyyy-MM-dd";
//        LocalDateTime localDateTime = TimeUtil.parseTime("2012-05-05 12:34:56", pattern);

//        补全时分秒
//        String pattern = "yyyy-MM-dd HH:mm:ss";
//        LocalDateTime localDateTime = TimeUtil.parseTime("2012-01-05 12:36", pattern);

//        补全一位日期
//        String pattern = "yyyy-MM-dd HH:mm:ss";
//        LocalDateTime localDateTime = TimeUtil.parseTime("2012-1-05 12:36", pattern);

//        无法转换,localDateTime返回为null
        String pattern = "yyyy-MM-dd HH:mm:ss";
        LocalDateTime localDateTime = TimeUtil.parseTime("abc", pattern);


        if (localDateTime == null) {
            System.out.println("无法转换");
            return;
        }

        System.out.println(localDateTime.format(DateTimeFormatter.ofPattern(pattern)));
    }
}
相关推荐
l1t1 小时前
DeepSeek 4.1总结的Tom Lane 谈塑造 Postgres 三十年历程的架构决策
开发语言·数据库·postgresql·架构
欢迎来到祖安!2 小时前
企业微信 API 接口能力介绍:支持消息收发、图片发送、表情互动、联系人管理等多场景应用
java·前端·数据仓库·spring cloud·企业微信
dadaobusi5 小时前
学习:RV lkvm SBI
java·学习·spring
djarmy7 小时前
MAIN.c(1): warning C318: can’t open file ‘STC8G.H’ 报错 解决 分析
c语言·开发语言·mongodb
HEJOO97 小时前
深入理解 Java volatile 关键字:原理、用法与常见误区
java·开发语言·spring
曹牧7 小时前
Java:no content to map due to end of input
java·开发语言
阿维的博客日记7 小时前
Example 类全场景实战指南
java·mybatis
梦梦代码精8 小时前
《回收租赁系统技术选型避坑指南:业务闭环与二开自由度详解》
开发语言·低代码·docker·开源·代码规范
两点王爷8 小时前
SpringBoot 项目集成 GeoServer 源码,实现地图服务发布
java·spring boot·后端
隔窗听雨眠8 小时前
记一次SQL Server数据库性能分析:从CPU100%到单配置修复的完整诊断
开发语言·数据库·php