在监控系统中,触犯规则并进行告警时,需要根据特定时间点(如每天的00:00-06:00,或工作日的22:00-24:00等)进行特殊处理。该需求包含以下2点:
设计一个存储格式来表示特定的时间段
需要判断某个时间是否在这个时间范围内
一般特定的时间段有以下特征:
指定月份列表,如: 1月、6月、12月等
指定每月的日期列表,如: 1日、10日、30日等
指定周几的列表,如: 周一、周三、周五等
具体某一天的时间段列表,如:09:00-18:00、22:00-24:00等
因为我们关注的是具体某天的时间段,因此月份、日期、周几等表示都简单的使用数据来表示,并不需要支持范围(当然也可以根据需要来进行修改)
我们使用Json格式来存储该时间范围的规则,这样就可以直接使用Json反序列化来转化为对象,然后调用方法判断某个时间是否在该范围内.
以下为java对象的属性:
private
List
<
Integer
> months;
* 每月的日期列表, 如1号,5号可表示为: 1,5
private
List
<
Integer
> daysOfMonth;
* 表示周几的列表
private
List
<
Integer
> daysOfWeek;
* 时间段表达式列表
* 单个表达式如: 10:00-20:00
private
List
<
String
> timeRanges;
复制代码
json格式的例子如下:
{"daysOfWeek":[1,3,6],"timeRanges":["09:00-18:00","22:00-24:00"]}
其中 months, daysOfMonth, daysOfWeek等属性值可以为空,表示忽略该属性值;
若这3个值都为空,则表示每天
这3个值的是and的关系,需要都满足,具体看下面的代码
由于我们需要支持多个时间段,因此需要使用json数组表示多个TimeSection, json格式如下:
[{"daysOfWeek":[1,2,3,4,5,6],"timeRanges":["20:00-24:00","00:00-09:00"]},{"daysOfWeek":[7],"timeRanges":["00:00-24:00"]}]
以上的区间满足了表示非工作时间的时间范围
通过如下代码转换成TimeSection对象(使用的是fastjson库)
List<TimeSection> timeSectionList = JSON.parseArray(json, TimeSection.class)
复制代码
使用Java8日期API进行日期判断
Java8表示日期时间的类有:
LocalDate: 表示日期(不含时间)
LocalTime: 表示时间(不含日期)
LocalDateTime: 同时包含日期和时间(LocalDateTime类中包含了2个属性LocalDate和LocalTime)
由于我们判断的时间段有日期和时间,因此使用LocalDateTime类
生成LocalDateTime:
LocalDateTime.now()
LocalDateTime.parse("2020-06-13 10:10:10", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
复制代码
使用LocalDateTime的方法来判断月份、日期、周几等的判断
// 获取月份
Month month = localDateTime.getMonth()
// 获取日期
int dayOfMonth = localDateTime.getDayOfMonth()
// 获取周几
DayOfWeek dayOfWeek = localDateTime.getDayOfWeek()
复制代码
其中Month,DayOfWeek为枚举类,通过.getValue()方法来获取int类型
month.getValue()
dayOfWeek.getValue()
复制代码
把具体的时间转换成它在一天中的毫秒数(long)
TemporalAccessor temporalAccessor = DateTimeFormatter.ofPattern(timePat).parse(timeValue);
temporalAccessor.getLong(ChronoField.MILLI_OF_DAY);
复制代码
其中:
Temporal: 是所有时间的超级接口
TemporalAccessor : 只提供只读版本的接口
通过temporalAccessor.getLong(ChronoField.MILLI_OF_DAY)获取某个时间点在一天中过去的毫秒数来做时间判断非常合适
startTime的毫秒数 < 当前时间的毫秒数 < endTime的毫秒数
复制代码
时间范围抽象
我们存储的时间范围为类似: 11:00-20:00 这样,而实际判断过程中我们需要将开始时间和结束时间提取出来, 因此需要抽象成如下的对象:
public static class TimeRange {
* 开始时间段
* HH:mm格式
private String timeStart;
* 结束时间段
* HH:mm格式
private String timeEnd;
复制代码
在做匹配的时候通过字符串处理进行timeStart和timeEnd的赋值
* 表示时间区间
@Data
public class TimeSection {
* 时间格式
private final transient static String TIME_PATTERN = "HH:mm";
private List<Integer> months;
* 每月的日期列表, 如1号,5号可表示为: 1,5
private List<Integer> daysOfMonth;
* 表示周几的列表
private List<Integer> daysOfWeek;
* 时间段表达式列表
* 单个表达式如: 10:00-20:00
private List<String> timeRanges;
@Data
@AllArgsConstructor
public static class TimeRange {
* 开始时间段
* HH:mm格式
private String timeStart;
* 结束时间段
* HH:mm格式
private String timeEnd;
* 某个时间是否在该时间范围内
* @param localDateTime
* @return
public boolean isIn(LocalDateTime localDateTime) {
String comparedCurrentHourMin = localDateTime.format(DateTimeFormatter.ofPattern(TIME_PATTERN));
long comparedMilliSeconds = getMillisOfDay(comparedCurrentHourMin, TIME_PATTERN);
long startMilliSeconds = getMillisOfDay(timeStart, TIME_PATTERN);
long endMilliSeconds = getMillisOfDay(timeEnd, TIME_PATTERN);
return comparedMilliSeconds >= startMilliSeconds && comparedMilliSeconds < endMilliSeconds;
* 获取某一个时间在一天中的毫秒数
* @param timeValue
* @param timePat
* @return
public static long getMillisOfDay(String timeValue, String timePat) {
return DateTimeFormatter.ofPattern(timePat).parse(timeValue).getLong(ChronoField.MILLI_OF_DAY);
* 获取时间范围对象列表
* @return
@JSONField(serialize = false)
public List<TimeRange> getRanges() {
List<TimeRange> resultList = new ArrayList<>(4);
if (CollectionUtils.isNotEmpty(timeRanges)) {
for (String timeSection : timeRanges) {
String[] array = timeSection.split("-");
if (array.length != 2) {
continue;
resultList.add(new TimeRange(array[0], array[1]));
return resultList;
* 某一个时间是否在范围内
* @param localDateTime
* @return
public boolean isCurrentIn(LocalDateTime localDateTime) {
if (CollectionUtils.isNotEmpty(months)) {
if (!months.contains(localDateTime.getMonth().getValue())) {
return false;
if (CollectionUtils.isNotEmpty(daysOfMonth)) {
if (!daysOfMonth.contains(localDateTime.getDayOfMonth())) {
return false;
if (CollectionUtils.isNotEmpty(daysOfWeek)) {
if (!daysOfWeek.contains(localDateTime.getDayOfWeek().getValue())) {
return false;
List<TimeRange> ranges = getRanges();
if (CollectionUtils.isNotEmpty(ranges)) {
for (TimeRange timeRange : ranges) {
if (timeRange.isIn(localDateTime)) {
return true;
return false;
复制代码
如何使用
String json = "{\"daysOfWeek\":[1,3,6],\"timeRanges\":[\"09:00-18:00\",\"22:00-24:00\"]}";
TimeSection timeSection = JSON.parseObject(json, TimeSection.class);
timeSection.isCurrentIn(LocalDateTime.now())
复制代码
- 25.2w
-
Python
ChatGPT
掘金·日新计划
- 5.1w
-
AI理性派思考者
ChatGPT
OpenAI
- 8.3w
-
程序员小北
ChatGPT
掘金·日新计划
- 2.9w
-
星期一研究室
Vue.js
掘金·金石计划
- 7.9w
-
jiangxia_1024
Swagger