npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

persian-calendar-suite

v1.5.2

Published

Complete Persian (Jalali/Shamsi) calendar suite for React with datepicker, range picker, time range picker, event calendar, and timeline

Readme

Persian Calendar Suite

A comprehensive Persian (Jalali/Shamsi) calendar suite for React with datepicker, range picker, event calendar, and multiple output formats.

npm version License: MIT

Live Demo | Documentation

Table of Contents

Installation

npm install persian-calendar-suite

or with yarn:

yarn add persian-calendar-suite

Compatibility

  • React: +16.8 (Hooks required)
  • React DOM: +16.8
  • Next.js: +12 (Client-side components)
  • Node: 12+

Peer Dependencies

npm install react react-dom

Quick Start

React

import React, { useState } from 'react';
import { PersianDateTimePicker } from 'persian-calendar-suite';

function App() {
  const [date, setDate] = useState(null);

  return (
    <PersianDateTimePicker
      value={date}
      onChange={setDate}
    />
  );
}

Next.js

'use client'; // Required for Next.js 13+ App Router

import { useState } from 'react';
import { PersianDateTimePicker } from 'persian-calendar-suite';

export default function Page() {
  const [date, setDate] = useState(null);

  return (
    <PersianDateTimePicker
      value={date}
      onChange={setDate}
    />
  );
}

Components

PersianDateTimePicker

Single date and time picker with Shamsi calendar.

Import

import { PersianDateTimePicker } from 'persian-calendar-suite';

Basic Usage

const [value, setValue] = useState(null);

<PersianDateTimePicker
  value={value}
  onChange={setValue}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | string \| number \| null | null | Selected date value | | onChange | (value: string \| number) => void | - | Callback when date changes | | showTime | boolean | true | Show time picker | | minuteStep | number | 1 | Minute step interval | | outputFormat | 'iso' \| 'shamsi' \| 'gregorian' \| 'hijri' \| 'timestamp' | 'iso' | Output format | | showFooter | boolean | true | Show OK/Cancel buttons | | theme | ThemeObject | {} | Theme customization | | disabledHours | number[] | [] | Array of disabled hours | | minDate | string \| Date | null | Minimum selectable date | | maxDate | string \| Date | null | Maximum selectable date | | persianNumbers | boolean | false | Display numbers in Persian digits | | rtlCalendar | boolean | false | Start calendar from right (Sunday first) |

Example with Options

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  showTime={true}
  outputFormat="shamsi"
  showFooter={false}
  theme={{
    primaryColor: '#6366f1',
    circularDates: true
  }}
/>

PersianDateRangePicker

Date range picker with dual calendars.

Import

import { PersianDateRangePicker } from 'persian-calendar-suite';

Basic Usage

const [range, setRange] = useState(null);

<PersianDateRangePicker
  value={range}
  onChange={setRange}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | [string, string] \| null | null | Selected range [start, end] | | onChange | (value: [string, string]) => void | - | Callback when range changes | | placeholder | [string, string] | ['تاریخ شروع', 'تاریخ پایان'] | Placeholder text | | disabled | boolean | false | Disable the picker | | outputFormat | 'iso' \| 'shamsi' \| 'gregorian' \| 'hijri' \| 'timestamp' | 'iso' | Output format | | showFooter | boolean | true | Show OK/Cancel buttons | | theme | ThemeObject | {} | Theme customization | | minDate | string \| Date | null | Minimum selectable date | | maxDate | string \| Date | null | Maximum selectable date | | persianNumbers | boolean | false | Display numbers in Persian digits | | rtlCalendar | boolean | false | Start calendar from right (Sunday first) |

Example with Options

<PersianDateRangePicker
  value={range}
  onChange={setRange}
  placeholder={['Start', 'End']}
  outputFormat="timestamp"
  theme={{
    primaryColor: '#10b981'
  }}
/>

PersianTimePicker

Standalone time picker component with range support.

Import

import { PersianTimePicker } from 'persian-calendar-suite';

Basic Usage

const [time, setTime] = useState('');

<PersianTimePicker
  value={time}
  onChange={setTime}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | string \| string[] | '' | Selected time value (HH:MM or [HH:MM, HH:MM] for range) | | onChange | (value: string \| string[]) => void | - | Callback when time changes | | defaultValue | string \| 'now' | null | Default time ('now' or 'HH:MM') | | minuteStep | number | 1 | Minute step interval | | disabledHours | number[] | [] | Array of disabled hours | | placeholder | string | 'انتخاب زمان' | Placeholder text | | persianNumbers | boolean | false | Display numbers in Persian digits | | isRange | boolean | false | Enable time range selection | | theme | ThemeObject | {} | Theme customization |

