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)));
    }
}
相关推荐
yaoxin5211231 小时前
384. Java IO API - Java 文件复制工具:Copy 示例完整解析
java·开发语言·python
NotFound4861 小时前
实战指南如何实现Java Web 拦截机制:Filter 与 Interceptor 深度分享
java·开发语言·前端
Ava的硅谷新视界2 小时前
用了一天 Claude Opus 4.7,聊几点真实感受
开发语言·后端·编程
rabbit_pro2 小时前
Python调用onnx模型
开发语言·python
一 乐3 小时前
医院挂号|基于springboot + vue医院挂号管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·医院挂号管理系统
浪客川3 小时前
【百例RUST - 010】字符串
开发语言·后端·rust
鱼鳞_3 小时前
Java学习笔记_Day29(异常)
java·笔记·学习
烟锁池塘柳03 小时前
一文讲透 C++ / Java 中方法重载(Overload)与方法重写(Override)在调用时机等方面的区别
java·c++·面向对象
一叶飘零_sweeeet3 小时前
深入拆解 Fork/Join 框架:核心原理、分治模型与参数调优实战
java·并发编程
云烟成雨TD3 小时前
Spring AI Alibaba 1.x 系列【23】短期记忆
java·人工智能·spring