forks

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

登录 后可以评论

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

登录 后可以评论

Comments

登录 后可以评论