Example with Options

<PersianTimePicker
  value={time}
  onChange={setTime}
  defaultValue="now"
  minuteStep={15}
  disabledHours={[0, 1, 2, 22, 23]}
  theme={{
    primaryColor: '#10b981'
  }}
/>

Time Range Picker

const [timeRange, setTimeRange] = useState([]);

<PersianTimePicker
  value={timeRange}
  onChange={setTimeRange}
  isRange={true}
  minuteStep={15}
/>
// Output: ["09:00", "17:00"]

PersianTimeline

Timeline component for displaying chronological events with Persian dates.

Import

import { PersianTimeline } from 'persian-calendar-suite';

Basic Usage

const [events] = useState([
  {
    id: 1,
    date: '2025-12-10',
    time: '14:30',
    title: 'Project Started',
    description: 'Initial project setup and planning',
    color: '#10b981',
    icon: '🚀'
  },
  {
    id: 2,
    date: '2025-12-15',
    title: 'Milestone Reached',
    color: '#6366f1',
    image: '/milestone.jpg'
  }
]);

<PersianTimeline
  events={events}
  onEventClick={(event) => console.log(event)}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | events | Event[] | [] | Array of timeline events | | onEventClick | (event: Event) => void | - | Callback when event is clicked | | direction | 'vertical' \| 'horizontal' | 'vertical' | Timeline orientation | | markerShape | 'circular' \| 'rect' | 'circular' | Shape of event markers | | showIcons | boolean | true | Show event icons/images | | alternating | boolean | true | Alternate event sides (vertical only) | | persianNumbers | boolean | false | Display numbers in Persian digits | | theme | ThemeObject | {} | Theme customization |

Event Object

{
  id: number;           // Unique identifier
  date: string;         // ISO date string (YYYY-MM-DD)
  time?: string;        // Optional time (HH:mm)
  title: string;        // Event title
  description?: string; // Event description
  color?: string;       // Event color (hex)
  icon?: string | ReactNode; // Event icon (emoji or component)
  image?: string;       // Event image URL
}

Timeline Types

Vertical Timeline (default)

<PersianTimeline
  events={events}
  direction="vertical"
  alternating={true}
/>

Horizontal Timeline

<PersianTimeline
  events={events}
  direction="horizontal"
/>

Custom Markers

<PersianTimeline
  events={events}
  markerShape="rect"
  showIcons={true}
/>

Advanced Example

const timelineEvents = [
  {
    id: 1,
    date: '2025-01-15',
    time: '09:00',
    title: 'Project Kickoff',
    description: 'Initial team meeting and project planning session',
    color: '#10b981',
    icon: '🎯'
  },
  {
    id: 2,
    date: '2025-02-20',
    title: 'Design Phase Complete',
    description: 'UI/UX designs approved and ready for development',
    color: '#6366f1',
    image: '/design-preview.jpg'
  },
  {
    id: 3,
    date: '2025-03-30',
    time: '16:00',
    title: 'Beta Release',
    description: 'First beta version released to testing team',
    color: '#f59e0b',
    icon: <svg>...</svg>
  }
];

<PersianTimeline
  events={timelineEvents}
  direction="vertical"
  markerShape="circular"
  alternating={true}
  persianNumbers={true}
  onEventClick={(event) => {
    console.log('Timeline event clicked:', event);
  }}
  theme={{
    primaryColor: '#6366f1',
    lineColor: '#e5e7eb',
    markerSize: '16px',
    eventRadius: '12px'
  }}
/>

PersianMoment

Jalali date arithmetic utility for date calculations and conversions.

Import

import persianMoment from 'persian-calendar-suite/PersianMoment';

Basic Usage

// Create Persian dates
const m1 = persianMoment('1404/12/30', 'jYYYY/jMM/jDD');
const m2 = persianMoment('1404/01/02', 'jYYYY/jMM/jDD');

// Calculate differences
console.log(m2.diff(m1, 'day')); // 3
console.log(m2.diff(m1, 'jMonth')); // 1

// Format conversion
console.log(m1.format('YYYY/MM/DD')); // 2025/03/19
console.log(m1.format('jYYYY/jMM/jDD')); // 1404/12/30

