Last active
May 7, 2023 18:53
-
-
Save lukeed/1bbdde31b5861c95f44c3a49a88f5213 to your computer and use it in GitHub Desktop.
cron syntax & human readable output — https://t.co/CBThaezwzC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://crontab.guru/ | |
// https://www.typescriptlang.org/play?ts=4.9.5#code/C4TwDgpgBAwgSgeQHJQLxQAYBIDe9kB0AogB5gBOEAzlQJYD2AdgL5S75LFmU0Mtt5EnUhWp0mrdkK6jeEgRxk9xLDAG4AUAHotUPVAB6Afg0bQkKABVqwAIxpY0gBIBXALYBDRgB4ARAGYABigATigAKgiI3wA+TR19QxMzcGhrKmAAJgdFV08fX0iwyJLY+N19Y1NzNJt-HOd3Lz9wrXriqPCy7Qq9KpSLdOAAFgbCPObCtoitUZLouJ7E-pqrGwBWMc4Jgtb6+dLFhMrk1aGANi2CHZaZ-c7u477T1LWMgHYrm99bTdbsg6PXpJaqvIYADi+TV2UXuXSOwJWYJsYXQuWhtxK00O5WWL0GNlswTRjXymKg+y0AKBeNBBIytnsJPGGMKsJm2WGNJOdNqDOyzO2rOCmUpAOxs25z15bzs9UF11ZYVFHLuUC5CNpAz5dlGCu+rV+M0ukX2UpB2tlRv1rIOFIWuJ5SwAtK6AMYuYCu52mAAmEDdABsPJQoIwPG5qGAPG7oBwoDgNHoIGR6ORgFBViJlHwHABycJ5qAAHzD7gARhByCXMK1cIwK1XmOpTMnU+nM68bpYoCngBBGL6qI5kDEHEnElZeyR+4Ph9gcLRGAAzKtQACykkXK7XTi3S9X1YAIpuBAe1+vT7hz8eAOrNieTqBGKy0SPeJwAGnXY77A6HmDXju1Y9n+c5QBk5BLgA5g+T6JC+R4eCA3iXp+UAnuhR63r+M7-vOQGHhh06zgBkEwXB8EIcRYEAb4vhQI+VH6C+lhMcxUAAFyATglisEwAhHpRHHcYwEAAG5Vuxk6iRJUlUbJknkJoE4JN6jETmcb4QB+9AuOQ6HrkunoQGO6BMUZDb9iR+FQAWRbSS+Th6dWtHDvZegvnmcnkCAUBuMZ-ZFgk8wAPpQOFoXSdxzn6TZ4ELjeUBOPFAENm4lbkM2z6YD5fkBVZ0D0MuUAABYuQIe4YFAIXJRF9VRU+MUVW5tZaIRu6pcO6WZdlL4YHl-mBUVJWDbgRBKShX52eV+l5jE2W1a0KWRRF0Vhj5THcZZJldTxSXrntPVNtVjnJS1eHgR5-UeBmBUmc6uCbtVCSHZEq2NTJ51xa1iXAXVrXHVl1U3RmuB7pxuAAAoeL6qELS9uiHStDXrbFrmXQBGB1tuREABpHY2wM5Rgt1DYVj04Ke0YZL2k0CBNVYoXj6F5rN5DzYtSMzCjkXrWJSlbRuw17dj7W4xehMZSdjFPk5F2ke5hYk2NOCM75qGs-dQUIzVuitG9DVrU130Y4r+3-SlgNE31uX0+Nk2a3Z2sQJzUDFWVLmU1VeszMjRufYkzU-Zj8440lBPW9LxP9ar6soeuWvDW7Htx47LMzS5nOI37PMB-zm1BxtSkqYkqww3DKBR5lZmy5OVeh1AwReYEgR5utDfm-YLe2O3Jud7Z2Qt5kfdfQP4H1C3-ij0X48AaMLfDDP+jcXPw6bC36zL3oq97ZcLfnNvXFQGvUCfC37xH7vrWQi34JXyfe1hC3IQPwuSDNqX+irEhIBIBGOlT5AzMtJU+zc7IAGUXCDmQg-U+3c7LriYL6WBHc9pDzspYFw1AUEgDgXtSedlbwQF9GJKguD8GtQXpg0q+lyGoP7ntDedkABiUEKFoNavvSBt19IcJNgLKsmhEhMVWEgxgwBSr-3fEAomICnzwJynmAAUl4FwIY8GcMbhgvMLCIDlnIOo3ylDG6ELzOuEMbpSomPNtQvMABBCgtBAw2Nssw8xDCx57yUco6BrstHm3PnZXxgZNGMJvko+xLhoIuAyK48Cz9IEQDAP2aO8SAJEiUQgN0wB6CZXScORkSikD0EkmkgJtlbA6KPAGCA5SBE+S-noH+yFUJMEkVhW6EB0LEIgAAa1wbXaSSFrKtXstJTyG52mlT2uMjiL5ekDOQrM5WXlgq6FWgcCZK8oCLNwaLDq1ZbxS16iDDCyFpE6Rwr7TZURbzbJ3rsiA-T9m-XDv9Y51cZax3tmrR2t5Wb7I9pI6AAB3Z5fTs43PqgcLQ9yRLF3kvBba0yDkS2rIdL5wMHk5T2cssZqy7ZMygPspcAhxGSMufDLmRtIhvRxdxPFflfqHN2Sc75RLfKCQuQA7wOFWBkqetMqlP4aW3MOvC5ijKIWvMbmLVlnzG5A1tgNX58c+WAuWcC0qYKIVu0FVTYVvLRU53FTMSVClEXkG2SiiRMy3niwOuy7F8ynkvPxY3a6nK-KksYHTYlDsmZOzzG4aZUKEi3INhEBlbqlnMrlQq51KrVa-ypfyqAZK05BsTs7MNusI0wtzha5FsbZXm3leitlWLk1qv+Zqvy2rdX9P1X6rNGsc0hrzWKwtUbWjFoaYLS1gjrVPnWiM6ALLK1HiTRgbZL4KX2s9YWHFCyZUevNl6jAIKBAIHIL6JcHhAzeCPAtd2o16ahrtaa+q06SgxqZWipKirzbKrOaqgNOBU28vTV4X0mYdU7r3Qeo9J7+LnuJZeyR17wq3t2fetd8by3vKIs+2yr6Va-N3fu8MIHT2Nv8qi2gJVaAZloMOASbaUIArskCkq27wXNvzRsm9nQ4UxuHTaqZdrH3-UxUqm2s7XUPoJUWH5H6F0itPbgLDwHj1MaNtO+lCLhMJsrah8C6H+rbuk0BnDcnWC-qgIewM3K-7foWsOA1EnjXydWopuDymEOi2Q2udTaUBMk20zgGTenQNGcHP6rlgaNbUbzLR-9TbIWnqs0a98JroUwY3Oa9jhcS0Luc463jM6V2lvXbZTdXmfOHv02ewLflgsJy1l26DxFe3wfdYh2yf0UMzs8wBnT2Hit+cM8Z0zaaLMZtbbW7NVW7XhuY4lqN-avoqaQ5llr1a32Fd011vD4Ggt-JG7msb0XiOkfI0Nj96rQvhfo3q2zLHe1sYRRxodm0x1dIy6y6di251cckSso+1FZv5cJe+jb6qjz1tK9uyD1iLvhVaLB8I9W408YW-x6ONajuOyBzRrVdGANg-1XtjNw4PB9fM92yHWhYPTaLj9hKLmjmtbEwD1HrN8PY92xm-b7tDv06DSdjHEWoAMai8TmY0PrtSqtZx9Lk6nWvaE05kTGGUdBrR2F2B0W-VCrtZJwXUOksw8cw1+HrnafeoZgz9HeDVfktizpUVGbcdkaM4T98-Kava4lbDstTXqdVsR6c+XnONZK4oRb9XlKbMCrtwdsrJuufA-w-z8btLSc65F3dwdaXUUOtZXxl9AmcuU4Apuyjx7Y+Y+gMzgVHPyubfbaNyRCfNlJ7q3ruHkuPlG-+1XwHJfefl9t6zvHDvcBfqdxbovHby8u8b1oN3zePdU-m4bxbfvO+m+Vw20vBGdvh-7-bijw2Qvd7O4xivUeKvBonwloXuc+0pbTwOpFjzh2aGYKYBI0DYkkPQuWFwzjsPQSgOWIGPQG6H0jVJmDYFJKsKFEeLQNBCRt4KBD7muKWAuOhigTjK+vIlOJnpWpHEgTHDxHjJRNxIgeWqyngTnkjm+rgEQYJsfKQWhkTCTLgHxHQYpEIjKEMDAXARmOgNAbAfAVMIEAxKWFMLYCIVAFMJkBIVMP4DIa0FyDWL4OsBIecDEEsE6KsEVkerItHEMnoPAggnmIyHXhONfI3FUkolUqYY8vAmYrYP4DYcfO-NlJOuRIwLBLYNQTgB-BkGwTxB-NVG4cAFBB4cwJkN4R-IOP4S4UEapu4bBP4JEcwHujEbgB-FBipFmI7LoZlOhBAntAkbXIoi+AUdxAuNod4EgFJjgBAp-JwTYKFPHKFEyFAOqrYKzOzPNI6NKJaEME0ZNKFAKG0Y7JkJ0VnJqJociBkAMUzKFPKCMUGv4OMXNJMb0WcI0c0XqIsRrMMCsRzGsRaBsTMc0ZYegO0WMZnKsT0UcdMcALMb5EMcMeqpkJcWzBMTcUiPSPcaca0e0R0VcQcZ8fiDqA8SAEMX8aMQCe8dcRoesXcWCS0Qse0csYCd0XCbcd8YiSqOcaMaiTCUCRiVUEAA | |
type CRON = `${CRON.Expression} ${CRON.Expression} ${CRON.Expression} ${CRON.Expression} ${CRON.Expression}`; | |
// ^? | |
type Test1 = CRON.Human<"30 9 * * *">; | |
// ^? | |
type Test2 = CRON.Human<"* 9 * * *">; | |
// ^? | |
type Test3 = CRON.Human<"*/3 9 * * *">; | |
// ^? | |
type Test4 = CRON.Human<"*/3 */4 * * *">; | |
// ^? | |
type Test5 = CRON.Human<"*/3 * * * *">; | |
// ^? | |
type Test6 = CRON.Human<"* */3 * * *">; | |
// ^? | |
type Test7 = CRON.Human<"15 */2 * * *">; | |
// ^? | |
type Test8 = CRON.Human<"* * 3 * *">; | |
// ^? | |
type Test9 = CRON.Human<"* * */3 * *">; | |
// ^? | |
type Test10 = CRON.Human<"* * 3 */2 *">; | |
// ^? | |
type Test11 = CRON.Human<"* * 3 */2 4">; | |
// ^? | |
type Test12 = CRON.Human<"0 23 */2 */3 */4">; | |
// ^? | |
type Test13 = CRON.Human<"9 23 */2 */3 4">; | |
// ^? | |
type Test14 = CRON.Human<"*/15 */6 * 3 *">; | |
// ^? | |
type Test15 = CRON.Human<"* * * 3 *">; | |
// ^? | |
// ---cut--- | |
declare namespace CRON { | |
export type Expression = '*' | number | `*/${number}`; | |
export type Human<T extends CRON> = | |
T extends `${infer M} ${infer H} ${infer DM} ${infer MM} ${infer DW}` | |
? Time<H,M> extends `${infer T extends string}` | |
? Day<MM, DM, DW> extends `${infer D extends string}` | |
? D extends "" | |
? T | |
: `${T} on ${D}` | |
: never | |
: never | |
: never; | |
// --- | |
type Time<Hour, Minute> = | |
Minute extends '*' | |
? Hour extends '*' ? 'every minute' // * * _ _ _ | |
: Hour extends `${infer H extends number}` ? `every minute of hour ${H}` // * H _ _ _ | |
: Hour extends `*/${infer H extends number}` ? `every minute of every ${Every<H, 'hour'>}` // * */H _ _ _ | |
: never | |
: Minute extends `${infer M extends number}` | |
? Hour extends '*' ? `at minute-${M}` // M * _ _ _ | |
: Hour extends `${infer H extends number}` ? `at ${H}:${Pad<M>}` // M H _ _ _ | |
: Hour extends `*/${infer X extends number}` ? `at minute-${M} past every ${Every<X, 'hour'>}` // M */H _ _ _ | |
: never | |
: Minute extends `*/${infer M extends number}` | |
? Hour extends '*' ? `every ${Every<M, 'minute'>}` // */M * _ _ _ | |
: Hour extends `${infer H extends number}` ? `every ${Every<M, 'minute'>} of hour-${H}` // */M H _ _ _ | |
: Hour extends `*/${infer X extends number}` ? `every ${Every<M, 'minute'>} of every ${Every<X, 'hour'>}` // */M */H _ _ _ | |
: never | |
: never; | |
type Pad<N extends number> = | |
N extends 0 ? '00' | |
: N extends 1 ? '01' | |
: N extends 2 ? '02' | |
: N extends 3 ? '03' | |
: N extends 4 ? '04' | |
: N extends 5 ? '05' | |
: N extends 6 ? '06' | |
: N extends 7 ? '07' | |
: N extends 8 ? '08' | |
: N extends 9 ? '09' | |
: `${N}`; | |
type DayName<N extends number> = | |
N extends 0 ? 'Sunday' | |
: N extends 1 ? 'Monday' | |
: N extends 2 ? 'Tuesday' | |
: N extends 3 ? 'Wednesday' | |
: N extends 4 ? 'Thursday' | |
: N extends 5 ? 'Friday' | |
: N extends 6 ? 'Saturday' | |
: never; | |
type MonthName<N extends number> = | |
N extends 1 ? 'January' | |
: N extends 2 ? 'February' | |
: N extends 3 ? 'March' | |
: N extends 4 ? 'April' | |
: N extends 5 ? 'May' | |
: N extends 6 ? 'June' | |
: N extends 7 ? 'July' | |
: N extends 8 ? 'August' | |
: N extends 9 ? 'September' | |
: N extends 10 ? 'October' | |
: N extends 11 ? 'November' | |
: N extends 12 ? 'December' | |
: never; | |
type Day<Month, Date, Weekday> = | |
Date extends '*' | |
? Month extends '*' | |
? Weekday extends '*' ? '' // _ _ * * * | |
: Weekday extends `${infer W extends number}` ? DayName<W> // _ _ * * W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<W, 'day of the week'>}` // _ _ * * */W | |
: never | |
: Month extends `${infer M extends number}` | |
? Weekday extends '*' ? `every day in ${MonthName<M>}` // _ _ * M * | |
: Weekday extends `${infer W extends number}` ? `every ${DayName<W>} in ${MonthName<M>}` // _ _ * M W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<W, 'day of the week'>} in ${MonthName<M>}` // _ _ * M */W | |
: never | |
: Month extends `*/${infer M extends number}` | |
? Weekday extends '*' ? `every day in every ${Every<M, 'month'>}` // _ _ * */M * | |
: Weekday extends `${infer W extends number}` ? `every ${DayName<W>} in every ${Every<M, 'month'>}` // _ _ * */M W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<W, 'day of the week'>} in every ${Every<M, 'month'>}` // _ _ * */M */W | |
: never | |
: never | |
: Date extends `${infer D extends number}` | |
? Month extends '*' | |
? Weekday extends '*' ? `the ${Ordinal<D>} of every month` // _ _ D * * | |
: Weekday extends `${infer W extends number}` ? `every ${DayName<W>} and the ${Ordinal<D>} of every month` // _ _ D * W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Ordinal<D>} of the month if it is on every ${Every<W, 'day of the week'>}` // _ _ D * */W | |
: never | |
: Month extends `${infer M extends number}` | |
? Weekday extends '*' ? `every ${MonthName<M>} ${Ordinal<D>}` // _ _ D M * | |
: Weekday extends `${infer W extends number}` ? `the ${Ordinal<D>} and all ${DayName<W>}s in ${MonthName<M>}` // _ _ D M W | |
: Weekday extends `*/${infer W extends number}` ? `the ${Ordinal<D>} and every ${Every<W, 'day of the week'>} in ${MonthName<M>}` // _ _ D M */W | |
: never | |
: Month extends `*/${infer M extends number}` | |
? Weekday extends '*' ? `the ${Ordinal<D>} of every ${Every<M, 'month'>}` // _ _ D */M * | |
: Weekday extends `${infer W extends number}` ? `the ${Ordinal<D>} and all ${DayName<W>}s in every ${Every<M, 'month'>}` // _ _ D */M W | |
: Weekday extends `*/${infer W extends number}` ? `the ${Ordinal<D>} of every ${Every<M, 'month'>} if it is on every ${Every<W, 'day of the week'>}` // _ _ D */M */W | |
: never | |
: never | |
: Date extends `*/${infer D extends number}` | |
? Month extends '*' | |
? Weekday extends '*' ? `every ${Every<D, 'day of the month'>}` // _ _ */D * * | |
: Weekday extends `${infer W extends number}` ? `every ${Every<D, 'day of the month'>} if it is a ${DayName<W>}` // _ _ */D * W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<D, 'of the month'>} if it is on every ${Every<W, 'day of the week'>}` // _ _ */D * */W | |
: never | |
: Month extends `${infer M extends number}` | |
? Weekday extends '*' ? `every ${Every<D, 'day'>} in ${MonthName<M>}` // _ _ */D M * | |
: Weekday extends `${infer W extends number}` ? `every ${Every<D, 'day'>} in ${MonthName<M>} if it is a ${DayName<W>}` // _ _ */D M W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<D, 'day'>} in ${MonthName<M>} if it is on every ${Every<W, 'day of the week'>}` // _ _ */D M */W | |
: never | |
: Month extends `*/${infer M extends number}` | |
? Weekday extends '*' ? `every ${Every<D, 'day of the month'>} in every ${Every<M, 'month'>}` // _ _ */D */M * | |
: Weekday extends `${infer W extends number}` ? `every ${Every<D, 'day of the month'>} if it is a ${DayName<W>} in every ${Every<M, 'month'>}` // _ _ */D */M W | |
: Weekday extends `*/${infer W extends number}` ? `every ${Every<D, 'day of the month'>} if it is on every ${Every<W, 'day of the week'>} in every ${Every<M, 'month'>}` // _ _ */D */M */W | |
: never | |
: never | |
: never; | |
} | |
// unused, building block / tester | |
type _Digit<T extends number | `${number}` | `*/${number}`> = | |
T extends `*/${infer X extends number}` ? `${X}` | |
: T extends `${infer X extends number}` ? `${X}` | |
: T extends number ? `${T}` | |
: never; | |
type TestDigit = _Digit<"*/0" | "*/1" | "*/2" | "*/3" | "*/4" | "5" | 6> | |
// ^? | |
type Ordinal<N extends number> = | |
N extends 11 ? '11th' | |
: N extends 12 ? '12th' | |
: N extends 13 ? '13th' | |
: `${N}` extends `${string}1` ? `${N}st` | |
: `${N}` extends `${string}2` ? `${N}nd` | |
: `${N}` extends `${string}3` ? `${N}rd` | |
: `${N}th`; | |
type Every<N extends number, S extends string> = N extends 1 ? S : `${Ordinal<N>} ${S}`; | |
type Test_Every_1 = Every<1, 'hour'>; | |
// ^? | |
type Test_Every_2 = Every<2, 'hour'>; | |
// ^? | |
type Test_Every_3 = Every<3, 'hour'>; | |
// ^? | |
type Test_Every_4 = Every<4, 'hour'>; | |
// ^? | |
type Test_Every_12 = Every<12, 'hour'>; | |
// ^? | |
type Test_Every_22 = Every<22, 'hour'>; | |
// ^? | |
type Test_Every_11 = Every<11, 'hour'>; | |
// ^? | |
type Test_Every_21 = Every<21, 'hour'>; | |
// ^? | |
type Test_Every_13 = Every<13, 'hour'>; | |
// ^? | |
type Test_Every_23 = Every<23, 'hour'>; | |
// ^? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment