不知道你在玩游戏的时候是否发现过以下情况:
(1)玩某些游戏的时候,发现他的排行榜并不是时时更新的,而是每半个小时,或者一个小时更新一次。
(2)又比如很火的王者荣耀手游,它的日常任务,都是每天5点进行更新。
那么,这些时间控制,到底是由谁控制得如此精准呢?原来,这些都是corn表达式的功劳。
【1】什么是cron表达式 ?
在了解之前,我们先举几个例子,看看corn表达式长什么样子
cron="0 */5 * * * ?"
cron="0 30 8,10,12,14,16 * * ?"
cron="0 0 */1 * * ?"
可以看到,corn表达式就是:由若干数字、空格、符号按一定的规则,组成的一组字符串,从而表达时间的信息。
好像和正则表达式有点类似哈,都是一个字符串表示一些信息。 但比正则表达式要简单很多,在刚接触正则的时候,我们形容他是鬼画符(“鬼才知道你写什么”),道士的符号只有道士能看懂。而cron表达式看完下面内容你半小时就懂了,并且会写了
【2】cron 表达式标准结构
那么这个规则究竟是怎样的呢?我们继续往下了解。
从上面的例子中,我们可以看到,这个字符串被5个空格分成了6个部分。
假设我们以ABCDEF举例,它的标准格式为:"A B C D E F" 。
【3】cron 表达式具体含义
这个ABCDEF到底是什么意思呢?接下来是画重点的地方,认真看:
A表示秒,B表示分,C表示小时,D表示日,E表示月,F表示星期
故:"A B C D E F" --> "秒 分 时 日 月 星期"
这里也有带年的,星期后面就是年,但这个年可加可不加,加上就是 "A B C D E F G" 格式。
为什么通常不加年呢?你见过哪些程序会指定在哪一年跑的?或者每几年跑一次的?
所以,年的实用性不大,加上又为了书写方便,规则上就干脆省掉了!当然加上也没错!
【4】前例解释
cron="0 */5 * * * ?"
我们可以了解到:
它的秒位为0,表示每个0秒,分位为*/5,意思是每5分钟。所以总的来说就是每5分钟(每5分0秒)时执行一次;
cron="0 30 8,10,12,14,16 * * ?"
它的秒位为0,分位为30,时位为一串集合,则它的意思是每天 8点半、10点半、12点半、14点半、16点半各执行一次;
cron="0 0 */1 * * ?"
我想你已经知道它的意思了,没错,它就是每个小时整点(整点0分0秒)执行一次。
从上面三个列子中,我想你已经了解了orcn表达式的大致意思了:数字则表示具体时间,* 则表示任意时间,*/x 则表示每多少时间,还可以用集合表示具体的几个时间点。
【5】更多例子
(5.1)用短横线(-)表示时间段:
比如:我们的上班时间朝9晚6为(周一到周五的早上9点到晚上6点),则cron表达式为:0 0 9-18 * * MON-FRI
星期一到星期天的英文为:Monday,Tuesday、Wednesday、Thursday、Friday、Saturday 、Sunday ,取前三个字母,然后大写表示星期。
(5.2)用L表示最后,L是单词Last(最后的)的首字母:
比如:假设每个最后一天,下午2点发工资的时间,则cron表达式为:0 0 14 L * ?
注:如果没有具体说明是星期几,通常用问号代替。
其他不常用的功能 #,W 这里不就介绍了,想知道的朋友可以自己去查阅资料,因为在实际工作中,用得极少。
而我们最常用的就是在某个时间点,或者某些时间点执行程序,也就是【4】中的前例解释。
【6】cron 表达式用途
如前言所示,cron 表达式最主要的就是在程序中做一些定时任务,比如某些系统的报表数据,某些游戏的排行榜,由于这些数据量实时统计非常消耗程序性能,所以就每隔一段时间,通过自动任务跑一次,这样可以极大的提升用户浏览体验,要是在游戏里,还可以增加一种神秘感。
另外,某些具体点的数据拉取,比如你如果从事平台对接工作,要从某些平台下载你的订单,那么肯定是每隔多久抓一次。
目前常见的有2种Cron表达式,Linux Java,这里不多做扩展, 感兴趣的同学可以自行学习, 我这里做了一种Java的生成,废话不多说, 上图
先上index整合文件
import React, { useState, useEffect } from 'react';
import { Tabs, Button } from 'antd';
import MinutePane from './MinutePane';
import HourPane from './HourPane';
import DayPane from './DayPane';
import MonthPane from './MonthPane';
import WeekPane from './WeekPane';
import YearPane from './YearPane';
import { minuteRegex, hourRegex, dayRegex, monthRegex, weekRegex, yearRegex } from './cron-regex';
import styles from './index.less';
const { TabPane } = Tabs;
const tabPaneStyle = { paddingLeft: 10, paddingBottom: 8, marginTop: -10 };
const getTabTitle = (text) => <div style={{ width: 50, textAlign: 'center' }}>{text}</div>;
function Cron(props) {
const { style, footerStyle, value, onOk } = props;
const [currentTab, setCurrentTab] = useState('2');
const [second, setSecond] = useState('0');
const [minute, setMinute] = useState('0');
const [hour, setHour] = useState('0');
const [day, setDay] = useState('*');
const [month, setMonth] = useState('*');
const [week, setWeek] = useState('?');
const [year, setYear] = useState('*');
const onParse = () => {
if (value) {
try {
let [secondVal, minuteValue, hourVal, dayVal, monthVal, weekVal, yearVal] = value.split(' ');
secondVal = 0;
minuteValue = minuteRegex.test(minuteValue) ? minuteValue : '0';
hourVal = hourRegex.test(hourVal) ? hourVal : '0';
dayVal = dayRegex.test(dayVal) ? dayVal : '*';
monthVal = monthRegex.test(monthVal) ? monthVal : '*';
weekVal = weekRegex.test(weekVal) ? weekVal : '?';
weekVal = dayVal !== '?' ? '?' : weekVal;
yearVal = yearRegex.test(yearVal) ? yearVal : '*';
setSecond(secondVal);
setMinute(minuteValue);
setHour(hourVal);
setDay(dayVal);
setMonth(monthVal);
setWeek(weekVal);
setYear(yearVal);
} catch (error) {
setSecond('0');
setMinute('0');
setHour('0');
setDay('*');
setMonth('*');
setWeek('?');
setYear('*');
}
}
};
useEffect(() => {
onParse();
}, [value]);
const onGenerate = () => {
if (onOk) {
onOk([second, minute, hour, day, month, week, year].join(' '));
}
};
const onChangeDay = (v) => {
setDay(v);
if (v !== '?') {
setWeek('?');
}
};
const onChangeWeek = (v) => {
setWeek(v);
if (v !== '?') {
setDay('?');
}
};
return (
<div
className={styles.root}
style={{
borderRadius: '4px',
outline: 'none',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
...style,
}}
>
<Tabs tabBarGutter={0} animated destroyInactiveTabPane activeKey={currentTab} onChange={setCurrentTab}>
<TabPane tab={getTabTitle('分')} key="2" style={tabPaneStyle}>
<MinutePane value={minute} onChange={setMinute} />
</TabPane>
<TabPane tab={getTabTitle('时')} key="3" style={tabPaneStyle}>
<HourPane value={hour} onChange={setHour} />
</TabPane>
<TabPane tab={getTabTitle('日')} key="4" style={tabPaneStyle}>
<DayPane value={day} onChange={onChangeDay} />
</TabPane>
<TabPane tab={getTabTitle('月')} key="5" style={tabPaneStyle}>
<MonthPane value={month} onChange={setMonth} />
</TabPane>
<TabPane tab={getTabTitle('周')} key="6" style={tabPaneStyle}>
<WeekPane value={week} onChange={onChangeWeek} />
</TabPane>
<TabPane tab={getTabTitle('年')} key="7" style={tabPaneStyle}>
<YearPane value={year} onChange={setYear} />
</TabPane>
</Tabs>
<div style={{ borderTop: '1px solid #e8e8e8', padding: 10, textAlign: 'right', ...footerStyle }}>
<Button type="primary" onClick={onGenerate}>
生成
</Button>
</div>
</div>
);
}
export default Cron;
cron-regex cronValue的正则取值
/* eslint-disable max-len */
export const secondRegex = /^\*$|(^([0-9]|[1-5][0-9])-([0-9]|[1-5][0-9])$)|(^([0-9]|[1-5][0-9])\/\d+$)|(^(([0-9]|[1-5][0-9]),)*([0-9]|[1-5][0-9])$)/;
export const minuteRegex = /^\*$|(^([0-9]|[1-5][0-9])-([0-9]|[1-5][0-9])$)|(^([0-9]|[1-5][0-9])\/\d+$)|(^(([0-9]|[1-5][0-9]),)*([0-9]|[1-5][0-9])$)/;
export const hourRegex = /(^\*$)|(^([0-9]|(1[0-9])|(2[0-3]))-([0-9]|(1[0-9])|(2[0-3]))$)|(^([0-9]|(1[0-9])|(2[0-3]))\/\d+$)|(^(([0-9]|(1[0-9])|(2[0-3])),)*([0-9]|(1[0-9])|(2[0-3]))$)/;
export const dayRegex = /^\*$|^\?$|(^([1-9]|[1-2][0-9]|3[0-1])-([1-9]|[1-2][0-9]|3[0-1])$)|(^([1-9]|[1-2][0-9]|3[0-1])\/\d+$)|(^(([1-9]|[1-2][0-9]|3[0-1]),)*([1-9]|[1-2][0-9]|3[0-1])$)/;
export const monthRegex = /^\*$|(^([1-9]|1[0-2])-([1-9]|1[0-2])$)|(^([1-9]|1[0-2])\/\d+$)|(^(([1-9]|1[0-2]),)*([1-9]|1[0-2])$)/;
export const weekRegex = /^\*$|^\?$|(^(SUN|MON|TUE|WED|THU|FRI|SAT)-(SUN|MON|TUE|WED|THU|FRI|SAT)$)|(^(SUN|MON|TUE|WED|THU|FRI|SAT)#\d+$)|(^(SUN|MON|TUE|WED|THU|FRI|SAT)L$)|(^((SUN|MON|TUE|WED|THU|FRI|SAT),)*(SUN|MON|TUE|WED|THU|FRI|SAT)$)/;
export const yearRegex = /^\*$|^\?$|(^(2019|20[2-5][0-9]|206[0-6])-(2019|20[2-5][0-9]|206[0-6])$)|(^(2019|20[2-5][0-9]|206[0-6])\/\d+$)|(^((2019|20[2-5][0-9]|206[0-6]),)*(2019|20[2-5][0-9]|206[0-6])$)/;
进入其中一个模块(我这里秒分时日月周年都一样 ,随机以月份month为例)
// index布局页面
import { Radio } from 'antd';
import React from 'react';
import InputFromInterval from './InputFromInterval';
import InputFromTo from './InputFromTo';
import InputSpecified from './InputSpecified';
const radioStyle = { display: 'block', lineHeight: '32px' };
function MonthPane(props) {
const { value, onChange } = props;
let currentRadio = 0;
if (value === '*') {
currentRadio = 0;
} else if (value.indexOf('-') > -1) {
currentRadio = 1;
} else if (value.indexOf('/') > -1) {
currentRadio = 2;
} else {
currentRadio = 3;
}
const onChangeRadio = (e) => {
const valueType = e.target.value;
const defaultValues = ['*', '1-1', '1/1', '1'];
onChange(defaultValues[valueType]);
};
return (
<Radio.Group style={{ width: '100%' }} value={currentRadio} onChange={onChangeRadio}>
<Radio style={radioStyle} value={0}>
每一月
</Radio>
<Radio style={radioStyle} value={1}>
<InputFromTo disabled={currentRadio !== 1} value={value} onChange={onChange} />
</Radio>
<Radio style={radioStyle} value={2}>
<InputFromInterval disabled={currentRadio !== 2} value={value} onChange={onChange} />
</Radio>
<Radio style={radioStyle} value={3}>
<InputSpecified disabled={currentRadio !== 3} value={value} onChange={onChange} />
</Radio>
</Radio.Group>
);
}
export default MonthPane;
从X月到Y月,每月执行一次
import React from 'react';
import { InputNumber } from 'antd';
function InputFromTo(props) {
const { disabled, value, onChange } = props;
let from = 0;
let to = 0;
if (!disabled) {
[from, to] = value.split('-').map((v) => parseInt(v, 10));
}
const onChangeFrom = (v) => onChange(`${v || 0}-${to}`);
const onChangeTo = (v) => onChange(`${from}-${v || 0}`);
return (
<React.Fragment>
从
<InputNumber
disabled={disabled}
min={1}
max={12}
value={from}
size="small"
onChange={onChangeFrom}
style={{ width: 100 }}
/>
-
<InputNumber
disabled={disabled}
min={1}
max={12}
value={to}
size="small"
onChange={onChangeTo}
style={{ width: 100 }}
/>
月,每月执行一次
</React.Fragment>
);
}
export default InputFromTo;
从X月开始,每N个月执行一次
import React from 'react';
import { InputNumber } from 'antd';
function InputFromInterval(props) {
const { disabled, value, onChange } = props;
let from = 0;
let interval = 0;
if (!disabled) {
[from, interval] = value.split('/').map((v) => parseInt(v, 10));
}
const onChangeFrom = (v) => onChange(`${v || 0}/${interval}`);
const onChangeInterval = (v) => onChange(`${from}/${v || 0}`);
return (
<React.Fragment>
从
<InputNumber
disabled={disabled}
min={1}
max={12}
value={from}
size="small"
onChange={onChangeFrom}
style={{ width: 100 }}
/>
月开始, 每
<InputNumber
disabled={disabled}
min={1}
max={12}
value={interval}
size="small"
onChange={onChangeInterval}
style={{ width: 100 }}
/>
月执行一次
</React.Fragment>
);
}
export default InputFromInterval;
最后,指定某个月份执行
import React, { useMemo } from 'react';
import { Checkbox, Row, Col } from 'antd';
function InputSpecified(props) {
const { disabled, value, onChange } = props;
let selected = [];
if (!disabled) {
selected = value.split(',').map((v) => parseInt(v, 10));
}
const onChangeSelected = (v) => onChange(v.length === 0 ? '1' : v.join(','));
const checkList = useMemo(() => {
const checks = [];
for (let i = 1; i < 13; i++) {
checks.push(
<Col key={i} span={4}>
<Checkbox disabled={disabled} value={i}>
{i}
</Checkbox>
</Col>,
);
}
return checks;
}, [disabled]);
return (
<React.Fragment>
指定
<br />
<Checkbox.Group style={{ width: '100%' }} value={selected} onChange={onChangeSelected}>
<Row>{checkList}</Row>
</Checkbox.Group>
</React.Fragment>
);
}
export default InputSpecified;
如有不妥,请指教,谢谢