Java日期時間API系列42-----一種高效的中文日期格式化和解析方法

xkzhangsanx發表於2021-09-15

  中文日期(2021年09月11日 和 二〇二一年九月十一日 )在生活中經常用到,2021年09月11日很好處理直接使用模板:yyyy年MM月dd日;二〇二一年九月十一日比較不好處理,需要每個數字進行轉換判斷,下面使用陣列和HashMap來提高效率和簡化程式碼。

1.數字轉換列舉類

比較關鍵,將0到31和中文關聯起來,同時生成陣列和HashMap。

package com.xkzhangsan.time.enums;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

import com.xkzhangsan.time.utils.CollectionUtil;
import com.xkzhangsan.time.utils.CommonCache;

/**
 * 中文日期數字列舉
 *
 * @author xkzhangsan
 */
public enum ChineseDateDigitEnum {
    
    ZERO("〇"),
    ONE("一"),
    TWO("二"),
    THREE("三"),
    FOUR("四"),
    FIVE("五"),
    SIX("六"),
    SEVEN("七"),
    EIGHT("八"),
    NINE("九"),
    TEN("十"),
    ELEVEN("十一"),
    TWELVE("十二"),
    THIRTEEN("十三"),
    FOURTEEN("十四"),
    FIFTEEN("十五"),
    SIXTEEN("十六"),
    SEVENTEEN("十七"),
    EIGHTEEN("十八"),
    NINETEEN("十九"),
    TWENTY("二十"),
    TWENTYONE("二十一"),
    TWENTYTWO("二十二"),
    TWENTYTHREE("二十三"),
    TWENTYFOUR("二十四"),
    TWENTYFIVE("二十五"),
    TWENTYSIX("二十六"),
    TWENTYSEVEN("二十七"),
    TWENTYEIGHT("二十八"),
    TWENTYNINE("二十九"),
    THIRTY("三十"),
    THIRTYONE("三十一");
    
    public static final ChineseDateDigitEnum[] ENUMS = ChineseDateDigitEnum.values();
    
    public static final String CHINESE_DATE_DIGIT_MAP = "CHINESE_DATE_DIGIT_MAP";
    
    private String chineseDigit;

    private ChineseDateDigitEnum(String chineseDigit) {
        this.chineseDigit = chineseDigit;
    }

    public String getChineseDigit() {
        return chineseDigit;
    }
    
    @SuppressWarnings("unchecked")
    public static Integer getIndexUseCache(String chineseDigit){
        Map<String, Integer> chineseDateDigitMap = new HashMap<>(32);
        
        //查詢快取
        chineseDateDigitMap = (Map<String, Integer>)CommonCache.get(CHINESE_DATE_DIGIT_MAP);
        
        //快取存在,返回快取
        if(CollectionUtil.isNotEmpty(chineseDateDigitMap)){
            return chineseDateDigitMap.get(chineseDigit);
        }
        
        //快取不存在,先設定快取然後返回
        Supplier<Object> supplier = new Supplier<Object>() {
            @Override
            public Object get() {
                Map<String, Integer> dateDigitMap = new HashMap<>();
                
                for(ChineseDateDigitEnum chineseDateDigitEnum : ENUMS){
                    dateDigitMap.put(chineseDateDigitEnum.getChineseDigit(), chineseDateDigitEnum.ordinal());
                }
                return dateDigitMap;
            }
        };
        return ((Map<String, Integer>)CommonCache.get(CHINESE_DATE_DIGIT_MAP, supplier)).get(chineseDigit);
    }
    

}

 

2.格式化

ChineseDateDigitEnum.ENUMS[localDateTime.getDayOfMonth()] ,通過列舉陣列下標取值,效率非常高。
    /**
     * 中文日期格式化,isUpperCase false:2021年09月11日 true: 二〇二一年九月十一日
     * @param date Date
     * @param isUpperCase 是否大寫,false:2021年09月11日 true: 二〇二一年九月十一日
     * @return String
     */
    public static String formatToChineseDateStr(Date date, boolean isUpperCase){
        return formatToChineseDateStr(DateTimeConverterUtil.toLocalDateTime(date), isUpperCase);
    }
    
    /**
     * 中文日期格式化,isUpperCase false:2021年09月11日 true: 二〇二一年九月十一日
     * @param localDateTime LocalDateTime
     * @param isUpperCase 是否大寫,false:2021年09月11日 true: 二〇二一年九月十一日
     * @return String
     */
    public static String formatToChineseDateStr(LocalDateTime localDateTime, boolean isUpperCase){
        Objects.requireNonNull(localDateTime, "localDateTime");
        if(isUpperCase){
            StringBuilder buf = new StringBuilder();
            //
            String year = String.valueOf(localDateTime.getYear());
            int yearLength = year.length();
            for(int i=0; i<yearLength; i++){
                buf.append(ChineseDateDigitEnum.ENUMS[year.charAt(i)-48].getChineseDigit());
            }
            buf.append("年");
            //
            buf.append(ChineseDateDigitEnum.ENUMS[localDateTime.getMonthValue()].getChineseDigit());
            buf.append("月");
            //
            buf.append(ChineseDateDigitEnum.ENUMS[localDateTime.getDayOfMonth()].getChineseDigit());
            buf.append("日");
            return buf.toString();
        }else{
            return format(localDateTime, YYYY_MM_DD_CN_FMT);
        }
    }

 

