Work Documentary-33-Date function chaos

The company recently wants to do statistics on data indicators, some time conversion analysis of week-to-week and month-to-month comparisons, date function chaos

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;

    /**
     * Get the time span of a certain week in a certain year
     *
     * @param year year
     * @param week week number
     * @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);
        // Set Monday as the first day of the week
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        // can not be set
        calendar.setMinimalDaysInFirstWeek(4);
        // get the current year
        int weekYear = calendar. get(Calendar. YEAR);
        // Get the start date of the week of the specified year (day of the week)
        calendar.setWeekDate(weekYear, week, Calendar.MONDAY);
        Date time = calendar. getTime();
        String startTime = new SimpleDateFormat(DATE_STR_FORMAT).format(time);
        dateMap.put(START_TIME, startTime);
        // Get the end date of the week of the specified year (day of the week)
        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;
    }

    /**
     * Get how many weeks there are in a year
     *
     * @param year year
     * @return The total number of weeks in the current year
     */
    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 + "")) {<!-- -->
                    // Determine whether the year matches, if it matches, there are 53 weeks.
                    week = 53;
                }
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        }
        return week;
    }

    /**
     * Get the date span of all weeks in a year
     *
     * @param year year
     * @return list the date range of all weeks in the current year
     */
    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;
    }

    /**
     * Get the week start and week end time of the date
     */
    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)};
    }

    /**
     * Get the absolute value of the month difference between two dates
     */
    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);
    }

    /**
     * Get the date at the end of the month
     */
    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();
    }

    /**
     * Get the date of the beginning of the month
     */
    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();
    }

    /**
     * Get the number of days in a month
     */
    public static int getDaysOfMonth(Date date) {<!-- -->
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId. systemDefault();
        LocalDate startDate = instant. atZone(zoneId).toLocalDate();
        return startDate. lengthOfMonth();
    }

    /**
     * According to the date --> the information of the week spanning the month
     */
    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);
        //Initialization data
        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();
        // processing across months
        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;
    }

    /**
     * According to the date --> get the number of days that have passed
     */
    public static int getMonthPassDays(Date date) {<!-- -->
        Calendar calendar = Calendar. getInstance();
        calendar. setTime(date);
        int passDays = calendar. get(Calendar. DAY_OF_MONTH);
        return passDays;
    }

    /**
     * Get the start and end time of the previous X weeks [including the current week]
     *
     * @param date current time
     * @param preNum the previous X weeks
     * @param cycle Whether the same period
     */
    public static List<TimeRangeParam> getPreWeeksFromCurrent(Date date, int preNum, boolean cycle) {<!-- -->
        Calendar cal = Calendar. getInstance();
        cal. setTime(date);

        // Set Monday as the start week of each week
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        List<TimeRangeParam> result = Lists. newArrayList();

        // add the current week
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date weekFirstDay = getWeekPeriodByDate(date)[START];
        Date weekLastDay = getWeekPeriodByDate(cal. getTime())[END];

        // The pass is year-on-year, just control the end time
        if (cycle) {<!-- -->
            weekLastDay = date;
        }
        result.add(new TimeRangeParam(sdf.format(weekFirstDay.getTime()), sdf.format(DateUtils.getDayEndTime(weekLastDay))));

        // add the previous 4 weeks
        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;
    }

    /**
     * Get the start time of X months from the current time
     */
    public static Long getMonthStartTime(Date date, int num) {<!-- -->
        long currentTime = date. getTime();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// Get the current date
        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);// Set to 1st, the current date is the first day of this month
        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();
    }

    /**
     * Get the end time of the current X month
     */
    public static Long getMonthEndTime(Date date, int num) {<!-- -->
        long currentTime = date. getTime();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// Get the current date
        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));// Get the last day of the current 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();
    }

    /**
     * Get the start and end time of the previous X month [including the current month]
     *
     * @param date current time
     * @param preNum the previous X months
     * @param cycle Whether the same period
     */
    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");

        // the number of days in the current month
        LocalDateTime localDateTime = DateUtils.date2LocalDateTime(date);
        int curMonthDays = localDateTime.toLocalDate().lengthOfMonth();
        int dayOfMonth = localDateTime. getDayOfMonth();
        // If it is the whole month, then it does not need to be processed according to the same period, but the whole month is processed
        if (curMonthDays == dayOfMonth) {<!-- -->
            cycle = false;
        }

        // Accumulate the time of the previous preNum months
        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 the total number of days in the month is smaller than the current number of days, round up the month
                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));
        }
        // plus the range of the current month
        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 it is year-on-year, control the end time
        if (cycle) {<!-- -->
            endTimeStr = ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(DateUtils.getDayEndTime(date)), ZoneId.systemDefault()));
        }
        result.add(new TimeRangeParam(startTimeStr, endTimeStr));
        return result;
    }

    /**
     * Get month information
     */
    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();
    }

    /**
     * Given a start and end time, calculate the number of days the week spans
     */
    public static WeekInfo getWeekCrossInfoByPeriod(Date start, Date end) {<!-- -->
        if (start.after(end)) {<!-- -->
            throw new RuntimeException("The parameter is abnormal, the week information cannot be calculated");
        }
        LocalDate startDate = DateUtils.date2LocalDate(start);
        LocalDate endDate = DateUtils.date2LocalDate(end);

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

        // If the week spans the previous month and the current month, you need to calculate how many days they span the previous month and the current month
        if (startDate.getMonthValue() != endDate.getMonthValue()) {<!-- -->
            weekInfo.setPreMonth(startDate.getMonthValue());
            weekInfo.setPreMonthDays(startDate.lengthOfMonth());
            // Calculate how many days the week spans the previous month
            LocalDate preMonthLastDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
            weekInfo.setPreMonthCrossDays(preMonthLastDate.getDayOfMonth() - startDate.getDayOfMonth() + 1);
            // Calculate the number of days in the month of the week
            LocalDate curMonthFirstDate = endDate.with(TemporalAdjusters.firstDayOfMonth());
            weekInfo.setCurMonthCrossDays(endDate.getDayOfMonth() - curMonthFirstDate.getDayOfMonth() + 1);
        } else {<!-- --> // If the week is in the same month, you only need to calculate the number of days in the week [actually 7 days]
            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;
    }

    /**
     * Get all data between two dates
     */
    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());

    }
}

Test Case

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 {<!-- -->

    // Get the week information of the date
    @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));
        }
    }


    // Get the span information of the week information [week start time - week end time]
    @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);
    }

    // get the current month + previous 4 months
    @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("preMonthsFromCurrent-" + JSON.toJSONString(preMonthsFromCurrent));
            preMonthsFromCurrent = WeekMonthUtil.getPreMonthsFromCurrent(date, 4, false);
            Assert.assertEquals(5, preMonthsFromCurrent.size());
            System.out.println("General Months-" + JSON.toJSONString(preMonthsFromCurrent));
        }
    }

    // Get the current week + previous four weeks
    @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("Normal week:" + JSON.toJSONString(weeks));
            weeks = WeekMonthUtil.getPreWeeksFromCurrent(date, 4, true);
            Assert.assertEquals(5, weeks.size());
            System.out.println("weekly year-on-year:" + JSON.toJSONString(weeks));
        }
    }

}