A comprehensive jQuery plugin that supports Gregorian, Persian (Jalaali), and Hijri (Islamic) calendar systems with advanced features including time picker, RTL support, and extensive customization options.
Try it out:https://delphiassistant.github.io/MultiCalendarDatePicker/
Experience all calendar systems, themes, and features in action with interactive examples.
- Gregorian Calendar: Standard Western calendar with leap year calculations (1900-2100)
- Persian Calendar: Solar calendar with verified leap year algorithm (1200-1500)
- Hijri Calendar: Lunar Islamic calendar with improved leap year detection (1000-2000)
- Responsive Design: Works on desktop and mobile devices
- RTL Support: Full right-to-left support for Persian and Arabic
- Themes: Light and dark themes
- Customizable: Extensive styling options via CSS variables
- 24-hour Format: HH:mm, HH:mm:ss
- 12-hour Format: hh:mm A, hh:mm:ss A
- Configurable: Show/hide seconds, custom stepping
- Localized: Hour, minute, second labels in multiple languages
- Languages: English, Persian (Farsi), Arabic
- Auto-detection: Automatic RTL detection for Persian/Hijri calendars
- Custom Locales: Easy to add new languages
- Date Constraints: Min/max date, disabled dates, disabled weekdays
- Keyboard Navigation: Full keyboard support with arrow keys
- Data Attributes: Initialize via HTML data attributes
- API Methods: Programmatic control and manipulation
- Event System: Rich event handling for all interactions
- Input Validation: Real-time validation with auto-correction for manual input
- Visual Feedback: Animated feedback for corrected and invalid inputs
- Editable Year Input: Year field in datepicker title is always visible and editable
- Real-Time Year Validation: Year validation happens during typing, not just on blur
- Bidirectional Sync: Changes in datepicker year input automatically update attached textbox
- Year Range Validation: Automatic correction of invalid years to nearest valid value
- Configurable Timeouts: Customizable auto-correction timeout for mobile-friendly typing
- Mobile Optimization: Number input spinners visible on mobile devices
- Custom RTL Input: Better control over RTL input styling with
.mcdp-rtl-inputclass
The project includes a bundled Moment.js library (moment-bundled.js) that combines:
- Moment.js Core (2.29.4)
- Persian/Jalaali Calendar with verified leap year detection (1300-2100)
- Hijri/Islamic Calendar with verified leap year algorithms (1300-1600)
This single file replaces the need for separate Moment.js, moment-jalaali, and moment-hijri libraries, making setup simpler and more reliable.
- ✅ Accurate Persian conversion using verified leap year data
- ✅ Reliable Hijri conversion with improved algorithms
- ✅ Browser-compatible - Works in all modern browsers
- ✅ Optimized performance - Single file reduces HTTP requests
- ✅ Verified accuracy - Tested against known date pairs
<!-- Required Libraries --><scriptsrc="lib/jquery-3.7.1.min.js"></script><scriptsrc="lib/bootstrap-5.3.0.min.js"></script><scriptsrc="lib/moment-bundled.js"></script><!-- DatePicker Files --><linkhref="lib/multi-calendar-datepicker.css" rel="stylesheet"><scriptsrc="lib/multi-calendar-datepicker.js"></script>lib/
├── multi-calendar-datepicker.js # Main plugin file
├── multi-calendar-datepicker.css # Styles and themes
├── moment-bundled.js # Combined Moment.js with calendar extensions
├── jquery-3.7.1.min.js # jQuery library
└── bootstrap-5.3.0.min.js # Bootstrap JavaScript
moment.js-fixes/ # Archived files
├── moment-2.29.4.min.js # Original Moment.js
├── moment-jalali.js # Original Persian library
├── moment-hijri.js # Original Hijri library
└── CALENDAR_FIXES.md # Documentation of fixes
<inputtype="text" id="datepicker" placeholder="YYYY/MM/DD">$('#datepicker').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD'});$('#gregorian-date').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD',timePicker: true,timeFormat: 'HH:mm'});$('#persian-date').multiCalendarDatePicker({calendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD',rtl: true});$('#hijri-date').multiCalendarDatePicker({calendar: 'hijri',locale: 'ar',format: 'YYYY/MM/DD',rtl: true});| Option | Type | Default | Possible Values | Effect |
|---|---|---|---|---|
calendar | string | 'gregorian' | 'gregorian', 'persian', 'hijri' | Determines calendar system and year ranges |
locale | string | 'en' | 'en', 'fa', 'ar' | Sets language for month names, weekdays, and UI text |
format | string | 'YYYY/MM/DD' | 'YYYY/MM/DD', 'MM/DD/YYYY', 'DD/MM/YYYY' | Controls date display and input parsing format |
rtl | boolean | null | true, false, null | Enables RTL layout (auto-detected for Persian/Hijri) |
theme | string | 'light' | 'light', 'dark' | Sets color scheme and visual appearance |
| Option | Type | Default | Possible Values | Effect |
|---|---|---|---|---|
timePicker | boolean | false | true, false | Enables/disables time selection interface |
timeFormat | string | 'HH:mm' | 'HH:mm', 'HH:mm:ss', 'hh:mm A', 'hh:mm:ss A' | Controls time display format |
showSeconds | boolean | false | true, false | Shows/hides seconds input field |
use24Hour | boolean | true | true, false | Uses 24-hour vs 12-hour time format |
stepping | number | 1 | 1, 5, 15, 30 | Sets minute increment step for time picker |
| Option | Type | Default | Possible Values | Effect |
|---|---|---|---|---|
placement | string | 'bottom-start' | 'bottom-start', 'bottom-end', 'top-start', 'top-end' | Controls picker position relative to input |
showToday | boolean | true | true, false | Shows/hides "Today" button |
showClear | boolean | true | true, false | Shows/hides "Clear" button |
showClose | boolean | true | true, false | Shows/hides "Close" button |
autoClose | boolean | true | true, false | Auto-closes picker after date selection |
hideAfterSelect | boolean | true | true, false | Hides picker immediately after selection |
keyboardNavigation | boolean | true | true, false | Enables arrow key navigation |
todayHighlight | boolean | true | true, false | Highlights current date in calendar |
allowInput | boolean | true | true, false | Allows manual typing in input field |
| Option | Type | Default | Possible Values | Effect |
|---|---|---|---|---|
minDate | string | null | Date string in format, null | Prevents selection of dates before this date |
maxDate | string | null | Date string in format, null | Prevents selection of dates after this date |
disabledDates | array | [] | Array of date strings | Disables specific dates from selection |
disabledDays | array | [] | Array of numbers (0-6) | Disables specific weekdays (0=Sunday) |
| Option | Type | Default | Possible Values | Effect |
|---|---|---|---|---|
autoCorrectionTimeout | number | 3000 | Any positive number (ms) | Sets timeout for auto-correction of invalid years and dates |
$('#datepicker').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD'// autoCorrectionTimeout defaults to 3000ms});$('#mobile-datepicker').multiCalendarDatePicker({calendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD',rtl: true,autoCorrectionTimeout: 4000// 4 seconds for slower mobile typing});$('#desktop-datepicker').multiCalendarDatePicker({calendar: 'hijri',locale: 'ar',format: 'YYYY/MM/DD',rtl: true,autoCorrectionTimeout: 1000// 1 second for quick desktop typing});$('#accessible-datepicker').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD',autoCorrectionTimeout: 5000// 5 seconds for users who type slowly});$('#advanced-datepicker').multiCalendarDatePicker({// Core Settingscalendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD',rtl: true,theme: 'dark',// Time Picker SettingstimePicker: true,timeFormat: 'HH:mm:ss',showSeconds: true,use24Hour: true,stepping: 5,// UI Behaviorplacement: 'bottom-end',showToday: true,showClear: true,showClose: true,autoClose: false,hideAfterSelect: false,keyboardNavigation: true,todayHighlight: true,allowInput: true,// Date ConstraintsminDate: '1200/01/01',maxDate: '1500/12/29',disabledDates: ['1200/01/01','1200/12/29'],disabledDays: [0,6],// Disable weekends// Auto-Correction SettingsautoCorrectionTimeout: 3500// Custom timeout for this instance});Initialize datepickers using HTML data attributes:
<inputtype="text" data-mcdpdata-calendar="hijri"
data-locale="ar"
data-theme="dark"
data-time-picker="true"
data-time-format="hh:mm A">data-calendar- Calendar system (gregorian,persian,hijri)data-locale- Language (en,fa,ar)data-format- Date format (YYYY/MM/DD,MM/DD/YYYY,DD/MM/YYYY)data-rtl- RTL mode (true,false)data-theme- Theme (light,dark)data-time-picker- Enable time picker (true,false)data-time-format- Time format (HH:mm,HH:mm:ss,hh:mm A,hh:mm:ss A)data-show-seconds- Show seconds (true,false)data-use24-hour- 24-hour format (true,false)data-stepping- Minute stepping (1,5,15,30)data-placement- Picker placement (bottom-start,bottom-end,top-start,top-end)data-show-today- Show today button (true,false)data-show-clear- Show clear button (true,false)data-show-close- Show close button (true,false)data-auto-close- Auto-close after selection (true,false)data-hide-after-select- Hide after date selection (true,false)data-keyboard-navigation- Enable keyboard navigation (true,false)data-today-highlight- Highlight today's date (true,false)data-allow-input- Allow manual input (true,false)data-min-date- Minimum date (date string)data-max-date- Maximum date (date string)data-disabled-dates- Disabled dates (comma-separated)data-disabled-days- Disabled weekdays (comma-separated, 0-6)data-auto-correction-timeout- Auto-correction timeout in milliseconds
// Show/hide picker$('#datepicker').multiCalendarDatePicker('show');$('#datepicker').multiCalendarDatePicker('hide');$('#datepicker').multiCalendarDatePicker('toggle');// Set/get date$('#datepicker').multiCalendarDatePicker('setDate','2024/12/25');vardate=$('#datepicker').multiCalendarDatePicker('getDate');// Clear date$('#datepicker').multiCalendarDatePicker('clear');// Set today$('#datepicker').multiCalendarDatePicker('today');// Get formatted datevarformatted=$('#datepicker').multiCalendarDatePicker('getFormattedDate');// Initialize different calendars$('#gregorian-input').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD'});$('#persian-input').multiCalendarDatePicker({calendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD',rtl: true});$('#hijri-input').multiCalendarDatePicker({calendar: 'hijri',locale: 'ar',format: 'YYYY/MM/DD',rtl: true});// Use API methods on specific calendars$('#gregorian-input').multiCalendarDatePicker('today');// Sets Gregorian today$('#persian-input').multiCalendarDatePicker('today');// Sets Persian today$('#hijri-input').multiCalendarDatePicker('today');// Sets Hijri today// Initialize with one calendar$('#universal-input').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD'});// Switch to different calendars dynamicallyfunctionswitchToPersian(){$('#universal-input').multiCalendarDatePicker('setOptions',{calendar: 'persian',locale: 'fa',rtl: true});}functionswitchToHijri(){$('#universal-input').multiCalendarDatePicker('setOptions',{calendar: 'hijri',locale: 'ar',rtl: true});}// Now API methods work with the current calendar$('#universal-input').multiCalendarDatePicker('today');// Uses current calendar// Update options$('#datepicker').multiCalendarDatePicker('setOptions',{theme: 'dark',timePicker: true});// Get current optionsvaroptions=$('#datepicker').multiCalendarDatePicker('getOptions');// Destroy instance$('#datepicker').multiCalendarDatePicker('destroy');- Range: 1900-2100
- Week Start: Sunday
- Leap Year Rule: Divisible by 4, except century years unless divisible by 400
- Leap Day: February 29th
- Year Validation: Auto-corrects invalid years to nearest valid value (min: 1900, max: 2100)
- Range: 1200-1500
- Week Start: Saturday
- Leap Year Algorithm: Verified list (1300-2100) with 33-year cycle fallback
- New Year: Nowruz (March 20-21)
- Leap Day: 30th day of Esfand (month 12)
- Year Validation: Auto-corrects invalid years to nearest valid value (min: 1200, max: 1500)
- Editable Year: Year input in title is always visible and editable
- Range: 1000-2000
- Week Start: Saturday
- Leap Year Algorithm: Verified list (1300-1600) with improved 30-year cycle fallback
- Leap Years: 11 leap years per 30-year cycle
- Leap Day: 30th day of Dhul-Hijjah (month 12)
- Fixed: Correctly shows 30 days for month 12 in leap years (e.g., 1401, 1406, 1411)
- Year Validation: Auto-corrects invalid years to nearest valid value (min: 1000, max: 2000)
The autoCorrectionTimeout parameter controls how long the system waits before automatically correcting invalid years and dates. This is particularly useful for mobile devices where typing can be slower.
// Default 3000ms timeout$('#datepicker').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD'// autoCorrectionTimeout: 3000 (default)});$('#mobile-datepicker').multiCalendarDatePicker({calendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD',rtl: true,autoCorrectionTimeout: 4000// Slower for touch typing});$('#desktop-datepicker').multiCalendarDatePicker({calendar: 'hijri',locale: 'ar',format: 'YYYY/MM/DD',rtl: true,autoCorrectionTimeout: 1500// Faster for keyboard typing});$('#accessible-datepicker').multiCalendarDatePicker({calendar: 'gregorian',locale: 'en',format: 'YYYY/MM/DD',autoCorrectionTimeout: 5000// Extra time for users who type slowly});Year Input Validation: When typing in the datepicker's year input field
- 4-digit invalid years: Corrected immediately
- 3-digit invalid years: Corrected after timeout
- Valid years: Updated immediately
Attached Input Validation: When typing in the main textbox
- Invalid dates: Corrected after timeout
- Valid dates: Processed immediately
Timeout Behavior:
- Timer resets on each keystroke
- Only triggers when user stops typing
- Prevents excessive corrections during typing
:root {
--mcdp-primary-color:#007bff;
--mcdp-background:#ffffff;
--mcdp-text-color:#333333;
--mcdp-border-color:#dee2e6;
--mcdp-hover-color:#f8f9fa;
--mcdp-selected-color:#007bff;
--mcdp-today-color:#28a745;
--mcdp-disabled-color:#6c757d;
}.mcdp-theme-dark {
--mcdp-background:#343a40;
--mcdp-text-color:#ffffff;
--mcdp-border-color:#495057;
--mcdp-hover-color:#495057;
}The datepicker includes comprehensive input validation that works in real-time as users type:
// Automatic validation examples"2025/19/24"→"2025/12/24"(monthcorrected)"2025/02/30"→"2025/02/28"(daycorrectedfornon-leapyear)"1401/12/30"→Valid(leapyearinHijricalendar)"1402/12/30"→"1402/12/29"(correctedfornon-leapyear)"25:70:90"→"25:59:59"(timevaluescorrected)// Year validation examples"1104"inPersiancalendar→"1200"(correctedtominimumvalidyear)"1600"inPersiancalendar→"1500"(correctedtomaximumvalidyear)"2025"inGregoriancalendar→Valid(within1900-2100range)- Month: 1-12 (auto-corrected if out of range)
- Day: 1 to maximum days in month (considering leap years)
- Year: Calendar-specific ranges (Gregorian: 1900-2100, Persian: 1200-1500, Hijri: 1000-2000)
- Hour: 0-23 (24-hour) or 1-12 (12-hour format)
- Minute/Second: 0-59 (auto-corrected if out of range)
- Always Visible: Year input in datepicker title is always visible and editable (not just on hover)
- Real-Time Validation: Year validation happens during typing, not just on blur
- Auto-Correction: Invalid years are automatically corrected to nearest valid value
- Bidirectional Sync: Changes in datepicker year input automatically update the attached textbox
- Visual Feedback: Red border for invalid years, normal styling for valid years
- 🟡 Yellow Pulse: Input was corrected automatically
- 🔴 Red Shake: Input is invalid and couldn't be corrected
- 🔴 Red Border: Year input shows red border for invalid years
- ✅ Green: Input is valid
- 300ms debounce prevents excessive validation during typing
- Efficient algorithms for leap year calculations
- Minimal DOM updates for smooth user experience
- Smart validation: Only validates complete years (4+ digits) during typing
The year field in the datepicker title is now always visible and editable, providing immediate access to year modification:
// Year input is always visible in the datepicker title// Users can click and edit the year directly$('#datepicker').multiCalendarDatePicker({calendar: 'persian',locale: 'fa',format: 'YYYY/MM/DD'});Year validation happens during typing, providing immediate feedback:
// Examples of real-time year validation// Persian Calendar (range: 1200-1500)"1104"→Auto-correctsto"1200"(minimumvalidyear)"1600"→Auto-correctsto"1500"(maximumvalidyear)"1403"→Valid(withinrange)// Gregorian Calendar (range: 1900-2100)"1800"→Auto-correctsto"1900"(minimumvalidyear)"2200"→Auto-correctsto"2100"(maximumvalidyear)"2024"→Valid(withinrange)// Hijri Calendar (range: 1000-2000)"900"→Auto-correctsto"1000"(minimumvalidyear)"2100"→Auto-correctsto"2000"(maximumvalidyear)"1445"→Valid(withinrange)Changes in the datepicker year input automatically update the attached textbox:
// When user changes year in datepicker title// 1. Year input updates (e.g., 1403 → 1404)// 2. Calendar display updates// 3. Attached textbox updates (e.g., "1403/01/15" → "1404/01/15")// 4. mcdp:change event fires with new date- Normal State: Year input has visible border and background
- Invalid Year: Red border appears for out-of-range years
- Valid Year: Normal styling for years within valid range
- Auto-Correction: Invalid years are automatically corrected to nearest valid value
- During Typing: Only validates complete years (4+ digits) to avoid premature corrections
- On Blur: Full validation and correction for any remaining invalid input
- Performance: Optimized to prevent excessive validation during typing
$('#datepicker').on('mcdp:init',function(e,data){console.log('DatePicker initialized',data.instance);});$('#datepicker').on('mcdp:show',function(e){console.log('DatePicker shown');});$('#datepicker').on('mcdp:hide',function(e){console.log('DatePicker hidden');});$('#datepicker').on('mcdp:select',function(e,data){console.log('Date selected',data.date,data.formatted);});$('#datepicker').on('mcdp:change',function(e,data){console.log('Date changed',data.date,data.formatted);});$('#datepicker').on('mcdp:clear',function(e){console.log('Date cleared');});$('#datepicker').on('mcdp:today',function(e,data){console.log('Today set',data.date);});$('#datepicker').on('mcdp:timeChange',function(e,data){console.log('Time changed',data.date,data.formatted);});$('#datepicker').on('mcdp:error',function(e,data){console.log('Error occurred',data.error);});- Chrome: 60+
- Firefox: 55+
- Safari: 12+
- Edge: 79+
- Mobile Browsers: iOS Safari, Chrome Mobile
- IE: 11+ (with graceful degradation)
Datepicker not showing
- Check if jQuery is loaded
- Verify CSS file is included
- Check browser console for errors
RTL not working
- Ensure
rtl: trueis set for Persian/Hijri calendars - Check if RTL CSS is properly loaded
- Ensure
Date parsing errors
- Verify date format matches input
- Check if date is within supported range
- Ensure calendar type is correct
Leap year issues
- Clear browser cache (Ctrl+Shift+R)
- Verify you're using the latest version
- Check if date is in supported range
Manual input validation
- Invalid dates are automatically corrected
- Visual feedback shows corrections (yellow pulse) or errors (red shake)
- Real-time validation with 300ms debounce for performance
// Enable debug logging$('#datepicker').multiCalendarDatePicker({debug: true,// ... other options});This project is licensed under the MIT License - see the LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
For issues, questions, or contributions:
- Create an issue on GitHub
- Check the documentation
- Review the examples in
index.html
- ✅ Configurable Auto-Correction Timeout - New
autoCorrectionTimeoutparameter for customizable validation timing - ✅ Mobile Optimization - Number input spinners now visible on mobile devices
- ✅ Custom RTL Input Class - New
.mcdp-rtl-inputclass for better RTL input control - ✅ Consistent Timeout Behavior - Both year input and attached input use same configurable timeout
- ✅ Editable Year Input - Year field in datepicker title is now always visible and editable (not just on hover)
- ✅ Real-Time Year Validation - Year validation happens during typing, not just on blur
- ✅ Bidirectional Sync - Changes in datepicker year input automatically update the attached textbox
- ✅ Year Range Validation - Automatic correction of invalid years to nearest valid value
- ✅ Month Name Updates - Month name in title now updates correctly after year changes and month navigation
- ✅ Fixed Hijri leap year detection - Now correctly shows 30 days for month 12 in leap years (1401, 1406, 1411, etc.)
- ✅ Disabled incorrect moment function - Forces use of verified leap year arrays for accuracy
- ✅ Enhanced input validation - Real-time validation with auto-correction for manual date/time input
- ✅ Visual feedback system - Yellow pulse for corrections, red shake for invalid inputs
- ✅ Verified leap year algorithm (1200-1500)
- ✅ Fixed leap year discrepancies (1393, 1403, etc.)
- ✅ Accurate leap year detection using verified data
- ✅ Corrected "today" calculation - Fixed Persian date display
- ✅ Extended year range from 1-1500 to 1000-2000
- ✅ Improved leap year algorithm with better cycle calculation
- ✅ Fixed date parsing for years after 1500
- ✅ Enhanced fallback algorithm for years outside verified range
- ✅ Fixed month 12 leap year display - Now correctly shows 30 days in leap years
- ✅ Enhanced data attributes support for all configuration options
- ✅ Better RTL support with improved layout and custom
.mcdp-rtl-inputclass - ✅ Improved time picker integration with localized labels
- ✅ Fixed event handling for better user experience
- ✅ Enhanced date validation with extended ranges
- ✅ Manual input validation - Validates month (1-12), day (1-max for month), hour (0-23/1-12), minute/second (0-59)
- ✅ Configurable validation timing - Customizable timeout for auto-correction (default 3000ms)
- ✅ Textbox synchronization - Picker UI always reflects textbox value
- ✅ Year input styling - Always visible year input with proper borders and hover effects
- ✅ Smart validation timing - Only validates complete years (4+ digits) during typing for better UX
- ✅ Mobile-friendly spinners - Number input spinners visible on all devices
- ✅ Consistent timeout behavior - Unified timeout system for all auto-corrections
- ✅ Fixed year parsing (1591 showing as 159)
- ✅ Fixed date reflection for typed dates after 1500
- ✅ Fixed leap year detection for all calendar types
- ✅ Fixed time formatting and parsing issues
- ✅ Fixed RTL arrow icons with proper flipping
- ✅ Fixed calendar key errors - Corrected uppercase/lowercase calendar constants
- ✅ Fixed programmatic show - Resolved "Show Picker" button conflicts
- ✅ Fixed month name updates - Month name now updates correctly after year changes and month navigation
- ✅ Fixed year input visibility - Year input is now always visible instead of only on hover
- ✅ Fixed year validation timing - Year validation now happens during typing, not just on blur
- ✅ Fixed textbox synchronization - Year changes in datepicker now properly update the attached textbox
Multi-Calendar DatePicker - Supporting multiple calendar systems with accuracy and precision! 🌍📅