工作纪实-33-日期函数大乱斗

公司最近要做数据指标的统计,周同比和月同比一些时间换算分析,日期函数大乱斗


package com.renrenche.business.sale.customer.manage.infrastructure.general.util;

import com.google.common.collect.Lists;
import com.renrenche.business.sale.customer.manage.domain.common.bean.TimeRangeParam;
import lombok.experimental.UtilityClass;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author liulei44
 * @date 2023/5/10 12:06 AM
 */
@UtilityClass
public class WeekMonthUtil {
    
    

    public static final String DATE_STR_FORMAT = "yyyy-MM-dd";

    public static final String START_TIME = "startTime";
    public static final String END_TIME = "endTime";

    public static final Integer START = 0;
    public static final Integer  END = 1;

    /**
     * 获取某年某周的时间跨度
     *
     * @param year 年份
     * @param week 周数
     * @return k-v
     */
    private static Map<String, String> getWeekRangeMap(int year, int week) {
    
    
        Map<String, String> dateMap = new HashMap<>(8);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        // 设置星期一为一周开始的第一天
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        // 可以不用设置
        calendar.setMinimalDaysInFirstWeek(4);
        // 获得当前的年
        int weekYear = calendar.get(Calendar.YEAR);
        // 获得指定年的第几周的开始日期(星期几)
        calendar.setWeekDate(weekYear, week, Calendar.MONDAY);
        Date time = calendar.getTime();
        String startTime = new SimpleDateFormat(DATE_STR_FORMAT).format(time);
        dateMap.put(START_TIME, startTime);
        // 获得指定年的第几周的结束日期(星期几)
        calendar.setWeekDate(weekYear, week, Calendar.SUNDAY);
        time = calendar.getTime();
        String endTime = new SimpleDateFormat(DATE_STR_FORMAT).format(time);
        dateMap.put(END_TIME, endTime);
        return dateMap;
    }

