package com.ruoyi.common.utils.date;


import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;

import cn.hutool.core.date.LocalDateTimeUtil;

/**
 * 时间工具类，用于 {@link LocalDateTime}
 *
 * @author yshop
 */
public class LocalDateTimeUtils {

    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter FORMATTER_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");

    /**
     * 空的 LocalDateTime 对象，主要用于 DB 唯一索引的默认值
     */
    public static LocalDateTime EMPTY = buildTime(1970, 1, 1);

    public static LocalDateTime addTime(Duration duration) {
        return LocalDateTime.now().plus(duration);
    }

    public static boolean beforeNow(LocalDateTime date) {
        return date.isBefore(LocalDateTime.now());
    }

    public static boolean afterNow(LocalDateTime date) {
        return date.isAfter(LocalDateTime.now());
    }

    /**
     * 创建指定时间
     *
     * @param year  年
     * @param mouth 月
     * @param day   日
     * @return 指定时间
     */
    public static LocalDateTime buildTime(int year, int mouth, int day) {
        return LocalDateTime.of(year, mouth, day, 0, 0, 0);
    }

    public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
                                                   int year2, int mouth2, int day2) {
        return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
    }

    /**
     * 判断当前时间是否在该时间范围内
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 是否
     */
    public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
        if (startTime == null || endTime == null) {
            return false;
        }
        return startTime.isBefore(LocalDateTime.now()) && endTime.isAfter(LocalDateTime.now());
    }

    /**
     * 本周开始时间
     *
     * @return
     */
    public static LocalDateTime weekStartTime() {
        LocalDate now = LocalDate.now();
        return LocalDateTime.of(now.minusDays(now.getDayOfWeek().getValue() - 1), LocalTime.MIN);
    }

    /**
     * 本周结束时间
     *
     * @return
     */
    public static LocalDateTime weekEndTime() {
        LocalDate now = LocalDate.now();
        return LocalDateTime.of(now.plusDays(7 - now.getDayOfWeek().getValue()), LocalTime.MAX);
    }

    /**
     * 本月开始时间
     *
     * @return
     */
    public static LocalDateTime monthStartTime() {
        return LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
    }

    /**
     * 本月结束时间
     *
     * @return
     */
    public static LocalDateTime monthEndTime() {
        return LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
    }

    /**
     * Date转换成LocalDateTime(系统默认时区)
     *
     * @param date
     * @return
     */
    public static LocalDateTime convertLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * DateStr 转换成LocalDateTime(不带有时区)
     *
     * @param date
     * @return
     */
    public static LocalDateTime convertLocalDateTime(String date) {
        return LocalDateTime.parse(date, FORMATTER);
    }

    /**
     * 时间戳转换成LocalDateTime(系统默认时区)
     *
     * @param tempStamp
     * @return
     */

    public static LocalDateTime convertLocalDateTime(Long tempStamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(tempStamp), ZoneId.systemDefault());
    }


    /**
     * Date转换成LocalDateTime(带有时区字符串)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(Date date, String zoneId) {
        validateZoneId(zoneId);
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.of(zoneId));
    }

    /**
     * DateStr转换成LocalDateTime(带有时区字符串)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(String date, String zoneId) {
        validateZoneId(zoneId);
        return LocalDateTime.parse(date, FORMATTER.withZone(ZoneId.of(zoneId)));
    }



    /**
     * Date转换成LocalDateTime(带有时区对象)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(Date date, ZoneId zoneId) {
        return LocalDateTime.ofInstant(date.toInstant(), zoneId);
    }

    /**
     * DateStr转换成LocalDateTime(带有时区对象)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(String date, ZoneId zoneId) {
        return LocalDateTime.parse(date, FORMATTER.withZone(zoneId));
    }

    /**
     * 时间戳转换成LocalDateTime(带有时区对象)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(Long tempStamp, ZoneId zoneId) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(tempStamp), zoneId);
    }

    /**
     * 时间戳转换成LocalDateTime(带有时区字符串)
     *
     * @return
     */
    public static LocalDateTime convertLocalDateTimeOfZoneId(Long tempStamp, String zoneId) {
        validateZoneId(zoneId);
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(tempStamp), ZoneId.of(zoneId));
    }

    /**
     * 时间戳转换成ZonedDateTime(带有时区字符串)
     *
     * @return
     */
    public static ZonedDateTime convertZonedDateTimeOfZoneId(Long tempStamp, String zoneId) {
        validateZoneId(zoneId);
        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(tempStamp), ZoneId.of(zoneId));
    }

    /**
     * 时间戳转换成ZonedDateTime(带有时区字符串)
     * @return
     */
    public static ZonedDateTime convertZonedDateTimeOfZoneId(Long tempStamp, ZoneId zoneId) {
        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(tempStamp), zoneId);
    }


    /**
     * 校验时区字符串是否正确
     *
     * @param zoneId
     */
    private static void validateZoneId(String zoneId) {
        if (!ZoneId.SHORT_IDS.containsValue(zoneId)) {
            throw new RuntimeException("时区字符串错误");
        }
    }


    /**
     * 获取系统默认时区的当前时区时间
     *
     * @return
     */
    public static ZonedDateTime getCurrentZonedDateTime() {
        return ZonedDateTime.now(ZoneId.systemDefault());
    }

    /**
     * 获取给定时区的当前时区时间
     *
     * @param zoneId
     * @return
     */
    public static ZonedDateTime getCurrentZonedDateTime(ZoneId zoneId) {
        return ZonedDateTime.now(zoneId);
    }

    /**
     * 获取给定时区的当前时区时间
     *
     * @param zoneId
     * @return
     */
    public static ZonedDateTime getCurrentZonedDateTime(String zoneId) {
        return ZonedDateTime.now(ZoneId.of(zoneId));
    }

    /**
     * 获取UTC时区的当前带偏移时间
     *
     * @return
     */
    public static OffsetDateTime getCurrentUtcOffsetDateTime() {
        return OffsetDateTime.now(ZoneOffset.UTC);
    }

    /**
     * 格式化带偏移时间
     *
     * @param offsetDateTime
     * @return
     */
    public static String formatOffsetDateTime(OffsetDateTime offsetDateTime, DateTimeFormatter formatter) {
        if (offsetDateTime == null) {
            return null;
        }
        if (formatter == null) {
            formatter = FORMATTER;
        }
        return offsetDateTime.format(formatter);
    }

    /**
     * 转换为其他UTC偏移的时间（例如，东八区）  convertOffsetDateTimeOfZoneId(offsetDateTime,ZoneOffset.of("+08:00"))
     * @param offsetDateTime
     * @param zoneOffset
     * @return
     */
    public static OffsetDateTime convertOffsetDateTimeOfZoneOffset(OffsetDateTime offsetDateTime, ZoneOffset zoneOffset) {
        if (offsetDateTime == null) {
            return null;
        }
        return offsetDateTime.withOffsetSameInstant(zoneOffset);
    }

    /**
     * 转换为其他UTC偏移的时间（例如，东八区）  convertOffsetDateTimeOfZoneId(offsetDateTime,"+08:00")
     * @param offsetDateTime
     * @param zoneOffset
     * @return
     */
    public static OffsetDateTime convertOffsetDateTimeOfZoneOffset(OffsetDateTime offsetDateTime, String zoneOffset) {
        if (offsetDateTime == null) {
            return null;
        }
        return offsetDateTime.withOffsetSameInstant(ZoneOffset.of(zoneOffset));
    }

    /**
     * 获取UTC偏移时间的偏移量字面值
     * @param offsetDateTime
     * @return
     */
    public static String getOffsetDateTimeOfZoneOffset(OffsetDateTime offsetDateTime) {
        if (offsetDateTime == null) {
            return null;
        }
        return offsetDateTime.getOffset().getId();
    }

    /**
     * 格式化LocalDateTime
     *
     * @param localDateTime
     * @return
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime, DateTimeFormatter formatter) {
        if (localDateTime == null) {
            return null;
        }
        if (formatter == null) {
            formatter = FORMATTER;
        }
        return localDateTime.format(formatter);
    }

    /**
     * 格式化LocalDateTime
     *
     * @param localDateTime
     * @return
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime, String formatter) {
        if (localDateTime == null) {
            return null;
        }
        if (formatter == null) {
            return localDateTime.format(FORMATTER);
        }
        return localDateTime.format(DateTimeFormatter.ofPattern(formatter));
    }

    /**
     * 格式化zonedDateTime
     *
     * @param zonedDateTime
     * @return
     */
    public static String formatZonedDateTime(ZonedDateTime zonedDateTime, String formatter) {
        if (zonedDateTime == null) {
            return null;
        }
        if (formatter == null) {
            return zonedDateTime.format(FORMATTER);
        }
        return zonedDateTime.format(DateTimeFormatter.ofPattern(formatter));
    }

    /**
     * 格式化zonedDateTime
     *
     * @param zonedDateTime
     * @param formatter     系统值DateTimeFormatter.ISO_ZONED_DATE_TIME  或自定义，为空用上面的常值
     * @return
     */
    public static String formatZonedDateTime(ZonedDateTime zonedDateTime, DateTimeFormatter formatter) {
        if (zonedDateTime == null) {
            return null;
        }
        if (formatter == null) {
            return zonedDateTime.format(FORMATTER);
        }
        return zonedDateTime.format(formatter);
    }

    /**
     * 格式化zonedDateTime
     *
     * @param zonedDateTime
     * @return
     */
    public static String formatZonedDateTime(ZonedDateTime zonedDateTime) {
        if (zonedDateTime == null) {
            return null;
        }
        return zonedDateTime.format(FORMATTER);
    }

    /**
     * 转换时区得到新时区的时间
     *
     * @param source
     * @param zoneId
     * @return
     */
    public static ZonedDateTime convertZonedDateTimeOfZoneId(ZonedDateTime source, String zoneId) {
        return source.withZoneSameInstant(ZoneId.of(zoneId));
    }

    /**
     * 转换时区得到新时区的时间
     *
     * @param source
     * @param zoneId
     * @return
     */
    public static ZonedDateTime convertZonedDateTimeOfZoneId(ZonedDateTime source, ZoneId zoneId) {
        return source.withZoneSameInstant(zoneId);
    }

    /**
     * 通过本地时间对象和时区得到新时区的时区时间
     *
     * @param source
     * @param zoneId
     * @return
     */
    public static ZonedDateTime convertLocalToZonedDateTimeOfZoneId(LocalDateTime source, ZoneId zoneId) {
        return ZonedDateTime.of(source, zoneId);
    }

    /**
     * 通过本地时间对象和时区得到新时区的时区时间
     *
     * @param source
     * @param zoneId
     * @return
     */
    public static ZonedDateTime convertLocalToZonedDateTimeOfZoneId(LocalDateTime source, String zoneId) {
        return ZonedDateTime.of(source, ZoneId.of(zoneId));
    }


    /**
     * 获取时区字符串
     *
     * @param zonedDateTime
     * @return
     */
    public static String getZonedId(ZonedDateTime zonedDateTime) {
        return zonedDateTime.getZone().getId();
    }

    /**
     * 判断是否是夏令时,不是夏令时就是冬令时
     */
    public static Boolean isDaylightSavingsTime(ZonedDateTime zonedDateTime) {
        return zonedDateTime.getZone().getRules().isDaylightSavings(zonedDateTime.toInstant());
    }

    /**
     * 获取指定日期的结束时间
     *
     * @param date
     * @return
     */
    public static Date getEndOfDate(Date date) {
        LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(date));
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }


    public static void main(String[] args) {

        // 获取当前时间
        ZonedDateTime currentTime = getCurrentZonedDateTime();
        System.out.println("当前时区：" + getZonedId(currentTime));
        System.out.println("当前时区时间：" + formatZonedDateTime(currentTime, DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("当前时区时间：" + formatZonedDateTime(currentTime));
        System.out.println("==============================");


        // 转换为北京时间（东八区）
        ZonedDateTime beijingTime = getCurrentZonedDateTime(ZoneId.of("Asia/Shanghai"));
        System.out.println("当前时区：" + getZonedId(beijingTime));
        System.out.println("北京时间：" + formatZonedDateTime(beijingTime, DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("北京时间：" + formatZonedDateTime(beijingTime));
        System.out.println("==============================");

        // 转换为纽约时间（西五区）
        ZonedDateTime newYorkTime = getCurrentZonedDateTime(ZoneId.of("America/New_York"));
        System.out.println("当前时区：" + getZonedId(newYorkTime));
        System.out.println("纽约时间：" + formatZonedDateTime(newYorkTime, DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("纽约时间：" + formatZonedDateTime(newYorkTime));

        System.out.println("==============================");

        System.out.println("时区转换把美国纽约的时间转换成北京时间");
        ZonedDateTime newYorkTime2 = getCurrentZonedDateTime(ZoneId.of("America/New_York"));
        ZonedDateTime beijingTime2 = convertZonedDateTimeOfZoneId(newYorkTime2, ZoneId.of("Asia/Shanghai"));
        System.out.println("纽约时间：" + newYorkTime2);
        System.out.println("转换为北京时间：" + beijingTime2);

        System.out.println("夏令时==============================");
        // 纽约时间，2019年11月20日
        LocalDateTime localDateTime3 = LocalDateTime.of(2019, 11, 20, 19, 0);
        ZonedDateTime newYorkTime3 = convertLocalToZonedDateTimeOfZoneId(localDateTime3, ZoneId.of("America/New_York"));

        // 纽约时间，2019年6月20日
        localDateTime3 = LocalDateTime.of(2019, 6, 20, 19, 0);
        ZonedDateTime summerNewYorkTime = convertLocalToZonedDateTimeOfZoneId(localDateTime3, ZoneId.of("America/New_York"));

        System.out.println("2019年11月20日纽约时间：" + formatZonedDateTime(newYorkTime3, DateTimeFormatter.ISO_ZONED_DATE_TIME));
        System.out.println("2019年6月20日夏令时纽约时间：" + formatZonedDateTime(summerNewYorkTime, DateTimeFormatter.ISO_ZONED_DATE_TIME));

        System.out.println("==============================");
        if (isDaylightSavingsTime(newYorkTime)) {
            System.out.println("纽约时间当前处于夏令时。");
        } else {
            System.out.println("纽约时间当前处于冬令时。");
        }


        System.out.println("处理特定日期和时间==============================");
        LocalDateTime dateTime = LocalDateTime.of(2023, 3, 15, 14, 30); // 假设这是2023年3月15日，14:30
        ZoneId zoneIdN = ZoneId.of("America/New_York");

        // 将本地日期时间转换为带时区的日期时间
//        ZonedDateTime zonedDateTime = dateTime.atZone(zoneIdN);
        ZonedDateTime zonedDateTime = convertLocalToZonedDateTimeOfZoneId(dateTime, zoneIdN);
        System.out.println("特定时间: " + zonedDateTime);

        // 格式化输出
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
        System.out.println("格式化时间: " + formatZonedDateTime(zonedDateTime, formatter2));


        if (isDaylightSavingsTime(zonedDateTime)) {
            System.out.println("纽约时间当前处于夏令时。");
        } else {
            System.out.println("纽约时间当前处于冬令时。");
        }

        System.out.println("UTC偏移量，而不是具体的地理位置时区==============================");
        DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 创建UTC偏移的时间
        OffsetDateTime utcNow = getCurrentUtcOffsetDateTime();
        System.out.println("UTC现在的时间: " + utcNow);
        System.out.println("UTC现在的时间: " + formatOffsetDateTime(utcNow, formatter4));

        // 转换为其他UTC偏移的时间（例如，东八区）
        OffsetDateTime beijingTime4 = convertOffsetDateTimeOfZoneOffset(utcNow,"+08:00");
        System.out.println("OffsetDateTime当前时间的偏移量: " + getOffsetDateTimeOfZoneOffset(beijingTime4));
        System.out.println("北京时间: " + beijingTime4);
        System.out.println("北京时间: " + formatOffsetDateTime(beijingTime4,formatter4));
        OffsetDateTime newYueTime5 = convertOffsetDateTimeOfZoneOffset(beijingTime4,"-05:00");
        System.out.println("当前时间（纽约）: " + newYueTime5);
        System.out.println("当前时间（纽约）: " + formatOffsetDateTime(newYueTime5,formatter4));
        System.out.println("==============================");
        // 获取当前的时间戳并转换为ZonedDateTime（以纽约为例）
        //获取当前时间戳
        long timestamp1 = System.currentTimeMillis();
        long timestamp2 = Instant.now().toEpochMilli();
        long timestamp3 = new Date().getTime();
        long timestamp4 = Calendar.getInstance().getTimeInMillis();
        System.out.println("当前时间戳1: " + timestamp1);
        System.out.println("当前时间戳2: " + timestamp2);
        System.out.println("当前时间戳3: " + timestamp3);
        System.out.println("当前时间戳4: " + timestamp4);

        Instant now = Instant.now();
        ZonedDateTime zdt = now.atZone(ZoneId.of("America/New_York"));
        ZonedDateTime zdt2 = getCurrentZonedDateTime(ZoneId.of("America/New_York"));
        System.out.println("当前时间（纽约）zdt: " + zdt);
        System.out.println("当前时间（纽约）zdt2: " + zdt2);
        System.out.println("当前时间（纽约）: " + formatZonedDateTime(zdt,FORMATTER));
        ZonedDateTime beijingTime5 = convertZonedDateTimeOfZoneId(zdt,ZoneId.of("Asia/Shanghai"));
        System.out.println("当前时间（北京）: " + formatZonedDateTime(beijingTime5,FORMATTER));


        long timestamp5 = zdt.toInstant().toEpochMilli();
        long timestamp6 = beijingTime5.toInstant().toEpochMilli();
        System.out.println("ZonedDateTime转时间戳: " + timestamp5);
        System.out.println("ZonedDateTime转时间戳: " + timestamp6);
        System.out.println("当前时刻，任何时区的时间戳相等: " + timestamp6);



        System.out.println("当前时间（北京）: " + convertZonedDateTimeOfZoneId(timestamp5, ZoneId.of("Asia/Shanghai")));
        System.out.println("当前时间（纽约）: " + convertZonedDateTimeOfZoneId(timestamp5, ZoneId.of("America/New_York")));


        Date date = Date.from(zdt.toInstant());
        SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("ZonedDateTime转Date: " + ft.format(date));

        LocalDateTime localDateTime = zdt.toLocalDateTime();
        System.out.println("ZonedDateTime转LocalDateTime: " + localDateTime);
        System.out.println("ZonedDateTime转LocalDateTime: " + localDateTime.format(formatter4));
        OffsetDateTime offsetDateTime = zdt.toOffsetDateTime();
        System.out.println("ZonedDateTime转OffsetDateTime: " + offsetDateTime);
        System.out.println("ZonedDateTime转OffsetDateTime: " + offsetDateTime.format(formatter4));
        System.out.println("OffsetDateTime当前时间的偏移量: " + offsetDateTime.getOffset().getId());
    }
}
