package com.hzya.frame.dateutil;
import cn.hutool.core.util.StrUtil;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Content
 * @Author 👻👻👻👻👻👻👻👻👻👻 gjh
 * @Date 2022-11-28 10:27
 * @Version 1.0
 */
public class DateUtil {
    public static final int FIRST_DAY_OF_WEEK = Calendar.MONDAY; // 中国周一是一周的第一天

    /**
     * 设置时间格式
     */
    private static final String COMMON_DATE = "yyyy-MM-dd";
    /**
     * 设置东八区
     **/
    private static final String UTC8 = "GMT+08:00";

    /**
     * 锁对象
     */
    private static final Object LOCKOBJ = new Object();

    /**
     * 存放不同的日期模板格式的sdf的Map
     */
    private static Map<String, ThreadLocal<SimpleDateFormat>> simpleDateFormatThreadLocalHashMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();

    /**
     * 存放不同的 时区的Calendar Map
     */
    private static Map<String, ThreadLocal<Calendar>> calendarThreadLocalMap = new HashMap<String, ThreadLocal<Calendar>>();


    /***
     * 根据模版获取 SimpleDateFormat
     * @param pattern
     * @return
     */
    private static SimpleDateFormat getSimpleDateFormat(final String pattern) {

        ThreadLocal<SimpleDateFormat> tl = simpleDateFormatThreadLocalHashMap.get(pattern);
        // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
        if (tl == null) {
            synchronized (LOCKOBJ) {
                tl = simpleDateFormatThreadLocalHashMap.get(pattern);
                if (tl == null) {
                    // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                    // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                    tl = new ThreadLocal<SimpleDateFormat>() {

                        @Override
                        protected SimpleDateFormat initialValue() {

                            return new SimpleDateFormat(pattern);
                        }
                    };
                    simpleDateFormatThreadLocalHashMap.put(pattern, tl);
                }
            }
        }
        return tl.get();
    }

    /***
     * 根据模版获取  Calendar
     * @param pattern
     * @return
     */
    private static Calendar getCalendar(final String pattern) {
        ThreadLocal<Calendar> tl = calendarThreadLocalMap.get(pattern);
        // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
        if (tl == null) {
            synchronized (LOCKOBJ) {
                tl = calendarThreadLocalMap.get(pattern);
                if (tl == null) {
                    // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                    //System.out.println("put new sdf of pattern " + pattern + " to map");
                    // 这里是关键,使用ThreadLocal<Calendar>替代原来直接new Calendar
                    tl = new ThreadLocal<Calendar>() {
                        @Override
                        protected Calendar initialValue() {
                            //System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
                            // new GregorianCalendar(pattern);
                            return Calendar.getInstance( TimeZone.getTimeZone(pattern)) ;
                        }
                    };
                    calendarThreadLocalMap.put(pattern, tl);
                }
            }
        }
        return tl.get();


    }


    /**
     * 获得某一月的天数集合
     *
     * @param year
     * @param month
     * @return
     * @throws ParseException
     */
    public static List<Date> getDateAry(int year, int month) throws ParseException {
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(getSimpleDateFormat(COMMON_DATE).parse("" + year + "-" + month + "-01"));
        int maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        List<Date> dateList = new ArrayList<Date>();
        for (int i = 0; i < maxDays; i++) {
            dateList.add(calendar.getTime());
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        return dateList;
    }

    /**
     * 日期转字符
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String dateToString(Date date, String pattern) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.format(date);
    }
    /**
     * 日期转字符
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String dateToString(Long date, String pattern) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.format(date);
    }

    /**
     * 字符转日期
     *
     * @param date
     * @param pattern
     * @return
     * @throws ParseException
     */
    public static Date stringToDate(String date, String pattern) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.parse(date);
    }


    /**
     *  传入一个事件，计算加减法，单位年
     * @param date  时间
     * @param num  加减的数量
     * @param type 增加，还是减少，请传入值：add    subtract
     * @return
     * @throws ParseException
     */
    public static Date getYearAddAndSubtract(Date date, BigDecimal num , String type) throws Exception {

        if(type == null ){
            throw new RuntimeException("请传入运算类型");
        }
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(date);
        int nu =0;
        if("add".equalsIgnoreCase(type)){
            nu = num.intValue();
        }else{
            nu = num.negate().intValue();
        }
        calendar.add(Calendar.YEAR, nu);
        return calendar.getTime();
    }



    /**
     *  传入一个事件，计算加减法，单位月
     * @param date  时间
     * @param num  加减的数量
     * @param type 增加，还是减少，请传入值：add    subtract
     * @return
     * @throws ParseException
     */
    public static Date getMonthAddAndSubtract(Date date, BigDecimal num , String type) throws Exception {

        if(type == null ){
            throw new RuntimeException("请传入运算类型");
        }
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(date);
        int nu =0;
        if("add".equalsIgnoreCase(type)){
            nu = num.intValue();
        }else{
            nu = num.negate().intValue();
        }
        calendar.add(Calendar.MONTH, nu);
        return calendar.getTime();
    }


    /**
     *  传入一个事件，计算加减法，单位根据 calendarType传入
     * @param date  时间
     * @param num  加减的数量
     * @param type 增加，还是减少，请传入值：add    subtract
     * @param calendarType YEAR 1 MONTH 2 DATE 5
     * @return
     * @throws ParseException
     */
    public static Date getDateAddAndSubtract(Date date, BigDecimal num , String type,Integer calendarType) throws Exception {

        if(type == null ){
            throw new RuntimeException("请传入运算类型");
        }
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(date);
        int nu =0;
        if("add".equalsIgnoreCase(type)){
            nu = num.intValue();
        }else{
            nu = num.negate().intValue();
        }
        calendar.add(calendarType, nu);
        return calendar.getTime();
    }

    /**
     * 传入年月 获取这个月的第一天
     *
     * @param year
     * @param month
     * @return
     */
    public static String getStartTime(Integer year, Integer month) {
        Calendar calendar = getCalendar(UTC8);
        //设置年份
        calendar.set(Calendar.YEAR, year);
        //设置月份
        calendar.set(Calendar.MONTH, month - 1);
        //设置日历中月份的第1天
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        //格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String firstDayOfMonth = sdf.format(calendar.getTime());
        return firstDayOfMonth;
    }

    /**
     * 传入年月 获取这个月的最后 一天
     *
     * @param year
     * @param month
     * @return
     */
    public static String getEndTime(Integer year, Integer month) {
        Calendar calendar = getCalendar(UTC8);
        //设置年份
        calendar.set(Calendar.YEAR, year);
        //设置月份
        calendar.set(Calendar.MONTH, month - 1);
        int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, lastDay);
        //格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String firstDayOfMonth = sdf.format(calendar.getTime());
        return firstDayOfMonth;
    }

    /**
     * 传入时间所在月份得到第一天
     *
     * @param tempDate
     * @return
     */
    public static Date getStartTime(Date tempDate) {
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(tempDate);
        //设置日历中月份的第1天
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 传入时间所在月份的最后 一天
     *
     * @param tempDate
     * @return
     */
    public static Date getEndTime(Date tempDate) {
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(tempDate);
        int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, lastDay);
        return calendar.getTime();
    }

    /**
     * 传入date 获取这个时间年的第一个月的第一天
     *
     * @param tempDate
     * @return
     */
    public static Date getStartMonth(Date tempDate) {
        Calendar cal = getCalendar(UTC8);
        cal.setTime(tempDate);
        //设置日历中月份的第1天
        cal.set(Calendar.DAY_OF_YEAR, 1);
        return cal.getTime();
    }

    /**
     * 传入date 获取这个时间年的最后一个月的最后一天
     *
     * @param tempDate
     * @return
     */
    public static Date getEndMonth(Date tempDate) {
        Calendar cal = getCalendar(UTC8);
        cal.setTime(tempDate);
        int lastDay = cal.getActualMaximum(Calendar.DAY_OF_YEAR);
        cal.set(Calendar.DAY_OF_YEAR, lastDay);
        return cal.getTime();
    }

    public static Date getYearFirstDate(Date tempDate) {
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(tempDate);
        calendar.set(Calendar.DAY_OF_YEAR, 1);
        return calendar.getTime();
    }

    /**
     * 时间类型的运算
     *
     * @param date 要运算的时间
     * @param num  加一天 1  减一天 -1
     * @return
     */
    public static Date getDateoperation(Date date, Integer num) {
        /** 初始化日期格式工具*/
        Calendar calendar = getCalendar(UTC8);
        calendar.setTime(date);
        /** 设置预警时间*/
        calendar.add(Calendar.DATE, num);
        return calendar.getTime();
    }

    /**
     * 取得日期：年
     *
     * @param date
     * @return
     */
    public static int getYear(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int year = c.get(Calendar.YEAR);
        return year;
    }

    /**
     * 取得日期：年
     *
     * @param date
     * @return
     */
    public static String getYearStr(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int year = c.get(Calendar.YEAR);
        return String.valueOf(year);
    }
    /**
     * 取得日期：年
     *
     * @param date
     * @return
     */
    public static int getMonth(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int month = c.get(Calendar.MONTH);
        return month + 1;
    }
    /**
     * 取得日期：年
     *
     * @param date
     * @return
     */
    public static String getMonthStr(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int month = c.get(Calendar.MONTH);
        return String.valueOf(month);
    }

    /**
     * 取得日期：年
     *
     * @param date
     * @return
     */
    public static int getDay(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int da = c.get(Calendar.DAY_OF_MONTH);
        return da;
    }

    /**
     * 取得当天日期是周几
     *
     * @param date
     * @return
     */
    public static int getWeekDay(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int week_of_year = c.get(Calendar.DAY_OF_WEEK);
        return week_of_year - 1;
    }

    /**
     * 取得一年的第几周
     *
     * @param date
     * @return
     */
    public static int getWeekOfYear(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int week_of_year = c.get(Calendar.WEEK_OF_YEAR);
        return week_of_year;
    }


    /**
     * 根据日期取得对应周周一日期
     *
     * @param date
     * @return
     */
    public static Date getMondayOfWeek(Date date) {
        Calendar monday = getCalendar(UTC8);
        monday.setTime(date);
        monday.setFirstDayOfWeek(FIRST_DAY_OF_WEEK);
        monday.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        return monday.getTime();
    }

    /**
     * 根据日期取得对应周周日日期
     *
     * @param date
     * @return
     */
    public static Date getSundayOfWeek(Date date) {
        Calendar sunday = getCalendar(UTC8);
        sunday.setTime(date);
        sunday.setFirstDayOfWeek(FIRST_DAY_OF_WEEK);
        sunday.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        return sunday.getTime();
    }

    /**
     * 取得月的剩余天数
     *
     * @param date
     * @return
     */
    public static int getRemainDayOfMonth(Date date) {
        int dayOfMonth = getDayOfMonth(date);
        int day = getPassDayOfMonth(date);
        return dayOfMonth - day;
    }

    /**
     * 取得月已经过的天数
     *
     * @param date
     * @return
     */
    public static int getPassDayOfMonth(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        return c.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 取得月天数
     *
     * @param date
     * @return
     */
    public static int getDayOfMonth(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        return c.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 取得月第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDateOfMonth(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
        return c.getTime();
    }

    /**
     * 取得月最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDateOfMonth(Date date) {
        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
        return c.getTime();
    }

    /**
     * 取得季度第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDateOfSeason(Date date) {
        return getFirstDateOfMonth(getSeasonDate(date)[0]);
    }

    /**
     * 取得季度最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDateOfSeason(Date date) {
        return getLastDateOfMonth(getSeasonDate(date)[2]);
    }

    /**
     * 取得季度天数
     *
     * @param date
     * @return
     */
    public static int getDayOfSeason(Date date) {
        int day = 0;
        Date[] seasonDates = getSeasonDate(date);
        for (Date date2 : seasonDates) {
            day += getDayOfMonth(date2);
        }
        return day;
    }

    /**
     * 取得季度剩余天数
     *
     * @param date
     * @return
     */
    public static int getRemainDayOfSeason(Date date) {
        return getDayOfSeason(date) - getPassDayOfSeason(date);
    }

    /**
     * 取得季度已过天数
     *
     * @param date
     * @return
     */
    public static int getPassDayOfSeason(Date date) {
        int day = 0;

        Date[] seasonDates = getSeasonDate(date);

        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int month = c.get(Calendar.MONTH);

        if (month == Calendar.JANUARY || month == Calendar.APRIL
                || month == Calendar.JULY || month == Calendar.OCTOBER) {// 季度第一个月
            day = getPassDayOfMonth(seasonDates[0]);
        } else if (month == Calendar.FEBRUARY || month == Calendar.MAY
                || month == Calendar.AUGUST || month == Calendar.NOVEMBER) {// 季度第二个月
            day = getDayOfMonth(seasonDates[0])
                    + getPassDayOfMonth(seasonDates[1]);
        } else if (month == Calendar.MARCH || month == Calendar.JUNE
                || month == Calendar.SEPTEMBER || month == Calendar.DECEMBER) {// 季度第三个月
            day = getDayOfMonth(seasonDates[0]) + getDayOfMonth(seasonDates[1])
                    + getPassDayOfMonth(seasonDates[2]);
        }
        return day;
    }

    /**
     * 取得季度月
     *
     * @param date
     * @return
     */
    public static Date[] getSeasonDate(Date date) {
        Date[] season = new Date[3];

        Calendar c = getCalendar(UTC8);
        c.setTime(date);

        int nSeason = getSeason(date);
        if (nSeason == 1) {// 第一季度
            c.set(Calendar.MONTH, Calendar.JANUARY);
            season[0] = c.getTime();
            c.set(Calendar.MONTH, Calendar.FEBRUARY);
            season[1] = c.getTime();
            c.set(Calendar.MONTH, Calendar.MARCH);
            season[2] = c.getTime();
        } else if (nSeason == 2) {// 第二季度
            c.set(Calendar.MONTH, Calendar.APRIL);
            season[0] = c.getTime();
            c.set(Calendar.MONTH, Calendar.MAY);
            season[1] = c.getTime();
            c.set(Calendar.MONTH, Calendar.JUNE);
            season[2] = c.getTime();
        } else if (nSeason == 3) {// 第三季度
            c.set(Calendar.MONTH, Calendar.JULY);
            season[0] = c.getTime();
            c.set(Calendar.MONTH, Calendar.AUGUST);
            season[1] = c.getTime();
            c.set(Calendar.MONTH, Calendar.SEPTEMBER);
            season[2] = c.getTime();
        } else if (nSeason == 4) {// 第四季度
            c.set(Calendar.MONTH, Calendar.OCTOBER);
            season[0] = c.getTime();
            c.set(Calendar.MONTH, Calendar.NOVEMBER);
            season[1] = c.getTime();
            c.set(Calendar.MONTH, Calendar.DECEMBER);
            season[2] = c.getTime();
        }
        return season;
    }

    //字符串日期转毫秒
    public static Long dateStrToLong(String dateStr){
        if(StrUtil.isNotEmpty(dateStr) && !"null".equals(dateStr)){
            return cn.hutool.core.date.DateUtil.parse(dateStr).getTime();
        }
        return null;
    }
    /**
     * 1 第一季度 2 第二季度 3 第三季度 4 第四季度
     *
     * @param date
     * @return
     */
    public static int getSeason(Date date) {

        int season = 0;

        Calendar c = getCalendar(UTC8);
        c.setTime(date);
        int month = c.get(Calendar.MONTH);
        switch (month) {
            case Calendar.JANUARY:
            case Calendar.FEBRUARY:
            case Calendar.MARCH:
                season = 1;
                break;
            case Calendar.APRIL:
            case Calendar.MAY:
            case Calendar.JUNE:
                season = 2;
                break;
            case Calendar.JULY:
            case Calendar.AUGUST:
            case Calendar.SEPTEMBER:
                season = 3;
                break;
            case Calendar.OCTOBER:
            case Calendar.NOVEMBER:
            case Calendar.DECEMBER:
                season = 4;
                break;
            default:
                break;
        }
        return season;
    }




    public static void main1(String[] args) {
        try {
            /** 初始化日期格式工具*/
            Calendar calendar = getCalendar(UTC8);
            /** 设置保质期时间*/
            calendar.setTime(stringToDate("2015-05-26", "yyyy-MM-dd"));
            /** 设置预警时间*/
            calendar.add(Calendar.DATE, 1);
            /** 得到预警日期*/
            //System.out.println("保质期：2015-05-26");
            //System.out.println("预警日期：" + dateToString(calendar.getTime(), "yyyy-MM-dd"));

            //System.out.println(dateToString(getDateoperation(new Date(), 1), "yyyy-MM-dd"));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