    /**
     * 获取某年有多少周
     *
     * @param year 年份
     * @return 当前年份的总周数
     */
    public static int getYearWeekCount(int year) {
    
    
        int week = 52;
        try {
    
    
            Map<String, String> timeMap = getWeekRangeMap(year, 53);
            if (!CollectionUtils.isEmpty(timeMap)) {
    
    
                String startTime = timeMap.get(START_TIME);
                if (startTime.substring(0, 4).equals(year + "")) {
    
    
                    // 判断年度是否相符,如果相符说明有53个周。
                    week = 53;
                }
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return week;
    }

    /**
     * 获取某年所有周的日期跨度
     *
     * @param year 年份
     * @return list 当前年份所有周的日期范围
     */
    public static List<Map<String, String>> getYearWeekMap(int year) {
    
    
        int weeks = getYearWeekCount(year);
        List<Map<String, String>> yearWeekMap = new ArrayList<>();
        for (int i = 1; i <= weeks; i++) {
    
    
            Map<String, String> dateMap = getWeekRangeMap(year, i);
            yearWeekMap.add(dateMap);
        }
        return yearWeekMap;
    }

    /**
     * 获取日期所在的周开始和周结束时间
     */
    public static Date[] getWeekPeriodByDate(Date date) {
    
    
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        // start of the week
        if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
    
    
            calendar.add(Calendar.DAY_OF_YEAR, -1);
        }
        calendar.add(Calendar.DAY_OF_WEEK, -(calendar.get(Calendar.DAY_OF_WEEK) - 2));
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        long startTime = calendar.getTimeInMillis();
        // end of the week
        calendar.add(Calendar.DAY_OF_WEEK, 6);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        long endTime = calendar.getTimeInMillis();
        return new Date[]{
    
    new Date(startTime), new Date(endTime)};
    }

    /**
     * 获取两个日期之间的月份差的绝对值
     */
    public static int getMonthSpace(Date date1, Date date2) {
    
    
        int result;
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(date1);
        c2.setTime(date2);
        int i = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
        int month = 0;
        if (i < 0) {
    
    
            month = -i * 12;
        } else if (i > 0) {
    
    
            month = i * 12;
        }
        result = (c2.get(Calendar.MONTH) - c1.get(Calendar.MONTH)) + month;
        return Math.abs(result);
    }

    /**
     * 获取月末的日期
     */
    public static Date getMonthEndDate(Date date) {
    
    
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        return calendar.getTime();
    }

    /**
     * 获取月初的日期
     */
    public static Date getMonthBeginDate(Date date) {
    
    
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        return calendar.getTime();
    }

    /**
     * 获取一个月多少天
     */
    public static int getDaysOfMonth(Date date) {
    
    
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate startDate = instant.atZone(zoneId).toLocalDate();
        return startDate.lengthOfMonth();
    }

    /**
     * 根据日期-->所在周跨月的的信息
     */
    public static WeekMonthInfo getWeekMonthInfo(Date date) {
    
    
        Date[] currentWeekTimeFrame = WeekMonthUtil.getWeekPeriodByDate(date);
        Date weekStart = currentWeekTimeFrame[START];
        Date weekEnd = currentWeekTimeFrame[END];
        int monthSpace = WeekMonthUtil.getMonthSpace(weekStart, weekEnd);
        //初始化数据
        WeekMonthInfo weekMonthInfo = WeekMonthInfo.builder()
                .preMonth(DateUtils.getMonth(DateUtils.addMonths(date,-1)))
                .preMonthAllDays(WeekMonthUtil.getDaysOfMonth(DateUtils.addMonths(date,-1)))
                .curMonth(DateUtils.getMonth(date))
                .curMonthAllDays(WeekMonthUtil.getDaysOfMonth(date))
                .curMonthPassDays(WeekMonthUtil.getMonthPassDays(date))
                .curWeekAllDays(7)
                .build();
        // 跨月处理
        if (monthSpace > 0) {
    
    
            Date monthEndDate = WeekMonthUtil.getMonthEndDate(weekStart);
            int preIntervalDays = Math.abs(DateUtils.getIntervalDays(weekStart, monthEndDate));
            weekMonthInfo.setPreMonthCrossDays(preIntervalDays + 1);
            weekMonthInfo.setCurWeekAllDays(7-weekMonthInfo.getPreMonthCrossDays());
        }
        int curIntervalDays = Math.abs(DateUtils.getIntervalDays(weekStart, date));
        weekMonthInfo.setCurWeekPassDays(curIntervalDays + 1);
        return weekMonthInfo;
    }

    /**
     * 根据日期-->获取已经过去的天数
     */
    public static int getMonthPassDays(Date date) {
    
    
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int passDays = calendar.get(Calendar.DAY_OF_MONTH);
        return passDays;
    }

    /**
     * 获取前X周的开始和结束时间【包括当前所在周】
     *
     * @param date   当前时间
     * @param preNum 前X周
     * @param cycle  是否同比
     */
    public static List<TimeRangeParam> getPreWeeksFromCurrent(Date date, int preNum, boolean cycle) {
    
    
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        // 设置为每周的周一为起始周
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        List<TimeRangeParam> result = Lists.newArrayList();

        // 添加当前所在周
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date weekFirstDay = getWeekPeriodByDate(date)[START];
        Date weekLastDay = getWeekPeriodByDate(cal.getTime())[END];

        // 通过是同比,控制好end时间即可
        if (cycle) {
    
    
            weekLastDay = date;
        }
        result.add(new TimeRangeParam(sdf.format(weekFirstDay.getTime()), sdf.format(DateUtils.getDayEndTime(weekLastDay))));

        // 添加前面4个周
        for (int i = 0; i < preNum; i++) {
    
    
            weekFirstDay = DateUtils.addDays(weekFirstDay, -7);
            weekLastDay = DateUtils.addDays(weekLastDay, -7);
            result.add(new TimeRangeParam(sdf.format(weekFirstDay), sdf.format(DateUtils.getDayEndTime(weekLastDay))));
        }
        Collections.reverse(result);
        return result;
    }

    /**
     * 获取离当前X月的时间开始时间
     */
    public static Long getMonthStartTime(Date date, int num) {
    
    
        long currentTime = date.getTime();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, num);
        calendar.set(Calendar.DAY_OF_MONTH, 1);// 设置为1号,当前日期既为本月第一天
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTimeInMillis();
    }

