如何在React版FullCalendar中添加希吉拉日历(Hijri Calendar)并设置其为起始日期(支持月视图与列表月视图)
Hey there! I've worked through similar requirements for integrating Hijri calendar support with FullCalendar in React, so let's walk through exactly how to make your month and listMonth views follow Hijri start dates instead of Gregorian.
First, we need a reliable way to convert between Gregorian and Hijri dates. I recommend hijri-date-converter for its simplicity and lightweight footprint:
npm install hijri-date-converter # or yarn add hijri-date-converter
Create helper functions to map Hijri months to their corresponding Gregorian date ranges—this is the core of making FullCalendar respect Hijri start/end dates:
import { toHijri, toGregorian } from 'hijri-date-converter'; // Get the Gregorian start/end dates for a given Hijri year/month const getHijriMonthGregorianRange = (hijriYear, hijriMonth) => { // Convert Hijri month's first day to Gregorian const firstDayGreg = toGregorian(hijriYear, hijriMonth, 1); // Calculate last day by getting next month's first day and subtracting 1 const nextHijriMonth = hijriMonth === 12 ? 1 : hijriMonth + 1; const nextHijriYear = hijriMonth === 12 ? hijriYear + 1 : hijriYear; const lastDayGreg = toGregorian(nextHijriYear, nextHijriMonth, 1); lastDayGreg.setDate(lastDayGreg.getDate() - 1); // FullCalendar uses exclusive end dates, so add 1 day to the last Gregorian day return { start: new Date(firstDayGreg.getFullYear(), firstDayGreg.getMonth(), firstDayGreg.getDate()), end: new Date(lastDayGreg.getFullYear(), lastDayGreg.getMonth(), lastDayGreg.getDate() + 1) }; }; // Get the current Hijri year/month based on today's Gregorian date const getCurrentHijriYearMonth = () => { const today = new Date(); const hijriToday = toHijri(today.getFullYear(), today.getMonth() + 1, today.getDate()); return { year: hijriToday.year, month: hijriToday.month }; };
Now, we'll set up the FullCalendar component to use our Hijri-derived date ranges for dayGridMonth and listMonth views. We'll manage the current Hijri year/month with React state:
import React, { useState } from 'react'; import FullCalendar from '@fullcalendar/react'; import dayGridPlugin from '@fullcalendar/daygrid'; import listPlugin from '@fullcalendar/list'; const HijriCalendar = () => { const [currentHijri, setCurrentHijri] = useState(getCurrentHijriYearMonth()); const { start, end } = getHijriMonthGregorianRange(currentHijri.year, currentHijri.month); // Customize day cell content to display both Hijri and Gregorian dates const dayCellContent = (info) => { const gregDate = info.date; const hijriDate = toHijri(gregDate.getFullYear(), gregDate.getMonth() + 1, gregDate.getDate()); return ( <div className="calendar-day"> <span className="hijri-day">{hijriDate.day}</span> <span className="gregorian-day">{info.dayText}</span> </div> ); }; return ( <div className="hijri-calendar-container"> {/* Custom Navigation (replace FullCalendar's default Gregorian navigation) */} <div className="calendar-nav"> <button onClick={() => { setCurrentHijri(prev => ({ year: prev.month === 1 ? prev.year - 1 : prev.year, month: prev.month === 1 ? 12 : prev.month - 1 })); }}>← Previous Hijri Month</button> <h3>{`${currentHijri.year} AH, Month ${currentHijri.month}`}</h3> <button onClick={() => { setCurrentHijri(prev => ({ year: prev.month === 12 ? prev.year + 1 : prev.year, month: prev.month === 12 ? 1 : prev.month + 1 })); }}>Next Hijri Month →</button> </div> <FullCalendar plugins={[dayGridPlugin, listPlugin]} initialView="dayGridMonth" // Force views to use our Hijri-derived Gregorian range views={{ dayGridMonth: { type: 'dayGridMonth', visibleRange: { start, end } }, listMonth: { type: 'listMonth', visibleRange: { start, end } } }} dayCellContent={dayCellContent} // Hide default navigation (we're using our custom one) headerToolbar={{ left: '', center: '', right: 'dayGridMonth,listMonth' }} // Optional: Customize event rendering if needed eventContent={(info) => ( <div>{info.event.title}</div> )} /> </div> ); }; export default HijriCalendar;
Add some CSS to make the Hijri date display clean:
.calendar-day { text-align: center; } .hijri-day { font-size: 0.8rem; color: #666; display: block; } .gregorian-day { font-size: 1rem; font-weight: 600; } .calendar-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; padding: 0.5rem; } .calendar-nav button { padding: 0.5rem 1rem; cursor: pointer; }
If your events are stored as Hijri dates, convert them to Gregorian before passing to FullCalendar:
// Example: Convert Hijri events to Gregorian const hijriEvents = [ { title: 'Eid al-Fitr', hijriYear: 1445, hijriMonth: 10, hijriDay: 1 } ]; const gregorianEvents = hijriEvents.map(event => { const gregDate = toGregorian(event.hijriYear, event.hijriMonth, event.hijriDay); return { title: event.title, start: new Date(gregDate.getFullYear(), gregDate.getMonth(), gregDate.getDate()) }; }); // Then pass gregorianEvents to FullCalendar's `events` prop
- FullCalendar operates natively in Gregorian, so we're essentially "tricking" it into displaying Hijri months by mapping them to Gregorian date ranges.
- The custom navigation ensures users move between Hijri months, not Gregorian ones.
- Always test edge cases (like Hijri months with 29 vs 30 days) to ensure date ranges are accurate.
内容的提问来源于stack exchange,提问作者zahra honarvar

