Java中日期格式化的实现算法

时间:2021-11-05 18:08:43
package com.study.test;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 实现Java中日期的简单格式化,支持以下字段:
 * yyyy:年
 * MM:月
 * dd:日
 * hh:1~12小时制(1-12)
 * HH:24小时制(0-23)
 * mm:分
 * ss:秒
 * S:毫秒
 * E:星期几
 * a: 上午/下午
 */
public class DateFormatter implements Serializable {

    private static Map<Character, Integer> tokenFieldMap = new HashMap<>();
    private static final int NO_LENGTH = 1000;            //不用自动补0的属性,例如毫秒
    private static final int LOCALE = NO_LENGTH + 1000;  //用Locale获取值的属性,例如星期

    //tokenFieldMap,将字符与Calendar中的field对应
    static {
        tokenFieldMap.put('y', Calendar.YEAR);
        tokenFieldMap.put('M', Calendar.MONTH);
        tokenFieldMap.put('d', Calendar.DATE);
        tokenFieldMap.put('h', Calendar.HOUR);
        tokenFieldMap.put('H', Calendar.HOUR_OF_DAY);
        tokenFieldMap.put('m', Calendar.MINUTE);
        tokenFieldMap.put('s', Calendar.SECOND);
        tokenFieldMap.put('a', Calendar.AM_PM + LOCALE);
        tokenFieldMap.put('S',Calendar.MILLISECOND + NO_LENGTH);
        tokenFieldMap.put('E',Calendar.DAY_OF_WEEK + LOCALE);
    }

    private static class Token implements Serializable {
        int field;   // Calendar中的field对应
        int length; // 自动补0的长度

        public Token(int field, int length) {
            this.field = field;
            this.length = length;
        }
    }

    //解析出的token,可能是token或者是String
    private List<Object> tokens;

    private String format;

    public DateFormatter(String format) {
        this.format = format;
        parseTokens();
    }

    public String getFormat() {
        return format;
    }

    public String format(Date date){
        if(date == null){
            throw new IllegalArgumentException("null argument!");
        }
        StringBuilder sb = new StringBuilder();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        for(Object token : tokens){
            if(token instanceof Token){
                token2String(sb,(Token)token,calendar);
            }else{
                sb.append(token);
            }
        }
        return sb.toString();
    }

    private void parseTokens() {
        tokens = new ArrayList<>();
        StringBuilder temp = new StringBuilder();
        for (int i = 0; i < format.length(); i++) {
            char c = format.charAt(i);
            Integer field = tokenFieldMap.get(c);
            if (field != null) {
                checkStr(temp);
                if(field < NO_LENGTH){
                    int num = 1;
                    while (i < format.length() - 1 && format.charAt(i + 1) == c) {
                        i++;
                        num++;
                    }
                    tokens.add(new Token(field, num));
                }else{
                    tokens.add(new Token(field, 0));
                }
            }else{
                temp.append(c);
            }
        }
        checkStr(temp);
    }

    private void checkStr(StringBuilder temp) {
        if (temp.length() > 0) {
            tokens.add(temp.toString());
            temp.setLength(0);
        }
    }

    @SuppressWarnings("MagicConstant")
    private void token2String(StringBuilder sb, Token token, Calendar calendar){
        int field = token.field;
        if(field > LOCALE){
            sb.append(calendar.getDisplayName(field  - LOCALE,Calendar.SHORT,Locale.getDefault()));
        }else if(field > NO_LENGTH){
            sb.append(calendar.get(field - NO_LENGTH));
        }else{
            int val = calendar.get(field);
            if(field == Calendar.MONTH){
                val++;  //如果是月,取出的范围是0-11,需要加一
            }
            String value = String.valueOf(val);
            if(value.length() > token.length){
                if(token.field == Calendar.YEAR){       //如果是年,才截取,比如2018可以截取成18年,小时分钟不能截取
                    sb.append(value.substring(value.length() - token.length));
                }else{
                    sb.append(value);
                }
            }else{
                for(int i=0;i<token.length - value.length();i++){   //根据length自动补0
                    sb.append('0');
                }
                sb.append(value);
            }
        }
    }



    public static void main(String[] args)throws Exception {
        String fmt = "yy-M-dd a h:m:s E";
        Date date = new Date();

        DateFormatter formatter = new DateFormatter(fmt);
        String result = formatter.format(date);
        System.out.println(result);


        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(fmt);
        System.out.println(simpleDateFormat.format(date));

        //性能测试
        long t1 = System.currentTimeMillis();
        for(int i=0;i<30000;i++){
            simpleDateFormat.format(new Date(System.currentTimeMillis() + i * 3000 ));
        }
        long t2 = System.currentTimeMillis();
        for(int i=0;i<30000;i++){
            formatter.format(new Date(System.currentTimeMillis() + i * 3000));
        }
        long t3 = System.currentTimeMillis();

        System.out.println("formatter cost : " + (t3 - t2));
        System.out.println("java format cost : " + (t2 - t1));

    }
}