    /**
     * 获取离当前X月的时间结束时间
     */
    public static Long getMonthEndTime(Date date, int num) {
    
    
        long currentTime = date.getTime();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, num);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));// 获取当前月最后一天
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);

        return calendar.getTimeInMillis();
    }

    /**
     * 获取前X月的开始和结束时间【包括当前所在月】
     *
     * @param date   当前时间
     * @param preNum 前X月
     * @param cycle  是否同比
     */
    public static List<TimeRangeParam> getPreMonthsFromCurrent(Date date, int preNum, boolean cycle) {
    
    
        List<TimeRangeParam> result = Lists.newArrayList();
        DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 当前月份的天数
        LocalDateTime localDateTime = DateUtils.date2LocalDateTime(date);
        int curMonthDays = localDateTime.toLocalDate().lengthOfMonth();
        int dayOfMonth = localDateTime.getDayOfMonth();
        // 如果是整个月,那么不用按照同比处理,整月处理
        if (curMonthDays == dayOfMonth) {
    
    
            cycle = false;
        }

        // 累加前preNum个月的时间
        for (int i = preNum; i > 0; i--) {
    
    
            Long startTime = getMonthStartTime(date, -i);
            Long endTime = getMonthEndTime(date, -i);
            String startTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault()));
            String endTimeStr;
            if (cycle) {
    
    
                LocalDateTime endLocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());
                int monthDays = endLocalDateTime.toLocalDate().lengthOfMonth();
                // 如果月份总天数都比当前天数要小,则取整月
                if (monthDays <= dayOfMonth) {
    
    
                    endTimeStr = ftf.format(endLocalDateTime);
                } else {
    
    
                    Long dayEndTime = DateUtils.getDayEndTime(DateUtils.addMonths(date, -i));
                    endTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(dayEndTime), ZoneId.systemDefault()));
                }
            } else {
    
    
                endTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault()));
            }
            result.add(new TimeRangeParam(startTimeStr, endTimeStr));
        }
        // 加上当前月的范围
        Long startTime = getMonthStartTime(date, 0);
        Long endTime = getMonthEndTime(date, 0);
        String startTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault()));
        String endTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault()));

        // 如果是同比,控制一下结束时间
        if (cycle) {
    
    
            endTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(DateUtils.getDayEndTime(date)), ZoneId.systemDefault()));
        }
        result.add(new TimeRangeParam(startTimeStr, endTimeStr));
        return result;
    }

    /**
     * 获取月份信息
     */
    public static int getMonthByDateStr(String dateStr) throws ParseException {
    
    
        SimpleDateFormat formatter = new SimpleDateFormat(DATE_STR_FORMAT);
        Date date = formatter.parse(dateStr);
        LocalDate localDate = DateUtils.date2LocalDate(date);
        return localDate.getMonthValue();
    }

    /**
     * 给定开始和结束时间,计算周跨越的天数信息
     */
    public static WeekInfo getWeekCrossInfoByPeriod(Date start, Date end) {
    
    
        if (start.after(end)) {
    
    
            throw new RuntimeException("参数异常,无法计算周信息");
        }
        LocalDate startDate = DateUtils.date2LocalDate(start);
        LocalDate endDate = DateUtils.date2LocalDate(end);

        WeekInfo weekInfo = WeekInfo.builder()
                .curMonth(endDate.getMonthValue())
                .curMonthDays(endDate.lengthOfMonth())
                .build();

        // 如果该周跨上个月和所在月份两个月份,则需要计算它们跨上个月多少天和所在月份多少天
        if (startDate.getMonthValue() != endDate.getMonthValue()) {
    
    
            weekInfo.setPreMonth(startDate.getMonthValue());
            weekInfo.setPreMonthDays(startDate.lengthOfMonth());
            // 计算该周跨上个月多少天
            LocalDate preMonthLastDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
            weekInfo.setPreMonthCrossDays(preMonthLastDate.getDayOfMonth() - startDate.getDayOfMonth() + 1);
            // 计算该周在所在月份的天数
            LocalDate curMonthFirstDate = endDate.with(TemporalAdjusters.firstDayOfMonth());
            weekInfo.setCurMonthCrossDays(endDate.getDayOfMonth() - curMonthFirstDate.getDayOfMonth() + 1);
        } else {
    
     // 如果该周在同一个月份内,则只需要计算该周的天数【其实就是7天】
            LocalDate preMonth = DateUtils.date2LocalDate(DateUtils.addMonths(end, -1));
            weekInfo.setPreMonth(preMonth.getMonthValue());
            weekInfo.setPreMonthDays(preMonth.lengthOfMonth());
            int daysInCurrentMonth = endDate.getDayOfMonth() - startDate.getDayOfMonth() + 1;
            weekInfo.setCurMonthCrossDays(daysInCurrentMonth);
        }
        return weekInfo;
    }

    /**
     * 获取两个日期之间的所有数据
     */
    public static List<String> getPeriodDateStr(String start, String end) {
    
    
        LocalDate timeStart = LocalDate.parse(start);
        LocalDate timeEnd = LocalDate.parse(end);
        return Stream.iterate(timeStart, localDate -> localDate.plusDays(1))
                .limit(ChronoUnit.DAYS.between(timeStart, timeEnd) + 1)
                .map(localDate -> localDate.toString().replaceAll("-",""))
                .collect(Collectors.toList());

    }
}

测试案例

package com.renrenche.business.sale.customer.manage.infrastructure.utils;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Lists;
import com.renrenche.business.sale.customer.manage.domain.common.bean.TimeRangeParam;
import com.renrenche.business.sale.customer.manage.infrastructure.general.util.SpringUtil;
import com.renrenche.business.sale.customer.manage.infrastructure.general.util.WeekInfo;
import com.renrenche.business.sale.customer.manage.infrastructure.general.util.WeekMonthInfo;
import com.renrenche.business.sale.customer.manage.infrastructure.general.util.WeekMonthInfo;
import com.renrenche.business.sale.customer.manage.infrastructure.general.util.WeekMonthUtil;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author liulei44
 * @date 2023/5/10 12:58 AM
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(SpringUtil.class)
public class WeekInfoUtilTest {
    
    