Supported Input Formats

  • Jalali: '1404/12/30' with format 'jYYYY/jMM/jDD'
  • Gregorian: '2025/03/19' with format 'YYYY/MM/DD'
  • ISO: '2025-03-19' (no format needed)

Methods

diff(other, unit, outputFormat)

  • unit: 'day', 'jDay', 'jMonth', 'jYear'
  • outputFormat: 'number', 'persian', 'persian-text'

format(format)

  • format: 'jYYYY/jMM/jDD', 'YYYY/MM/DD', 'iso'

Output Formats

const diff = m2.diff(m1, 'day', 'persian-text');
// Output: "۳ روز"

const diff2 = m2.diff(m1, 'jMonth', 'persian');
// Output: "۱"

PersianCalendar

Full-featured calendar with event management (like FullCalendar).

Import

import { PersianCalendar } from 'persian-calendar-suite';

Basic Usage

const [events, setEvents] = useState([]);

<PersianCalendar
  events={events}
  onEventCreate={(event) => setEvents([...events, event])}
  onEventUpdate={(updated) => setEvents(events.map(e => e.id === updated.id ? updated : e))}
  onEventDelete={(deleted) => setEvents(events.filter(e => e.id !== deleted.id))}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | events | Event[] | [] | Array of event objects | | onEventCreate | (event: Event) => void | - | Callback when event is created | | onEventUpdate | (event: Event) => void | - | Callback when event is updated | | onEventDelete | (event: Event) => void | - | Callback when event is deleted | | onEventClick | (event: Event) => void | - | Callback when event is clicked | | initialView | 'day' \| 'week' \| 'month' | 'month' | Initial view mode | | editable | boolean | true | Enable event creation/editing | | showWeekends | boolean | true | Show weekend days | | headerFormat | 'full' \| 'short' | 'full' | Header format | | disabledHours | number[] | [] | Array of disabled hours (0-23) | | theme | ThemeObject | {} | Theme customization |

Event Object

{
  id: number;           // Unique identifier
  date: string;         // ISO date string (YYYY-MM-DD)
  startTime: string;    // Start time (HH:mm)
  endTime: string;      // End time (HH:mm)
  title: string;        // Event title
  color: string;        // Event color (hex)
  description?: string; // Event description (optional)
  isAllDay?: boolean;   // All-day event flag
  isRecurring?: boolean; // Recurring event flag
  recurringType?: 'daily' | 'weekly' | 'monthly' | 'yearly'; // Recurrence pattern
  recurringEnd?: string; // End date for recurring events
  isMultiDay?: boolean; // Multi-day event flag
  endDate?: string;     // End date for multi-day events
  readOnly?: boolean;   // Read-only event flag (cannot be edited)
}

Creating Events

Day View: Click on hour slots (00:00 - 23:00)

// User clicks on 09:00 slot
// Modal opens with startTime: "09:00", endTime: "10:00"

Week View: Click on time cells for any day

// User clicks on Tuesday 14:00
// Modal opens for that specific day and time

Month View: Click on day cells

// User clicks on day 15
// Modal opens with default time 09:00

Editing Events

Click on any existing event to open the edit modal:

  • Modify title, start/end time, color, description
  • Delete button available in modal
  • Changes trigger onEventUpdate callback

Advanced Features

Event Types:

  • All-day events: Check "رویداد تمام روز" to create events spanning entire days
  • Multi-day events: Check "رویداد چند روزه" to create events spanning multiple days
  • Recurring events: Check "رویداد تکراری" for daily/weekly/monthly/yearly repetition
  • Today button: Quick navigation to current date
  • Event tooltips: Hover over events to see full details

Overlap Detection:

  • Shows orange badge with count (e.g., "2 تداخل")
  • Highlights slots with light orange background
  • Displays events side-by-side in day view

Visual Indicators:

  • All-day events: Gradient background with dashed border and sun icon
  • Recurring events: Refresh icon after title
  • Multi-day and recurring events are mutually exclusive

Full Example

const [events, setEvents] = useState([
  {
    id: 1,
    date: '2025-12-10',
    startTime: '09:00',
    endTime: '10:00',
    title: 'Team Meeting',
    color: '#10b981',
    description: 'Weekly team sync',
    isRecurring: true,
    recurringType: 'weekly'
  },
  {
    id: 2,
    date: '2025-12-11',
    title: 'Company Event',
    color: '#ef4444',
    isAllDay: true
  }
]);

