小工具:将传入的日期区间按日、周、月、季、年分隔成多个时间区间

最近在做公司项目的时候,有一个需求是需要传入一个开始时间和一个结束时间。但是要按照统计的方式分别以日、周、月、季、年的方式来分组进行统计。

这个问题其实有两个解决方案,一个是利用sql处理日期的字段格式化进行分组;另一种就是写一个通用一点的SQL,然后在service里处理这些日期,遍历调用这个接口。

由于我们的需求对时间的要求比较复杂,并不可以按照自然月的方式进行统计,而是上个月26到本月25日为一个周期。比如统计2019年1月,实际上的开始和结束时间为:2018-12-26~2019-01-25。也正是因为这点,使用sql分组的方式就更加难以实现。

所以我选用了第一个解决方案,就是在业务里处理这些时间。遂萌生了写这个工具类的想法。

因为我们的业务需求比较特殊,所以下边贴上的代码还是按照自然月来处理的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272

import java.text.SimpleDateFormat;
import java.util.*;

/**
* 传开始和结束日期 按日、周、月、季、年分割成一些时间区间
* <p>
*/
public class DateSeparateUtil {

/**
* 按日分隔
*
* @param startTime
* @param endTime
* @param format
* @param timeForQuery 分隔好的时间区间 一个startTime和endTime为一组
* @param timeForShow 显示在横轴的时间区间
*/
public static void separateByDay(Date startTime, Date endTime, SimpleDateFormat format, List<Map<String, String>> timeForQuery, List<String> timeForShow) {
if (timeForQuery == null) {
timeForQuery = new ArrayList<>();
}
if (timeForShow == null) {
timeForShow = new ArrayList<>();
}

Calendar start = Calendar.getInstance();
start.setTime(startTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);

while (start.getTimeInMillis() < end.getTimeInMillis()) {
HashMap<String, String> map = new HashMap<>();
String startStr = format.format(new Date(start.getTimeInMillis()));
map.put("startTime", startStr + " 00:00:00"); // 按天算的话 开始和结束就是同一天
map.put("endTime", startStr + " 23:59:59");
timeForQuery.add(map);
timeForShow.add(startStr.substring(5, 10));
start.add(Calendar.DAY_OF_YEAR, 1);
}
}

/**
* 按星期分隔
*
* @param startTime
* @param endTime
* @param format
* @param timeForQuery
* @param timeForShow
*/
public static void separateByWeek(Date startTime, Date endTime, SimpleDateFormat format, List<Map<String, String>> timeForQuery, List<String> timeForShow) {
if (timeForQuery == null) {
timeForQuery = new ArrayList<>();
}
if (timeForShow == null) {
timeForShow = new ArrayList<>();
}
Calendar start = Calendar.getInstance();
start.setFirstDayOfWeek(Calendar.MONDAY);
start.setTime(startTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);

while (start.getTimeInMillis() < end.getTimeInMillis()) {
HashMap<String, String> map = new HashMap<>();
String startStr = format.format(new Date(start.getTimeInMillis()));
map.put("startTime", startStr + " 00:00:00");
start.add(Calendar.DAY_OF_WEEK, /*start.getActualMaximum(Calendar.DAY_OF_WEEK)*/8 - start.get(Calendar.DAY_OF_WEEK));
if (start.getTimeInMillis() > end.getTimeInMillis()) {
// 结束日期不能超过传来的最后日期
start.setTimeInMillis(end.getTimeInMillis());
}
String weekEnd = format.format(new Date(start.getTimeInMillis()));
map.put("endTime", weekEnd + " 23:59:59");
timeForQuery.add(map);
timeForShow.add(startStr.substring(5, 10) + "/" + weekEnd.substring(5, 10));
// 加一天到下一周
start.add(Calendar.DAY_OF_WEEK, 1);
}
}

/**
* 按月分割
*
* @param startTime
* @param endTime
* @param format
* @param timeForQuery
* @param timeForShow
*/
public static void separateByMonth(Date startTime, Date endTime, SimpleDateFormat format, List<Map<String, String>> timeForQuery, List<String> timeForShow) {
if (timeForQuery == null) {
timeForQuery = new ArrayList<>();
}
if (timeForShow == null) {
timeForShow = new ArrayList<>();
}
Calendar start = Calendar.getInstance();
start.setTime(startTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);

while (start.getTimeInMillis() < end.getTimeInMillis()) {
HashMap<String, String> map = new HashMap<>();
String startStr = format.format(new Date(start.getTimeInMillis()));
map.put("startTime", startStr + " 00:00:00");

// region 按照自然月方式处理
start.set(Calendar.DAY_OF_MONTH, start.getActualMaximum(Calendar.DAY_OF_MONTH));
// endregion
if (start.getTimeInMillis() > end.getTimeInMillis()) {
start.setTimeInMillis(end.getTimeInMillis());// 不能超过end时期
}
Date monthEnd = new Date(start.getTimeInMillis());
map.put("endTime", format.format(monthEnd) + " 23:59:59");
timeForQuery.add(map);
timeForShow.add(format.format(monthEnd).substring(2, 7));
start.add(Calendar.DAY_OF_MONTH, 1); // 跳到下一天
}
}

/**
* 按季度拆分
*
* @param startTime
* @param endTime
* @param format
* @param timeForQuery
* @param timeForShow
*/
public static void separateByQuarter(Date startTime, Date endTime, SimpleDateFormat format, List<Map<String, String>> timeForQuery, List<String> timeForShow) {
if (timeForQuery == null) {
timeForQuery = new ArrayList<>();
}
if (timeForShow == null) {
timeForShow = new ArrayList<>();
}
Calendar start = Calendar.getInstance();
start.setTime(startTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);

List<Integer> quarter1 = Arrays.asList(0, 1, 2);
List<Integer> quarter2 = Arrays.asList(3, 4, 5);
List<Integer> quarter3 = Arrays.asList(6, 7, 8);
List<Integer> quarter4 = Arrays.asList(9, 10, 11);

while (start.getTimeInMillis() < end.getTimeInMillis()) {

int curMonth = start.get(Calendar.MONTH); // 0-11

// 处理第一季度的月份
if (quarter1.contains(curMonth)) {
handleQuarterOnce(quarter1, 1, timeForQuery, timeForShow, format, start, end);
}
// 处理第二季度的月份
if (quarter2.contains(curMonth)) {
handleQuarterOnce(quarter2, 2, timeForQuery, timeForShow, format, start, end);
}
// 处理第三季度的月份
if (quarter3.contains(curMonth)) {
handleQuarterOnce(quarter3, 3, timeForQuery, timeForShow, format, start, end);
}
// 处理第四季度的月份
if (quarter4.contains(curMonth)) {
handleQuarterOnce(quarter4, 4, timeForQuery, timeForShow, format, start, end);
}
}

}

/**
* 按年分隔
*
* @param startTime
* @param endTime
* @param format
* @param timeForQuery
* @param timeForShow
*/
public static void separateByYear(Date startTime, Date endTime, SimpleDateFormat format, List<Map<String, String>> timeForQuery, List<String> timeForShow) {
if (timeForQuery == null) {
timeForQuery = new ArrayList<>();
}
if (timeForShow == null) {
timeForShow = new ArrayList<>();
}
Calendar start = Calendar.getInstance();
start.setTime(startTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);

while (start.getTimeInMillis() < end.getTimeInMillis()) {
HashMap<String, String> map = new HashMap<>();
String startStr = format.format(new Date(start.getTimeInMillis()));
map.put("startTime", startStr + " 00:00:00");
// region按自然月处理
start.set(Calendar.DAY_OF_YEAR, start.getActualMaximum(Calendar.DAY_OF_YEAR));
// endregion

if (start.getTimeInMillis() > end.getTimeInMillis()) {
start.setTimeInMillis(end.getTimeInMillis());
}
String yearEnd = format.format(new Date(start.getTimeInMillis()));
map.put("endTime", yearEnd + " 23:59:59");
timeForQuery.add(map);
timeForShow.add(yearEnd.substring(0, 4));
start.add(Calendar.DAY_OF_YEAR, 1);// 跳下一天
}
}

/**************************************************私有方法*********************************************************/
/**
* 近供分割季度的方法使用
*
* @param monthInQuarter List<Integer> quarter1 = Arrays.asList(0, 1, 2);
* @param quarter 1\2\3\4
* @param timeForQuery
* @param timeForShow
* @param format
* @param start
* @param end
*/
private static void handleQuarterOnce(List<Integer> monthInQuarter, int quarter, List<Map<String, String>> timeForQuery,
List<String> timeForShow, SimpleDateFormat format, Calendar start, Calendar end) {
HashMap<String, String> map = new HashMap<>();
String startStr = format.format(start.getTimeInMillis());
map.put("startTime", startStr + " 00:00:00");
// 设置为季度最后一天
// region 自然月的方式处理
start.set(Calendar.MONTH, monthInQuarter.get(2));
start.set(Calendar.DAY_OF_MONTH, start.getActualMaximum(Calendar.DAY_OF_MONTH));
// endregion

// 防止超出时间范围
if (start.getTimeInMillis() > end.getTimeInMillis()) {
start.setTimeInMillis(end.getTimeInMillis());
}
String quarterEnd = format.format(new Date(start.getTimeInMillis()));
map.put("endTime", quarterEnd + " 23:59:59");
timeForQuery.add(map);
timeForShow.add(quarterEnd.substring(0, 4) + "年" + quarter + "季度");
start.add(Calendar.DAY_OF_MONTH, 1);// +1天
}


/*****************************************************测试*********************************************************/

public static void main(String[] args) throws Exception {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DAY_OF_YEAR, 300);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// Date now = new Date();
// Date end = new Date(instance.getTimeInMillis());
Date now = format.parse("2017-09-26");
Date end = format.parse("2019-04-20");
// List<Map<String, String>> maps = separateByDay(new Date(), new Date(instance.getTimeInMillis()), format);
// System.out.println(maps);

ArrayList<Map<String, String>> timeForQuery = new ArrayList<>();
ArrayList<String> timeForShow = new ArrayList<>();
// separateByWeek(now, end, format, timeForQuery, timeForShow);
// separateByMonth(now, end, format, timeForQuery, timeForShow);
// assert (!ObjectUtils.hasLength(timeForQuery)):"OJBK";
// separateByQuarter(now, end, format, timeForQuery, timeForShow);
separateByYear(now, end, format, timeForQuery, timeForShow);
System.out.println(timeForQuery);
System.out.println(timeForShow);
}
}