    // 获取日期的周信息
    @Test
    public void dateWeekInfo() throws ParseException {
    
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        List<String> param = Lists.newArrayList();
        param.add("2023-05-10");
        param.add("2023-02-02");
        param.add("2022-12-30");
        for (String dateStr : param) {
    
    
            Date date = formatter.parse(dateStr);
            WeekMonthInfo weekInfo = WeekMonthUtil.getWeekMonthInfo(date);
            System.out.println(dateStr + "=>" + JSON.toJSONString(weekInfo));
        }
    }


    // 获取周信息的跨度信息【周开始时间-周结束时间】
    @Test
    public void getWeeksCrossMonthByPeriod() throws ParseException {
    
    
        // [2023-02-27,2023-03-05]
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Date start = formatter.parse("2023-02-27");
        Date end = formatter.parse("2023-03-05");
        WeekInfo weekInfo = WeekMonthUtil.getWeekCrossInfoByPeriod(start, end);
        System.out.println(JSON.toJSONString(weekInfo));

        // [2023-11-27,2023-12-03]
        start = formatter.parse("2023-11-27");
        end = formatter.parse("2023-12-03");
        weekInfo = WeekMonthUtil.getWeekCrossInfoByPeriod(start, end);
        System.out.println(JSON.toJSONString(weekInfo));

        // [2023-04-10, 2023-04-16]
        start = formatter.parse("2023-04-10");
        end = formatter.parse("2023-04-16");
        weekInfo = WeekMonthUtil.getWeekCrossInfoByPeriod(start, end);
        System.out.println(JSON.toJSONString(weekInfo));
        Assert.assertEquals(7, weekInfo.getCurMonthCrossDays());

        // [2023-04-10, 2023-04-16]
        start = formatter.parse("2023-05-10");
        end = formatter.parse("2023-05-12");
        weekInfo = WeekMonthUtil.getWeekCrossInfoByPeriod(start, end);
        System.out.println(JSON.toJSONString(weekInfo));
        Assert.assertEquals(3, weekInfo.getCurMonthCrossDays());
    }

    @Test
    public void getMonthTest() throws ParseException {
    
    
        int month = WeekMonthUtil.getMonthByDateStr("2023-02-27");
        Assert.assertEquals(2, month);
    }

    // 获取当前月+前四月
    @Test
    public void getPreMonthsFromCurrentTest() throws Exception {
    
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Date> paramList = Lists.newArrayList();
        paramList.add(formatter.parse("2023-05-10 00:00:00"));
        paramList.add(formatter.parse("2023-03-29 00:00:00"));
        paramList.add(formatter.parse("2023-02-20 00:00:00"));
        paramList.add(formatter.parse("2022-12-30 00:00:00"));
        for (Date date : paramList) {
    
    
            List<TimeRangeParam> preMonthsFromCurrent = WeekMonthUtil.getPreMonthsFromCurrent(date, 4, true);
            Assert.assertEquals(5, preMonthsFromCurrent.size());
            System.out.println("同比月-" + JSON.toJSONString(preMonthsFromCurrent));
            preMonthsFromCurrent = WeekMonthUtil.getPreMonthsFromCurrent(date, 4, false);
            Assert.assertEquals(5, preMonthsFromCurrent.size());
            System.out.println("普通月-" + JSON.toJSONString(preMonthsFromCurrent));
        }
    }

    // 获取当前周+前四周
    @Test
    public void getPreWeeks() throws ParseException {
    
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Date> param = Lists.newArrayList();
        param.add(formatter.parse("2023-02-27 00:00:00"));
        param.add(formatter.parse("2023-01-24 00:00:00"));
        param.add(formatter.parse("2023-04-22 00:00:00"));
        param.add(formatter.parse("2023-05-10 00:00:00"));
        for (Date date : param) {
    
    
            List<TimeRangeParam> weeks = WeekMonthUtil.getPreWeeksFromCurrent(date, 4, false);
            Assert.assertEquals(5, weeks.size());
            System.out.println("普通周:" + JSON.toJSONString(weeks));
            weeks = WeekMonthUtil.getPreWeeksFromCurrent(date, 4, true);
            Assert.assertEquals(5, weeks.size());
            System.out.println("周同比:" + JSON.toJSONString(weeks));
        }
    }

}

猜你喜欢

转载自blog.csdn.net/u013553309/article/details/130820733
今日推荐