<PersianCalendar
  events={events}
  onEventCreate={(event) => {
    console.log('Created:', event);
    setEvents([...events, event]);
  }}
  onEventUpdate={(updated) => {
    console.log('Updated:', updated);
    setEvents(events.map(e => e.id === updated.id ? updated : e));
  }}
  onEventDelete={(deleted) => {
    console.log('Deleted:', deleted);
    setEvents(events.filter(e => e.id !== deleted.id));
  }}
  onEventClick={(event) => {
    console.log('Clicked:', event);
  }}
  initialView="day"
  editable={true}
  theme={{
    primaryColor: '#6366f1'
  }}
  disabledHours={[0, 1, 2, 22, 23]} // Disable late night/early morning hours
/>

Theme Customization

All components support theme customization via the theme prop.

Theme Object

{
  primaryColor?: string;      // Primary color (default: '#1890ff')
  backgroundColor?: string;   // Background color (default: '#ffffff')
  textColor?: string;         // Text color (default: '#000000')
  borderColor?: string;       // Border color (default: '#d9d9d9')
  hoverColor?: string;        // Hover color (default: '#f0f0f0')
  selectedTextColor?: string; // Selected text color (default: '#ffffff')
  circularDates?: boolean;    // Circular date cells (default: false)
  // Calendar specific
  headerBg?: string;          // Header background (default: '#fafafa')
  eventRadius?: string;       // Event border radius (default: '6px')
  shadow?: string;            // Shadow (default: '0 2px 8px rgba(0,0,0,0.08)')
}

Example

const customTheme = {
  primaryColor: '#6366f1',
  backgroundColor: '#ffffff',
  textColor: '#1f2937',
  borderColor: '#e5e7eb',
  hoverColor: '#f3f4f6',
  circularDates: true
};

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  theme={customTheme}
/>

Output Formats

ISO 8601 (default)

2025-12-10T14:30:00

Shamsi

1404/09/20 14:30

Gregorian

2025/12/10 14:30

Hijri

1446/06/08 14:30

Timestamp

1702217400000

Changing Format

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  outputFormat="shamsi"  // or 'iso', 'gregorian', 'hijri', 'timestamp'
/>

Programmatic Control

Reading Data

All components are controlled - you manage the state:

const [date, setDate] = useState(null);
const [range, setRange] = useState(null);
const [events, setEvents] = useState([]);

// Read current values anytime
console.log('Current date:', date);
console.log('Current range:', range);
console.log('All events:', events);

Setting Data Programmatically

Set Date/Time

const [date, setDate] = useState(null);

// Set to current date
setDate(new Date().toISOString());

// Set specific date (ISO format)
setDate('2025-12-10T14:30:00');

// Set via timestamp
setDate(1702217400000);

// Clear date
setDate(null);

Set Date Range

const [range, setRange] = useState(null);

// Set range
setRange(['2025-12-01T00:00:00', '2025-12-31T23:59:59']);

// Set last 7 days
const end = new Date();
const start = new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000);
setRange([start.toISOString(), end.toISOString()]);

// Clear range
setRange(null);

Manage Events

const [events, setEvents] = useState([]);

// Add event programmatically
const addEvent = () => {
  const newEvent = {
    id: Date.now(),
    date: '2025-12-15',
    startTime: '10:00',
    endTime: '11:00',
    title: 'New Meeting',
    color: '#3b82f6',
    description: 'Auto-created event'
  };
  setEvents([...events, newEvent]);
};

// Update specific event
const updateEvent = (id, updates) => {
  setEvents(events.map(e => 
    e.id === id ? { ...e, ...updates } : e
  ));
};

// Delete event
const deleteEvent = (id) => {
  setEvents(events.filter(e => e.id !== id));
};

// Clear all events
setEvents([]);

// Load events from API
fetch('/api/events')
  .then(res => res.json())
  .then(data => setEvents(data));

Dynamic Updates

Auto-select Today

const [date, setDate] = useState(new Date().toISOString());

// Update to today on mount
useEffect(() => {
  setDate(new Date().toISOString());
}, []);

Filter Events by Date

const [events, setEvents] = useState([...]);
const [selectedDate, setSelectedDate] = useState('2025-12-10');

// Get events for specific date
const todayEvents = events.filter(e => e.date === selectedDate);

// Get events in date range
const rangeEvents = events.filter(e => 
  e.date >= '2025-12-01' && e.date <= '2025-12-31'
);

Bulk Operations

