Creation chat
ClarkZhang
A simple Crontab generator that supports demo generation.
During editing, the time, day, month, and week will be highlighted.
hint user with some tips: for example:
* any value
, value list separator
- range of values
/ step values
let user know next 4 times ,when would the cron tab run again.
02-25 03:56
暂无回复
No comments yet
登录 后可以评论
Assistant
(HTML code has been generated and updated; see above for the current version.)
▼ Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crontab Generator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>tailwind.config = { darkMode: 'class' }</script>
</head>
<body class="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100">
<!-- Theme toggle -->
<button id="theme-toggle-btn" class="fixed top-4 right-4 z-10 p-2 rounded-md hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors flex items-center justify-center" aria-label="Toggle theme" onclick="toggleTheme()">
<svg id="icon-moon" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998z"/></svg>
<svg id="icon-sun" class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"></path></svg>
</button>
<script>
function toggleTheme(){ var el=document.documentElement; var isDark=!el.classList.contains('dark'); el.classList.toggle('dark',isDark); localStorage.setItem('theme',isDark?'dark':'light'); applyTheme(isDark); }
function applyTheme(isDark){ document.getElementById('icon-moon').style.display=isDark?'none':'block'; document.getElementById('icon-sun').style.display=isDark?'block':'none'; }
document.addEventListener('DOMContentLoaded',function(){ var saved=localStorage.getItem('theme'); var isDark=saved==='dark'||(!saved&&window.matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.classList.toggle('dark',isDark); applyTheme(isDark); });
</script>
<div class="container mx-auto px-4 py-8 max-w-4xl">
<header class="text-center mb-10">
<h1 class="text-3xl md:text-4xl font-bold text-slate-800 dark:text-slate-100 mb-2">Crontab Generator</h1>
<p class="text-slate-600 dark:text-slate-300">Easily create and visualize cron expressions</p>
</header>
<main>
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 mb-8">
<h2 class="text-xl font-semibold mb-4 text-slate-800 dark:text-slate-100">Cron Expression Builder</h2>
<div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6">
<div class="space-y-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300">Minute</label>
<input
type="text"
id="minute"
class="w-full p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="*"
placeholder="*"
>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300">Hour</label>
<input
type="text"
id="hour"
class="w-full p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="*"
placeholder="*"
>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300">Day of Month</label>
<input
type="text"
id="day"
class="w-full p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="*"
placeholder="*"
>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300">Month</label>
<input
type="text"
id="month"
class="w-full p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="*"
placeholder="*"
>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300">Day of Week</label>
<input
type="text"
id="weekday"
class="w-full p-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="*"
placeholder="*"
>
</div>
</div>
<div class="mb-6">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Generated Cron Expression</label>
<div class="flex">
<input
type="text"
id="cron-expression"
class="flex-grow p-3 border border-slate-300 dark:border-slate-600 rounded-l-lg bg-slate-50 dark:bg-slate-700 text-slate-900 dark:text-slate-100 font-mono"
value="* * * * *"
readonly
>
<button
id="copy-btn"
class="bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white px-4 rounded-r-lg transition-colors"
>
Copy
</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-medium text-slate-800 dark:text-slate-200 mb-3">Syntax Help</h3>
<ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-center mr-2 flex-shrink-0">-</span>
<span><span class="font-mono">* </span> - any value</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-center mr-2 flex-shrink-0">,</span>
<span><span class="font-mono">, </span> - value list separator</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-center mr-2 flex-shrink-0">-</span>
<span><span class="font-mono">- </span> - range of values</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-center mr-2 flex-shrink-0">/</span>
<span><span class="font-mono">/ </span> - step values</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-center mr-2 flex-shrink-0">@</span>
<span><span class="font-mono">@yearly </span> - (0 0 1 1 *)</span>
</li>
</ul>
</div>
<div>
<h3 class="text-lg font-medium text-slate-800 dark:text-slate-200 mb-3">Examples</h3>
<ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200 text-center mr-2 flex-shrink-0">1</span>
<span><span class="font-mono">0 9 * * 1-5 </span> - Every weekday at 9am</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200 text-center mr-2 flex-shrink-0">2</span>
<span><span class="font-mono">*/5 * * * * </span> - Every 5 minutes</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200 text-center mr-2 flex-shrink-0">3</span>
<span><span class="font-mono">0 0 1 * * </span> - First day of every month</span>
</li>
<li class="flex items-start">
<span class="inline-block w-6 h-6 rounded-full bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200 text-center mr-2 flex-shrink-0">4</span>
<span><span class="font-mono">@daily </span> - Once a day at midnight</span>
</li>
</ul>
</div>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
<h2 class="text-xl font-semibold mb-4 text-slate-800 dark:text-slate-100">Next Execution Times</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 dark:divide-slate-700">
<thead class="bg-slate-50 dark:bg-slate-700">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-300 uppercase tracking-wider">Execution #</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-300 uppercase tracking-wider">Date & Time</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-300 uppercase tracking-wider">Relative Time</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-slate-800 divide-y divide-slate-200 dark:divide-slate-700">
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-900 dark:text-slate-100">1</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="next-run-1">-</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="relative-time-1">-</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-900 dark:text-slate-100">2</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="next-run-2">-</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="relative-time-2">-</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-900 dark:text-slate-100">3</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="next-run-3">-</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="relative-time-3">-</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-900 dark:text-slate-100">4</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="next-run-4">-</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-700 dark:text-slate-300" id="relative-time-4">-</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
<footer class="mt-10 text-center text-sm text-slate-600 dark:text-slate-400">
<p>Enter cron expressions to see when they will run next. All times shown in your local timezone.</p>
</footer>
</div>
<script>
// DOM Elements
const minuteInput = document.getElementById('minute');
const hourInput = document.getElementById('hour');
const dayInput = document.getElementById('day');
const monthInput = document.getElementById('month');
const weekdayInput = document.getElementById('weekday');
const cronExpression = document.getElementById('cron-expression');
const copyBtn = document.getElementById('copy-btn');
// Initialize with current time
updateCronExpression();
calculateNextRuns();
// Add event listeners
[minuteInput, hourInput, dayInput, monthInput, weekdayInput].forEach(input => {
input.addEventListener('input', () => {
updateCronExpression();
calculateNextRuns();
});
});
copyBtn.addEventListener('click', () => {
cronExpression.select();
document.execCommand('copy');
// Show feedback
const originalText = copyBtn.textContent;
copyBtn.textContent = 'Copied!';
setTimeout(() => {
copyBtn.textContent = originalText;
}, 2000);
});
// Update cron expression display
function updateCronExpression() {
const minute = minuteInput.value || '*';
const hour = hourInput.value || '*';
const day = dayInput.value || '*';
const month = monthInput.value || '*';
const weekday = weekdayInput.value || '*';
cronExpression.value = `${minute} ${hour} ${day} ${month} ${weekday}`;
}
// Calculate next 4 execution times
function calculateNextRuns() {
const cron = cronExpression.value;
const now = new Date();
try {
const nextRuns = getNextRuns(cron, now, 4);
for (let i = 0; i < 4; i++) {
if (nextRuns[i]) {
document.getElementById(`next-run-${i+1}`).textContent = formatDateTime(nextRuns[i]);
document.getElementById(`relative-time-${i+1}`).textContent = formatRelativeTime(nextRuns[i]);
} else {
document.getElementById(`next-run-${i+1}`).textContent = '-';
document.getElementById(`relative-time-${i+1}`).textContent = '-';
}
}
} catch (e) {
console.error('Error calculating next runs:', e);
for (let i = 0; i < 4; i++) {
document.getElementById(`next-run-${i+1}`).textContent = 'Invalid cron expression';
document.getElementById(`relative-time-${i+1}`).textContent = '-';
}
}
}
// Parse cron expression and calculate next runs
function getNextRuns(cron, startDate, count) {
const parts = cron.split(' ');
if (parts.length !== 5) throw new Error('Invalid cron expression');
const [minute, hour, day, month, weekday] = parts;
const nextRuns = [];
let currentDate = new Date(startDate);
// Simple cron parser (not exhaustive but covers common cases)
for (let i = 0; i < count * 100 && nextRuns.length < count; i++) {
currentDate = new Date(currentDate.getTime() + 60000); // Add 1 minute
if (matchesCron(currentDate, minute, hour, day, month, weekday)) {
nextRuns.push(new Date(currentDate));
}
}
return nextRuns;
}
// Check if a date matches the cron expression
function matchesCron(date, minute, hour, day, month, weekday) {
return (
matchesField(date.getMinutes(), minute, 0, 59) &&
matchesField(date.getHours(), hour, 0, 23) &&
matchesField(date.getDate(), day, 1, 31) &&
matchesField(date.getMonth() + 1, month, 1, 12) &&
matchesField(date.getDay(), weekday, 0, 6)
);
}
// Check if a value matches a cron field
function matchesField(value, field, min, max) {
if (field === '*') return true;
// Handle step values (e.g., */5)
if (field.includes('/')) {
const [range, step] = field.split('/');
const stepValue = parseInt(step);
if (range === '*') {
return value % stepValue === 0;
}
// Handle range with step (e.g., 1-10/2)
const [start, end] = range.split('-').map(Number);
return value >= start && value <= end && (value - start) % stepValue === 0;
}
// Handle ranges (e.g., 1-5)
if (field.includes('-') && !field.includes(',')) {
const [start, end] = field.split('-').map(Number);
return value >= start && value <= end;
}
// Handle lists (e.g., 1,2,3)
if (field.includes(',')) {
const values = field.split(',').map(v => parseInt(v));
return values.includes(value);
}
// Handle single value
const fieldValue = parseInt(field);
return value === fieldValue;
}
// Format date and time
function formatDateTime(date) {
return date.toLocaleString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
// Format relative time
function formatRelativeTime(date) {
const now = new Date();
const diffMs = date - now;
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffMins < 1) return 'In less than a minute';
if (diffMins < 60) return `In ${diffMins} minute${diffMins !== 1 ? 's' : ''}`;
if (diffHours < 24) return `In ${diffHours} hour${diffHours !== 1 ? 's' : ''}`;
return `In ${diffDays} day${diffDays !== 1 ? 's' : ''}`;
}
</script>
</body>
</html>
02-25 03:59
暂无回复
No comments yet
登录 后可以评论
Comments
暂无回复
No comments yet
登录 后可以评论