公司最近要做数据指标的统计,周同比和月同比一些时间换算分析,日期函数大乱斗
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));
}
}
}