// Change all event colors
const changeAllColors = (newColor) => {
  setEvents(events.map(e => ({ ...e, color: newColor })));
};

// Shift all events by 1 hour
const shiftEvents = () => {
  setEvents(events.map(e => ({
    ...e,
    startTime: addHour(e.startTime),
    endTime: addHour(e.endTime)
  })));
};

// Delete events by criteria
const deletePastEvents = () => {
  const today = new Date().toISOString().split('T')[0];
  setEvents(events.filter(e => e.date >= today));
};

Responding to Changes

// Track date changes
const [date, setDate] = useState(null);

useEffect(() => {
  if (date) {
    console.log('Date changed to:', date);
    // Trigger API call, validation, etc.
  }
}, [date]);

// Track event changes
const [events, setEvents] = useState([]);

useEffect(() => {
  console.log('Events updated:', events.length);
  // Save to localStorage, sync with backend, etc.
  localStorage.setItem('events', JSON.stringify(events));
}, [events]);

Complete Interactive Example

function App() {
  const [events, setEvents] = useState([]);
  const [selectedDate, setSelectedDate] = useState(null);

  // Add event via button
  const addQuickEvent = () => {
    setEvents([...events, {
      id: Date.now(),
      date: new Date().toISOString().split('T')[0],
      startTime: '09:00',
      endTime: '10:00',
      title: 'Quick Event',
      color: '#10b981'
    }]);
  };

  // Jump to specific date
  const jumpToDate = (dateStr) => {
    setSelectedDate(dateStr);
  };

  // Export events
  const exportEvents = () => {
    const json = JSON.stringify(events, null, 2);
    console.log(json);
    // Download or send to API
  };

  // Import events
  const importEvents = (jsonData) => {
    setEvents(JSON.parse(jsonData));
  };

  return (
    <div>
      <button onClick={addQuickEvent}>Add Event</button>
      <button onClick={() => jumpToDate('2025-12-25')}>Jump to Dec 25</button>
      <button onClick={exportEvents}>Export</button>
      
      <PersianCalendar
        events={events}
        onEventCreate={(e) => setEvents([...events, e])}
        onEventUpdate={(e) => setEvents(events.map(ev => ev.id === e.id ? e : ev))}
        onEventDelete={(e) => setEvents(events.filter(ev => ev.id !== e.id))}
      />
    </div>
  );
}

Examples

DateTime Picker with Time

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  showTime={true}
  minuteStep={15}
/>

Date Only (No Time)

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  showTime={false}
/>

Date Restrictions

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  minDate="2025-01-01"
  maxDate="2025-12-31"
/>

Time Picker with Default

<PersianTimePicker
  value={time}
  onChange={setTime}
  defaultValue="now"
  minuteStep={15}
/>

Auto-close (No Footer)

<PersianDateTimePicker
  value={value}
  onChange={setValue}
  showFooter={false}
/>

Range Picker with Timestamp

<PersianDateRangePicker
  value={range}
  onChange={setRange}
  outputFormat="timestamp"
/>

Calendar with Custom Theme

<PersianCalendar
  events={events}
  onEventCreate={handleCreate}
  onEventUpdate={handleUpdate}
  onEventDelete={handleDelete}
  theme={{
    primaryColor: '#ef4444',
    headerBg: '#fee2e2',
    eventRadius: '12px'
  }}
/>

Read-only Calendar

<PersianCalendar
  events={events}
  onEventClick={handleClick}
  editable={false}
/>

Mobile Support

All components are fully responsive and optimized for mobile devices:

  • Touch-friendly interface with larger tap targets
  • Adaptive layouts for small screens
  • Centered modals on mobile
  • Optimized spacing and font sizes
  • Smooth scrolling within pickers

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)
  • Opera (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile, Samsung Internet)

Persian Holiday Integration

All calendar components support Persian holiday integration via the Time.ir API:

<PersianDateTimePicker showHolidays={true} />
<PersianDateRangePicker showHolidays={true} />
<PersianCalendar showHolidays={true} />

Holiday Features

  • Automatic Loading: Holidays load automatically for the current month
  • Visual Highlighting: Holiday dates appear with red background
  • Read-Only: Holidays cannot be edited or deleted
  • Clickable: Click holidays to see details
  • API Integration: Uses Time.ir Persian calendar API with CORS proxy

License

MIT © BBBird1450

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Support

For issues and questions:

Changelog

See CHANGELOG.md for release history.