import { range } from '@sapphire/utilities' ; import { allowedNum , cronTokens , partRegex , predefined , Time , tokensRegex , wildcardRegex } from './constants' ; see https export class Cron { public cron : string ; public normalized : string ; public minutes : number [ ] ; public hours : number [ ] ; public days : number [ ] ; public months : number [ ] ; public dows : number [ ] ; param cron public constructor ( cron ) { this . cron = cron . toLowerCase ( ) ; this . normalized = Cron . normalize ( this . cron ) ; [ this . minutes , this . hours , this . days , this . months , this . dows ] = Cron . parseString ( this . normalized ) ; } param outset param origin public next ( outset = new Date ( ) , origin = true ) { if ( ! this . days . includes ( outset . getUTCDate ( ) ) || ! this . months . includes ( outset . getUTCMonth ( ) + 1 ) || ! this . dows . includes ( outset . getUTCDay ( ) ) ) { return this . next ( new Date ( outset . getTime ( ) + Time . Day ) , false ) ; } if ( ! origin ) return new Date ( Date . UTC ( outset . getUTCFullYear ( ) , outset . getUTCMonth ( ) , outset . getUTCDate ( ) , this . hours [ 0 ] , this . minutes [ 0 ] ) ) ; const now = new Date ( outset . getTime ( ) + 60000 ) ; for ( const hour of this . hours ) { if ( hour < now . getUTCHours ( ) ) continue ; for ( const minute of this . minutes ) { if ( hour === now . getUTCHours ( ) && minute < now . getUTCMinutes ( ) ) continue ; return new Date ( Date . UTC ( outset . getUTCFullYear ( ) , outset . getUTCMonth ( ) , outset . getUTCDate ( ) , hour , minute ) ) ; } } return this . next ( new Date ( outset . getTime ( ) + Time . Day ) , false ) ; } param cron private static normalize ( cron ) { if ( Reflect . has ( predefined , cron ) ) return Reflect . get ( predefined , cron ) ; const now = new Date ( ) ; cron = cron . split ( ' ' ) . map ( ( val , i ) => val . replace ( wildcardRegex , ( match ) => { if ( match === 'h' ) return ( Math . floor ( Math . random ( ) * allowedNum [ i ] [ 1 ] ) + allowedNum [ i ] [ 0 ] ) . toString ( ) ; if ( match === '?' ) { switch ( i ) { case 0 : return now . getUTCMinutes ( ) . toString ( ) ; case 1 : return now . getUTCHours ( ) . toString ( ) ; case 2 : return now . getUTCDate ( ) . toString ( ) ; case 3 : return now . getUTCMonth ( ) . toString ( ) ; case 4 : return now . getUTCDay ( ) . toString ( ) ; } } return match ; } ) ) . join ( ' ' ) ; return cron . replace ( tokensRegex , ( match ) => String ( Reflect . get ( cronTokens , match ) ) ) ; } param cron private static parseString ( cron ) { const parts = cron . split ( ' ' ) ; if ( parts . length !== 5 ) throw new Error ( 'Invalid Cron Provided' ) ; return parts . map ( ( part , i ) => Cron . parsePart ( part , i ) ) ; } param cronPart param id private static parsePart ( cronPart , id ) { if ( cronPart . includes ( ',' ) ) { const res = [ ] ; for ( const part of cronPart . split ( ',' ) ) res . push ( ... Cron . parsePart ( part , id ) ) ; return [ ... new Set ( res ) ] . sort ( ( a , b ) => a - b ) ; } const [ , wild , minStr , maxStr , step ] = partRegex . exec ( cronPart ) ! ; let [ min , max ] = [ parseInt ( minStr , 10 ) , parseInt ( maxStr , 10 ) ] ; if ( wild ) [ min , max ] = allowedNum [ id ] ; else if ( ! max && ! step ) return [ min ] ; [ min , max ] = [ min , max || allowedNum [ id ] [ 1 ] ] . sort ( ( a , b ) => a - b ) ; return range ( min , max , parseInt ( step , 10 ) || 1 ) ; } }