
package com.ycwl.basic.utils;

import lombok.SneakyThrows;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

/**
 * 日期处理
 */
public class DateUtils {
    /**
     * 时间格式(yyyy-MM-dd)
     */
    public final static String DATE_PATTERN = "yyyy-MM-dd";
    public final static String DATE_PATTERN_NYR = "yyyy年MM月dd日";
    public final static String DATE_NO_PATTERN = "yyyyMMdd";
    public final static String DATE_NO_TIME = "HHmmss";
    public final static String DATE_PATTERN_YYDD = "MM月dd日 HH:mm";
    public final static String YYYY_MM = "yyyyMM";
    public final static String YYYY_MM_DD_HH_MM_SS_SSS = "yyyyMMddHHmmssSSS";
    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public final static String CRON_DATE_TIME_PATTERN = "ss mm HH dd MM ? yyyy";

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     * @param date 日期
     * @return 返回yyyy-MM-dd格式日期
     */
    public static String format(Date date) {
        return format(date, DATE_PATTERN);
    }

    /**
     * 日期格式化 自定义格式
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 返回yyyy-MM-dd格式日期
     */
    public static String format(Date date, String pattern) {
        if (date != null) {
            SimpleDateFormat df = new SimpleDateFormat(pattern);
            return df.format(date);
        }
        return null;
    }


