algorithm - manualmente - instalar crontab ubuntu
Calcule cuándo se ejecutará un trabajo cron y la próxima vez (8)
Básicamente, esto es lo contrario de verificar si la hora actual se ajusta a las condiciones. algo así como:
//Totaly made up language
next = getTimeNow();
next.addMinutes(1) //so that next is never now
done = false;
while (!done) {
if (cron.minute != ''*'' && next.minute != cron.minute) {
if (next.minute > cron.minute) {
next.addHours(1);
}
next.minute = cron.minute;
}
if (cron.hour != ''*'' && next.hour != cron.hour) {
if (next.hour > cron.hour) {
next.hour = cron.hour;
next.addDays(1);
next.minute = 0;
continue;
}
next.hour = cron.hour;
next.minute = 0;
continue;
}
if (cron.weekday != ''*'' && next.weekday != cron.weekday) {
deltaDays = cron.weekday - next.weekday //assume weekday is 0=sun, 1 ... 6=sat
if (deltaDays < 0) { deltaDays+=7; }
next.addDays(deltaDays);
next.hour = 0;
next.minute = 0;
continue;
}
if (cron.day != ''*'' && next.day != cron.day) {
if (next.day > cron.day || !next.month.hasDay(cron.day)) {
next.addMonths(1);
next.day = 1; //assume days 1..31
next.hour = 0;
next.minute = 0;
continue;
}
next.day = cron.day
next.hour = 0;
next.minute = 0;
continue;
}
if (cron.month != ''*'' && next.month != cron.month) {
if (next.month > cron.month) {
next.addMonths(12-next.month+cron.month)
next.day = 1; //assume days 1..31
next.hour = 0;
next.minute = 0;
continue;
}
next.month = cron.month;
next.day = 1;
next.hour = 0;
next.minute = 0;
continue;
}
done = true;
}
Podría haber escrito eso un poco al revés. También puede ser mucho más corto si en cada main si en vez de hacer el check mayor, simplemente incrementa el grado de tiempo actual en uno y establece las calificaciones de tiempo menor en 0, luego continúa; sin embargo, entonces estarás repitiendo mucho más. Al igual que:
//Shorter more loopy version
next = getTimeNow().addMinutes(1);
while (true) {
if (cron.month != ''*'' && next.month != cron.month) {
next.addMonths(1);
next.day = 1;
next.hour = 0;
next.minute = 0;
continue;
}
if (cron.day != ''*'' && next.day != cron.day) {
next.addDays(1);
next.hour = 0;
next.minute = 0;
continue;
}
if (cron.weekday != ''*'' && next.weekday != cron.weekday) {
next.addDays(1);
next.hour = 0;
next.minute = 0;
continue;
}
if (cron.hour != ''*'' && next.hour != cron.hour) {
next.addHours(1);
next.minute = 0;
continue;
}
if (cron.minute != ''*'' && next.minute != cron.minute) {
next.addMinutes(1);
continue;
}
break;
}
Tengo una cron "definición de tiempo"
1 * * * * (every hour at xx:01)
2 5 * * * (every day at 05:02)
0 4 3 * * (every third day of the month at 04:00)
* 2 * * 5 (every minute between 02:00 and 02:59 on fridays)
Y tengo una marca de tiempo de Unix.
¿Existe una forma obvia de encontrar (calcular) la próxima vez (después de esa marca de tiempo dada) que se debe ejecutar el trabajo?
Estoy usando PHP, pero el problema debería ser bastante independiente del lenguaje.
[Actualizar]
La clase " PHP Cron Parser " (sugerida por Ray) calcula el ÚLTIMO tiempo que se suponía que se debía ejecutar el trabajo CRON, no la próxima vez.
Para hacerlo más fácil: en mi caso, los parámetros cron time son solo números absolutos, únicos o "*". No hay rangos de tiempo ni intervalos "* / 5".
Mira esto :
Puede calcular la próxima vez que se supone que se debe ejecutar un trabajo programado en función de las definiciones cron dadas.
Para cualquier persona interesada, aquí está mi implementación final de PHP, que es muy similar al pseudo código dlamblin:
class myMiniDate {
var $myTimestamp;
static private $dateComponent = array(
''second'' => ''s'',
''minute'' => ''i'',
''hour'' => ''G'',
''day'' => ''j'',
''month'' => ''n'',
''year'' => ''Y'',
''dow'' => ''w'',
''timestamp'' => ''U''
);
static private $weekday = array(
1 => ''monday'',
2 => ''tuesday'',
3 => ''wednesday'',
4 => ''thursday'',
5 => ''friday'',
6 => ''saturday'',
0 => ''sunday''
);
function __construct($ts = NULL) { $this->myTimestamp = is_null($ts)?time():$ts; }
function __set($var, $value) {
list($c[''second''], $c[''minute''], $c[''hour''], $c[''day''], $c[''month''], $c[''year''], $c[''dow'']) = explode('' '', date(''s i G j n Y w'', $this->myTimestamp));
switch ($var) {
case ''dow'':
$this->myTimestamp = strtotime(self::$weekday[$value], $this->myTimestamp);
break;
case ''timestamp'':
$this->myTimestamp = $value;
break;
default:
$c[$var] = $value;
$this->myTimestamp = mktime($c[''hour''], $c[''minute''], $c[''second''], $c[''month''], $c[''day''], $c[''year'']);
}
}
function __get($var) {
return date(self::$dateComponent[$var], $this->myTimestamp);
}
function modify($how) { return $this->myTimestamp = strtotime($how, $this->myTimestamp); }
}
$cron = new myMiniDate(time() + 60);
$cron->second = 0;
$done = 0;
echo date(''Y-m-d H:i:s'') . ''<hr>'' . date(''Y-m-d H:i:s'', $cron->timestamp) . ''<hr>'';
$Job = array(
''Minute'' => 5,
''Hour'' => 3,
''Day'' => 13,
''Month'' => null,
''DOW'' => 5,
);
while ($done < 100) {
if (!is_null($Job[''Minute'']) && ($cron->minute != $Job[''Minute''])) {
if ($cron->minute > $Job[''Minute'']) {
$cron->modify(''+1 hour'');
}
$cron->minute = $Job[''Minute''];
}
if (!is_null($Job[''Hour'']) && ($cron->hour != $Job[''Hour''])) {
if ($cron->hour > $Job[''Hour'']) {
$cron->modify(''+1 day'');
}
$cron->hour = $Job[''Hour''];
$cron->minute = 0;
}
if (!is_null($Job[''DOW'']) && ($cron->dow != $Job[''DOW''])) {
$cron->dow = $Job[''DOW''];
$cron->hour = 0;
$cron->minute = 0;
}
if (!is_null($Job[''Day'']) && ($cron->day != $Job[''Day''])) {
if ($cron->day > $Job[''Day'']) {
$cron->modify(''+1 month'');
}
$cron->day = $Job[''Day''];
$cron->hour = 0;
$cron->minute = 0;
}
if (!is_null($Job[''Month'']) && ($cron->month != $Job[''Month''])) {
if ($cron->month > $Job[''Month'']) {
$cron->modify(''+1 year'');
}
$cron->month = $Job[''Month''];
$cron->day = 1;
$cron->hour = 0;
$cron->minute = 0;
}
$done = (is_null($Job[''Minute'']) || $Job[''Minute''] == $cron->minute) &&
(is_null($Job[''Hour'']) || $Job[''Hour''] == $cron->hour) &&
(is_null($Job[''Day'']) || $Job[''Day''] == $cron->day) &&
(is_null($Job[''Month'']) || $Job[''Month''] == $cron->month) &&
(is_null($Job[''DOW'']) || $Job[''DOW''] == $cron->dow)?100:($done+1);
}
echo date(''Y-m-d H:i:s'', $cron->timestamp) . ''<hr>'';
Aquí hay un proyecto PHP que se basa en el código psuedo de dlamblin.
Puede calcular la siguiente fecha de ejecución de una expresión CRON, la fecha de ejecución anterior de una expresión CRON, y determinar si una expresión CRON coincide con un tiempo determinado. Puede saltear Este analizador de expresiones CRON implementa completamente CRON:
- Incrementos de rangos (ej. * / 12, 3-59 / 15)
- Intervalos (por ejemplo, 1-4, lunes-viernes, enero-marzo)
- Listas (ej. 1,2,3 | JAN, MAR, DEC)
- Último día de un mes (p. Ej. L)
- Último día de la semana de un mes (por ejemplo, 5L)
- Enésimo día de la semana de un mes (por ejemplo, 3 # 2, 1 # 1, MON # 4)
- Día de la semana más cercano a un día determinado del mes (por ejemplo, 15W, 1W, 30W)
https://github.com/mtdowling/cron-expression
Uso (PHP 5.3+):
<?php
// Works with predefined scheduling definitions
$cron = Cron/CronExpression::factory(''@daily'');
$cron->isDue();
$cron->getNextRunDate();
$cron->getPreviousRunDate();
// Works with complex expressions
$cron = Cron/CronExpression::factory(''15 2,6-12 */15 1 2-5'');
$cron->getNextRunDate();
Usa esta función:
function parse_crontab($time, $crontab)
{$time=explode('' '', date(''i G j n w'', strtotime($time)));
$crontab=explode('' '', $crontab);
foreach ($crontab as $k=>&$v)
{$v=explode('','', $v);
foreach ($v as &$v1)
{$v1=preg_replace(array(''/^/*$/'', ''/^/d+$/'', ''/^(/d+)/-(/d+)$/'', ''/^/*//(/d+)$/''),
array(''true'', ''"''.$time[$k].''"==="/0"'', ''(/1<=''.$time[$k].'' and ''.$time[$k].''<=/2)'', $time[$k].''%/1===0''),
$v1
);
}
$v=''(''.implode('' or '', $v).'')'';
}
$crontab=implode('' and '', $crontab);
return eval(''return ''.$crontab.'';'');
}
var_export(parse_crontab(''2011-05-04 02:08:03'', ''*/2,3-5,9 2 3-5 */2 *''));
var_export(parse_crontab(''2011-05-04 02:08:03'', ''*/8 */2 */4 */5 *''));
Editar Quizás esto sea más legible:
<?php
function parse_crontab($frequency=''* * * * *'', $time=false) {
$time = is_string($time) ? strtotime($time) : time();
$time = explode('' '', date(''i G j n w'', $time));
$crontab = explode('' '', $frequency);
foreach ($crontab as $k => &$v) {
$v = explode('','', $v);
$regexps = array(
''/^/*$/'', # every
''/^/d+$/'', # digit
''/^(/d+)/-(/d+)$/'', # range
''/^/*//(/d+)$/'' # every digit
);
$content = array(
"true", # every
"{$time[$k]} === 0", # digit
"($1 <= {$time[$k]} && {$time[$k]} <= $2)", # range
"{$time[$k]} % $1 === 0" # every digit
);
foreach ($v as &$v1)
$v1 = preg_replace($regexps, $content, $v1);
$v = ''(''.implode('' || '', $v).'')'';
}
$crontab = implode('' && '', $crontab);
return eval("return {$crontab};");
}
Uso:
<?php
if (parse_crontab(''*/5 2 * * *'')) {
// should run cron
} else {
// should not run cron
}
Creado javascript API para calcular el próximo tiempo de ejecución basado en la idea de @dlamblin. Admite segundos y años. Aún no he podido probarlo del todo, así que espere errores, pero avíseme si encuentra alguno.
Enlace de repositorio: https://bitbucket.org/nevity/cronner
Gracias por publicar este código. Definitivamente me ayudó, incluso 6 años después.
Al tratar de implementar encontré un pequeño error.
date(''i G jn w'', $time)
devuelve un 0 entero acolchado para los minutos.
Más adelante en el código, hace un módulo en ese 0 entero acolchado. PHP no parece manejar esto como se esperaba.
$ php
<?php
print 8 % 5 . "/n";
print 08 % 5 . "/n";
?>
3
0
Como puede ver, el 08 % 5
devuelve 0, mientras que el 8 % 5
devuelve el 3. esperado. No pude encontrar una opción no acolchada para el comando de fecha. Intenté jugar con la línea {$time[$k]} % $1 === 0
(como cambiar {$time[$k]}
a ({$time[$k]}+0)
, pero no pude obtener para soltar el relleno 0 durante el módulo.
Entonces, terminé simplemente cambiando el valor original devuelto por la función de fecha y eliminé el 0 ejecutando $time[0] = $time[0] + 0;
.
Aquí está mi prueba.
<?php
function parse_crontab($frequency=''* * * * *'', $time=false) {
$time = is_string($time) ? strtotime($time) : time();
$time = explode('' '', date(''i G j n w'', $time));
$time[0] = $time[0] + 0;
$crontab = explode('' '', $frequency);
foreach ($crontab as $k => &$v) {
$v = explode('','', $v);
$regexps = array(
''/^/*$/'', # every
''/^/d+$/'', # digit
''/^(/d+)/-(/d+)$/'', # range
''/^/*//(/d+)$/'' # every digit
);
$content = array(
"true", # every
"{$time[$k]} === $0", # digit
"($1 <= {$time[$k]} && {$time[$k]} <= $2)", # range
"{$time[$k]} % $1 === 0" # every digit
);
foreach ($v as &$v1)
$v1 = preg_replace($regexps, $content, $v1);
$v = ''(''.implode('' || '', $v).'')'';
}
$crontab = implode('' && '', $crontab);
return eval("return {$crontab};");
}
for($i=0; $i<24; $i++) {
for($j=0; $j<60; $j++) {
$date=sprintf("%d:%02d",$i,$j);
if (parse_crontab(''*/5 * * * *'',$date)) {
print "$date yes/n";
} else {
print "$date no/n";
}
}
}
?>
Mi respuesta no es única Solo una réplica de @BlaM responde escrita en Java porque la fecha y hora de PHP son un poco diferentes de Java.
Este programa asume que la expresión CRON es simple. Solo puede contener dígitos o *.
Minute = 0-60
Hour = 0-23
Day = 1-31
MONTH = 1-12 where 1 = January.
WEEKDAY = 1-7 where 1 = Sunday.
Código:
package main;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CronPredict
{
public static void main(String[] args)
{
String cronExpression = "5 3 27 3 3 ls -la > a.txt";
CronPredict cronPredict = new CronPredict();
String[] parsed = cronPredict.parseCronExpression(cronExpression);
System.out.println(cronPredict.getNextExecution(parsed).getTime().toString());
}
//This method takes a cron string and separates entities like minutes, hours, etc.
public String[] parseCronExpression(String cronExpression)
{
String[] parsedExpression = null;
String cronPattern = "^([0-9]|[1-5][0-9]|//*)//s([0-9]|1[0-9]|2[0-3]|//*)//s"
+ "([1-9]|[1-2][0-9]|3[0-1]|//*)//s([1-9]|1[0-2]|//*)//s"
+ "([1-7]|//*)//s(.*)$";
Pattern cronRegex = Pattern.compile(cronPattern);
Matcher matcher = cronRegex.matcher(cronExpression);
if(matcher.matches())
{
String minute = matcher.group(1);
String hour = matcher.group(2);
String day = matcher.group(3);
String month = matcher.group(4);
String weekday = matcher.group(5);
String command = matcher.group(6);
parsedExpression = new String[6];
parsedExpression[0] = minute;
parsedExpression[1] = hour;
parsedExpression[2] = day;
//since java''s month start''s from 0 as opposed to PHP which starts from 1.
parsedExpression[3] = month.equals("*") ? month : (Integer.parseInt(month) - 1) + "";
parsedExpression[4] = weekday;
parsedExpression[5] = command;
}
return parsedExpression;
}
public Calendar getNextExecution(String[] job)
{
Calendar cron = Calendar.getInstance();
cron.add(Calendar.MINUTE, 1);
cron.set(Calendar.MILLISECOND, 0);
cron.set(Calendar.SECOND, 0);
int done = 0;
//Loop because some dates are not valid.
//e.g. March 29 which is a Friday may never come for atleast next 1000 years.
//We do not want to keep looping. Also it protects against invalid dates such as feb 30.
while(done < 100)
{
if(!job[0].equals("*") && cron.get(Calendar.MINUTE) != Integer.parseInt(job[0]))
{
if(cron.get(Calendar.MINUTE) > Integer.parseInt(job[0]))
{
cron.add(Calendar.HOUR_OF_DAY, 1);
}
cron.set(Calendar.MINUTE, Integer.parseInt(job[0]));
}
if(!job[1].equals("*") && cron.get(Calendar.HOUR_OF_DAY) != Integer.parseInt(job[1]))
{
if(cron.get(Calendar.HOUR_OF_DAY) > Integer.parseInt(job[1]))
{
cron.add(Calendar.DAY_OF_MONTH, 1);
}
cron.set(Calendar.HOUR_OF_DAY, Integer.parseInt(job[1]));
cron.set(Calendar.MINUTE, 0);
}
if(!job[4].equals("*") && cron.get(Calendar.DAY_OF_WEEK) != Integer.parseInt(job[4]))
{
Date previousDate = cron.getTime();
cron.set(Calendar.DAY_OF_WEEK, Integer.parseInt(job[4]));
Date newDate = cron.getTime();
if(newDate.before(previousDate))
{
cron.add(Calendar.WEEK_OF_MONTH, 1);
}
cron.set(Calendar.HOUR_OF_DAY, 0);
cron.set(Calendar.MINUTE, 0);
}
if(!job[2].equals("*") && cron.get(Calendar.DAY_OF_MONTH) != Integer.parseInt(job[2]))
{
if(cron.get(Calendar.DAY_OF_MONTH) > Integer.parseInt(job[2]))
{
cron.add(Calendar.MONTH, 1);
}
cron.set(Calendar.DAY_OF_MONTH, Integer.parseInt(job[2]));
cron.set(Calendar.HOUR_OF_DAY, 0);
cron.set(Calendar.MINUTE, 0);
}
if(!job[3].equals("*") && cron.get(Calendar.MONTH) != Integer.parseInt(job[3]))
{
if(cron.get(Calendar.MONTH) > Integer.parseInt(job[3]))
{
cron.add(Calendar.YEAR, 1);
}
cron.set(Calendar.MONTH, Integer.parseInt(job[3]));
cron.set(Calendar.DAY_OF_MONTH, 1);
cron.set(Calendar.HOUR_OF_DAY, 0);
cron.set(Calendar.MINUTE, 0);
}
done = (job[0].equals("*") || cron.get(Calendar.MINUTE) == Integer.parseInt(job[0])) &&
(job[1].equals("*") || cron.get(Calendar.HOUR_OF_DAY) == Integer.parseInt(job[1])) &&
(job[2].equals("*") || cron.get(Calendar.DAY_OF_MONTH) == Integer.parseInt(job[2])) &&
(job[3].equals("*") || cron.get(Calendar.MONTH) == Integer.parseInt(job[3])) &&
(job[4].equals("*") || cron.get(Calendar.DAY_OF_WEEK) == Integer.parseInt(job[4])) ? 100 : (done + 1);
}
return cron;
}
}