3.解析

ChineseDateDigitEnum.getIndexUseCache(dayStrArr[0]) ,通過將數字轉換列舉類轉換為HashMap,並且使用快取將map快取起來,效率非常高。
    /**
     * 中文日期解析  2021年09月11日 或 二〇二一年九月十一日,返回Date
     * @param text 2021年09月11日 或 二〇二一年九月十一日
     * @return Date
     */
    public static Date parseChineseDateStrToDate(String text){
        return DateTimeConverterUtil.toDate(parseChineseDateStrToLocalDateTime(text));
    }
    
    /**
     * 中文日期解析  2021年09月11日 或 二〇二一年九月十一日,返回LocalDateTime
     * @param text 2021年09月11日 或 二〇二一年九月十一日
     * @return LocalDateTime
     */
    public static LocalDateTime parseChineseDateStrToLocalDateTime(String text){
        if(StringUtil.isEmpty(text)){
            throw new NullPointerException("text");
        }
        text = text.trim();
        Pattern pattern = RegexEnum.NormYearFour.getPattern();
        Matcher match = pattern.matcher(text);
        if (match.find()){
            return parseToLocalDateTime(text, YYYY_MM_DD_CN_FMT);
        } else {
            StringBuilder buf = new StringBuilder();
            //
            String[] yearStrArr = text.split("年");
            String yearStr = yearStrArr[0];
            int yearStrLength = yearStr.length();
            for(int i=0; i<yearStrLength; i++){
                buf.append(ChineseDateDigitEnum.getIndexUseCache(String.valueOf(yearStr.charAt(i))));
            }
            int year = Integer.parseInt(buf.toString());
            //
            String[] monthStrArr = yearStrArr[1].split("月");
            int month = ChineseDateDigitEnum.getIndexUseCache(monthStrArr[0]);
            //
            String[] dayStrArr = monthStrArr[1].split("日");
            int day = ChineseDateDigitEnum.getIndexUseCache(dayStrArr[0]);
            return LocalDateTime.of(year, month, day, 0, 0);
        }
    }

 

4.測試

    /**
     * 中文日期格式化測試
     */
    @Test
    public void formatToChineseDateStrTest(){
        Date date = DateTimeCalculatorUtil.getDate(2021, 9, 11);
        Assert.assertEquals("2021年09月11日",DateTimeFormatterUtil.formatToChineseDateStr(date, false));
        Assert.assertEquals("二〇二一年九月十一日",DateTimeFormatterUtil.formatToChineseDateStr(date, true));
        
        LocalDateTime localDateTime = LocalDateTime.of(2021, 9, 11, 0, 0);
        Assert.assertEquals("2021年09月11日",DateTimeFormatterUtil.formatToChineseDateStr(localDateTime, false));
        Assert.assertEquals("二〇二一年九月十一日",DateTimeFormatterUtil.formatToChineseDateStr(localDateTime, true));
    }
    
    /**
     * 中文日期解析測試
     */
    @Test
    public void parseChineseDateStrToDateTest(){
        Date date = DateTimeCalculatorUtil.getDate(2021, 8, 31);
        Assert.assertEquals(date, DateTimeFormatterUtil.parseChineseDateStrToDate("2021年08月31日"));
        Assert.assertEquals(date, DateTimeFormatterUtil.parseChineseDateStrToDate("二〇二一年八月三十一日"));
        
        LocalDateTime localDateTime = LocalDateTime.of(2021, 8, 31, 0, 0);
        Assert.assertEquals(localDateTime, DateTimeFormatterUtil.parseChineseDateStrToLocalDateTime("2021年08月31日"));
        Assert.assertEquals(localDateTime, DateTimeFormatterUtil.parseChineseDateStrToLocalDateTime("二〇二一年八月三十一日"));
    }

 

原始碼地址:https://github.com/xkzhangsan/xk-time

相關文章