    /**
     * 日期解析 日期格式为：yyyy-MM-dd HH:mm:ss
     * @param dataStr 日期字符串
     * @return 返回yyyy-MM-dd HH:mm:ss格式日期
     */
    public static Date parse(String dataStr) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_PATTERN);
        return sdf.parse(dataStr);
    }


    /**
     * 日期解析 自定义格式
     * @param date    日期字符串
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 返回Date
     */
    public static Date parse(String date, String pattern) {
        try {
            return new SimpleDateFormat(pattern).parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 返回0时0分0秒的date
     *
     * @param date 日期
     * @return
     */
    @SneakyThrows
    public static Date startOfDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        // 将时间部分设置为00:00:00.000
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 这个月的第一天
     * @param date 日期
     * @return
     */
    public static Date startOfMonth(Date date) {
        Date date1 = startOfDay(date);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date1);
        cal.set(Calendar.DAY_OF_MONTH,1);
        return cal.getTime();
    }

    /**
     * 获取某个月有多少天
     *
     * @param yearMonth
     * @return
     */
    public static String getLastDayOfMonth(String yearMonth) {
        int year = Integer.parseInt(yearMonth.split("-")[0]);  //年
        int month = Integer.parseInt(yearMonth.split("-")[1]); //月
        Calendar cal = Calendar.getInstance();
        // 设置年份
        cal.set(Calendar.YEAR, year);
        // 设置月份
        // cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.MONTH, month); //设置当前月的上一个月
        // 获取某月最大天数
        //int lastDay = cal.getActualMaximum(Calendar.DATE);
        int lastDay = cal.getMinimum(Calendar.DATE); //获取月份中的最小值，即第一天
        // 设置日历中月份的最大天数
        //cal.set(Calendar.DAY_OF_MONTH, lastDay);
        cal.set(Calendar.DAY_OF_MONTH, lastDay - 1); //上月的第一天减去1就是当月的最后一天
        // 格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("dd");
        return sdf.format(cal.getTime());
    }

    /**
     * 获得某月最大时间
     * @param date
     * @return
     */
    public static Date getEndMonthOfDay(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime endOfDay = localDateTime.with(TemporalAdjusters.lastDayOfMonth());
        return getEndOfDay(localDateTimeToDate(endOfDay));
    }

    /**
     * 获得某天最大时间
     * @param date
     * @return
     */
    public static Date getStartMonthOfDay(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime endOfDay = localDateTime.with(TemporalAdjusters.firstDayOfMonth());
        return getStartOfDay(localDateTimeToDate(endOfDay));
    }

    public static Date getMonthLastDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 0);
        calendar.add(Calendar.MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 判断当天和传入的时间是否是同一天
     *
     * @param thatDay 另一个日期
     * @return
     */
    public static boolean isSameDay(Date thatDay) {
        return isSameDay(thatDay, new Date());
    }

    /**
     * 判断两个日期是否为同一天
     *
     * @param date1 一个日期
     * @param date2 另一个日期
     * @return
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return false;
        }
        Calendar thisDat = Calendar.getInstance();
        thisDat.setTime(date1);
        Calendar thatDay = Calendar.getInstance();
        thatDay.setTime(date2);
        return (
                thatDay.get(Calendar.YEAR) == thisDat.get(Calendar.YEAR) &&
                        thatDay.get(Calendar.MONTH) == thisDat.get(Calendar.MONTH) &&
                        thatDay.get(Calendar.DAY_OF_MONTH) == thisDat.get(Calendar.DAY_OF_MONTH)
        );
    }

    /**
     * 判断两个日期相差多少天
     *
     * @param endTime
     * @param startTime
     * @return
     */
    public static int dateMinus(Date endTime, Date startTime) {
        return (int) ((endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60 * 24));
    }

    /**
     * 时间戳转字符串
     *
     * @param timeStamp
     * @return
     */
    //传入时间戳即可
    public static String conversionTime(String timeStamp) {
        //yyyy-MM-dd HH:mm:ss 转换的时间格式  可以自定义
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //转换
        String time = sdf.format(new Date(Long.parseLong(timeStamp)));
        return time;
    }

    /**
     * 判断两个日期相差多少秒
     *
     * @param endTime
     * @param startTime
     * @return
     */
    public static int dateSeconds(Date endTime, Date startTime) {
        return (int) ((endTime.getTime() - startTime.getTime()) / (1000));
    }

    /**
     * 获得某天最小时间
     * @param date
     * @return
     */
    public static Date getStartOfDay(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
        return localDateTimeToDate(startOfDay);
    }

    /**
     * 获得某天最大时间
     * @param date
     * @return
     */
    public static Date getEndOfDay(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
        return localDateTimeToDate(endOfDay);
    }

    /**
     * localDateTime 转date
     * @param localDateTime
     * @return
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 将秒转为时分秒格式【01:01:01】
     * @param second 需要转化的秒数
     * @return
     */
    public static String secondConvertHourMinSecond(Long second) {
        String str = "00:00:00";
        if (second == null || second < 0) {
            return str;
        }

        // 得到小时
        long h = second / 3600;
        str = h > 0 ? ((h < 10 ? ("0" + h) : h) + ":") : "00:";

        // 得到分钟
        long m = (second % 3600) / 60;
        str += (m < 10 ? ("0" + m) : m) + ":";

        //得到剩余秒
        long s = second % 60;
        str += (s < 10 ? ("0" + s) : s);
        return str;
    }

    /**
     *
     * @param date
     * @return
     */
    public static String getWeekOfDate(Date date) {
        String[] weekDays = {"7", "1", "2", "3", "4", "5", "6"}; // "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1; // 将星期日转换为7
        return weekDays[w]; // 直接返回对应的星期数
    }

    /**
     * 获取两个日期之间的所有年
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<String> getAllYearsBetweenDates(Date startDate, Date endDate)  {
        List<String> years = new ArrayList<>();
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        int startYear = startCal.get(Calendar.YEAR);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);
        int endYear = endCal.get(Calendar.YEAR);

        for (int year = startYear; year <= endYear; year++) {
            years.add(String.valueOf(year));
        }
        return years;
    }


    /**
     * 获取两个日期之间的所有月份
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<String> getAllMonthsBetweenDates(Date startDate, Date endDate)  {
        List<String> months = new ArrayList<>();
        //具体逻辑
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        startCal.set(Calendar.DAY_OF_MONTH, 1); // 设置为每个月的第一天

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);
        endCal.set(Calendar.DAY_OF_MONTH, 1); // 设置为每个月的第一天

        while (startCal.before(endCal) || startCal.equals(endCal)) {
            int year = startCal.get(Calendar.YEAR);
            int month = startCal.get(Calendar.MONTH) + 1; // Calendar中月份从0开始，所以要加1
            months.add(String.format("%d-%02d", year, month)); // 格式化年份和月份为 yyyy-MM

            startCal.add(Calendar.MONTH, 1); // 增加一个月
        }
        return months;
    }

    /**
     * 获取两个日期间所有的日期
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<String> getAllDaysBetweenDates(Date startDate, Date endDate){
        List<String> days = new ArrayList<>();
        //具体实现
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        while (startCal.before(endCal) || startCal.equals(endCal)) {
            days.add(sdf.format(startCal.getTime()));
            startCal.add(Calendar.DATE, 1);
        }
        return days;
    }
    /**
     * 获取两个时间点之间的所有的整点时间
     * @param startDate
     * @param endDate
     * @return
     */
    public static List<String> getAllTimesBetweenDates(Date startDate, Date endDate,String format){
        // 创建一个字符串列表用来存放整点时间
        List<String> timesList = new ArrayList<>();
        // 使用日历类来处理时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);

        // 格式化时间为 "yyyy-MM-dd HH:mm"
        SimpleDateFormat sdf = new SimpleDateFormat(format);

        // 逐小时遍历
//        while (calendar.getTime().before(endDate) || calendar.getTime().equals(endDate)) {
        while (calendar.getTime().before(endDate)) {
            // 将当前时间格式化为字符串并加入列表
            timesList.add(sdf.format(calendar.getTime()));
            // 增加一个小时
            calendar.add(Calendar.HOUR_OF_DAY, 1);
        }
        return timesList;
    }

    /**
     * 获取24小时制的时间
     * @return
     */
    public static List<String> getAllTimesBetweenDates(){
        // 创建一个字符串数组，容量为24
        String[] times24 = new String[24];
        // 设置格式化器为24小时制
        java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("HH");
        // 获取0点到23点的每一个小时
        for (int i = 0; i < 24; i++) {
            LocalTime time = LocalTime.of(i, 0);
            times24[i] = time.format(formatter);
        }
        return new ArrayList<>(Arrays.asList(times24));
    }

    /**
     * 在指定日期上增加i年,负数为减少i年
     * @param date 指定的日期
     * @param i 增加/减少的年数
     * @return
     */
    public static Date addDateYears(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, i);
        return calendar.getTime();
    }

    /**
     * 在指定日期上增加i月,负数为减少i月
     * @param date
     * @param i
     * @return
     */
    public static Date addDateMonths(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, i);
        return calendar.getTime();
    }

    /**
     * 在指定日期上增加i周,负数为减少i周
     * @param date
     * @param i
     * @return
     */
    public static Date addDateWeeks(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.WEEK_OF_YEAR, i);
        return calendar.getTime();
    }

    /**
     * 在指定日期上增加i天,负数为减少i天
     * @param date
     * @param i
     * @return
     */
    public static Date addDateDays(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_YEAR, i);
        return calendar.getTime();
    }
    /**
     * 在指定日期上增加i分钟,负数为减少i分钟
     * @param date
     * @param i
     * @return
     */
    public static Date addDateMinute(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, i);
        return calendar.getTime();
    }

    public static void main(String[] args) {
        Date date = new Date();
        Date yesterday = DateUtils.addDateMinute(date, 10);
        System.out.println(format(yesterday, "yyyy-MM-dd HH:mm:ss"));
//        Date startDate = DateUtils.getStartOfDay(yesterday);
//        Date endDate = DateUtils.getEndOfDay(yesterday);
//        System.out.println(format(startDate, "yyyy-MM-dd HH:mm:ss"));
//        System.out.println(format(endDate, "yyyy-MM-dd HH:mm:ss"));

    }
}
