Skip to main content

Django NPDateTime

Nepali Date Field and Date Picker Widget for Django

A Django package that provides custom model fields, form fields, and a modern date picker widget for working with Nepali (Bikram Sambat) dates. Powered by the high-performance npdatetime library.

Features

Custom Model Fields - NepaliDateField and NepaliDateTimeField with direct access to fiscal year/quarter properties

Fiscal Year Support - Built-in support for Nepali Fiscal Years (e.g., 2080/81) and quarters

🎨 Beautiful Date Picker Widget - Modern, responsive date picker with both BS and AD modes

🌐 Bilingual Support - English and Nepali language options

🔄 Auto Conversion - Seamless conversion between Bikram Sambat and Gregorian calendars

📝 Template Tags & Filters - Rich template tags for date formatting and conversion

High Performance - Built on Rust for maximum speed

🎯 Django Integration - Works seamlessly with Django's forms and admin

🔒 Enterprise Options - Disable past dates, weekends, holidays, specific dates/weekdays, and custom validation callbacks

For Developers

If you're contributing to this package, the JavaScript/CSS files are synchronized from ../javascript/. When you update the date picker:

# Run the build script to sync assets
python build_assets.py
# or
./build_assets.sh

This copies:

  • picker.jsstatic/npdt/js/picker.min.js
  • picker.cssstatic/npdt/css/picker.css
  • pkg/static/npdt/js/pkg/

Installation

pip install django-npdt
# or with uv
uv add django-npdt

Quick Start

1. Add to INSTALLED_APPS

# settings.py
INSTALLED_APPS = [
    ...
    'npdt',
    ...
]

2. Use in Models

from django.db import models
from npdt import NepaliDateField

class Person(models.Model):
    name = models.CharField(max_length=100)
    birth_date_bs = NepaliDateField()
    
    def __str__(self):
        return f"{self.name} - {self.birth_date_bs}"

3. Use in Forms

from django import forms
from npdt import NepaliDateField, NepaliDatePickerWidget

class PersonForm(forms.Form):
    name = forms.CharField(max_length=100)
    birth_date = NepaliDateField(
        mode='BS',
        language='np',
        label='जन्म मिति'
    )

4. Use Template Tags

{% load nepali_date %}

<p>Birth Date (BS): {{ person.birth_date_bs }}</p>
<p>Birth Date (AD): {{ person.birth_date_bs|to_gregorian_date }}</p>
<p>Fiscal Year: {{ person.birth_date_bs.fiscal_year }}</p>
<p>Today in BS: {% nepali_date_today %}</p>
<p>Month: {{ 1|nepali_month_name:"np" }}</p>
<p>Inline Picker: {% nepali_date_picker "event_date" theme="dark" %}</p>

Usage Guide

Model Fields

NepaliDateField

Stores Nepali dates in YYYY-MM-DD format.

from npdt.models import NepaliDateField

class Event(models.Model):
    event_name = models.CharField(max_length=200)
    event_date_bs = NepaliDateField(mode='BS', language='np')  # Configurable!

NepaliDateTimeField

Stores Nepali dates with time in YYYY-MM-DD HH:MM:SS format.

from npdt.models import NepaliDateTimeField

class Meeting(models.Model):
    title = models.CharField(max_length=200)
    scheduled_at_bs = NepaliDateTimeField()

Form Fields and Widgets

Basic Date Picker

from npdt.forms import NepaliDateField

class PersonForm(forms.Form):
    birth_date = NepaliDateField(
        mode='BS',           # 'BS' or 'AD'
        language='en',       # 'en' or 'np'
    )

Advanced Configuration

from npdt.widgets import NepaliDatePickerWidget

class EventForm(forms.Form):
    event_date = forms.CharField(
        widget=NepaliDatePickerWidget(
            mode='BS',
            language='np',
            theme='dark',
            show_today_button=True,
            show_clear_button=True,
            min_date='2080-01-01',
            max_date='2085-12-30',
        )
    )

Enterprise Configuration

from npdt.widgets import NepaliDatePickerWidget

class LeaveForm(forms.Form):
    # Future dates only, no weekends or holidays
    leave_date = forms.CharField(
        widget=NepaliDatePickerWidget(
            mode='BS',
            disable_past_dates=True,
            disable_weekends=True,
            disable_holidays=True,
        )
    )

Using widget_kwargs with Form Fields

NepaliDateField and NepaliDateRangeField accept widget_kwargs to pass options through to the widget:

from npdt.forms import NepaliDateField

class LeaveForm(forms.Form):
    start_date = NepaliDateField(
        mode='BS',
        widget_kwargs={
            'disable_past_dates': True,
            'disable_holidays': True,
            'disable_weekends': True,
            'show_tithi': True,
        }
    )

Date Range Picker

from npdt.forms import NepaliDateRangeField

class ReportForm(forms.Form):
    report_period = NepaliDateRangeField(
        mode='BS',
        language='np'
    )
    
    def clean_report_period(self):
        period = self.cleaned_data['report_period']
        start_date, end_date = period.split(' to ')
        # Process date range
        return period

Template Tags and Filters

Load the template tag library:

{% load nepali_date %}

Convert Gregorian to Nepali

{{ gregorian_date|to_nepali_date }}
{{ gregorian_date|to_nepali_date:"%Y/%m/%d" }}

Convert Nepali to Gregorian

{{ nepali_date|to_gregorian_date }}
{{ nepali_date|to_gregorian_date:"%d/%m/%Y" }}

Format Nepali Date

{{ "2081-01-15"|format_nepali_date:"%Y/%m/%d" }}

Get Month Name

{{ 1|nepali_month_name }}        {# Baisakh #}
{{ 1|nepali_month_name:"np" }}   {# बैशाख #}

Convert to Nepali Numerals

{{ 2081|to_nepali_number }}  {# २०८१ #}

Fiscal Year and Quarter

{{ "2081-01-15"|fiscal_year }}     {# 2080/81 #}
{{ "2081-01-15"|fiscal_quarter }}  {# 3 #}

Get Today's Date

{% nepali_date_today %}
{% nepali_date_today "%Y/%m/%d" %}

Inline Date Picker

{% load nepali_date %}

{# Basic #}
{% nepali_date_picker "event_date" %}

{# With dynamic options #}
{% nepali_date_picker "event_date" disable_past_dates=True %}
{% nepali_date_picker "event_date" disable_weekends=True disable_holidays=True %}
{% nepali_date_picker "event_date" admin_theme=True %}
{% nepali_date_picker "event_date" show_tithi=True %}

Widget Options

The NepaliDatePickerWidget accepts the following options:

Core Options

Option Type Default Description
mode str 'BS' Calendar mode: 'BS' or 'AD'
language str 'en' Interface language: 'en' or 'np'
theme str 'auto' Color theme: 'auto', 'light', 'dark', or 'admin'
format str '%Y-%m-%d' Date format string
include_time bool False Include time picker
show_today_button bool True Show "Today" button
show_clear_button bool True Show "Clear" button
min_date str None Minimum selectable date (YYYY-MM-DD)
max_date str None Maximum selectable date (YYYY-MM-DD)

Dynamic Disabling Options

Option Type Default Description
disabled_dates list[str] [] List of date strings to disable (YYYY-MM-DD)
disabled_days list[int] | str [] Weekday indices to disable. Supports: list [0,6], range "1-5" (Mon–Fri), mixed "1-5,0". 0=Sunday, 1=Monday, ..., 6=Saturday
disable_past_dates bool False Disable all dates before today
disable_weekends bool False Disable weekends (Saturday in BS, Sunday in AD)
disable_holidays bool False Disable holidays from the holiday provider
show_holidays bool True Highlight holidays visually (without disabling)
holiday_provider HolidayProvider None Custom holiday provider. Defaults to NepalPublicHolidays when disable_holidays=True
on_date_disabled str 'prevent' Behavior when a disabled date is selected: 'prevent' or 'warn'
show_tithi bool False Show lunar tithi on date hover
admin_theme bool False Shortcut for theme='admin' — matches Django admin styling

Holiday Provider System

The widget includes an extensible holiday provider system for defining which dates should be treated as holidays.

Built-in: NepalPublicHolidays

Covers major Nepal public holidays for BS years 2070–2100. Automatically used when disable_holidays=True and no custom provider is specified.

from npdt.widgets import NepaliDatePickerWidget

widget = NepaliDatePickerWidget(
    disable_holidays=True,  # Uses NepalPublicHolidays by default
)

Custom Holiday Provider

Subclass HolidayProvider to define your organization's holidays:

from npdt.holidays import HolidayProvider, HolidayDate

class MyCompanyHolidays(HolidayProvider):
    def get_holidays(self, year, month=None):
        return [
            HolidayDate('2082-01-01', 'Company Foundation Day', name_np='कम्पनी स्थापना दिवस'),
            HolidayDate('2082-06-15', 'Annual Retreat', type='optional'),
        ]

widget = NepaliDatePickerWidget(
    holiday_provider=MyCompanyHolidays(),
    disable_holidays=True,
)

CompositeHolidayProvider

Merge multiple providers (e.g., public holidays + company holidays):

from npdt.holidays import CompositeHolidayProvider, NepalPublicHolidays

provider = CompositeHolidayProvider(
    NepalPublicHolidays(),
    MyCompanyHolidays(),
)

widget = NepaliDatePickerWidget(
    holiday_provider=provider,
    disable_holidays=True,
    show_holidays=True,
)

StaticHolidayProvider

Quick one-off configuration without writing a full class:

from npdt.holidays import StaticHolidayProvider

provider = StaticHolidayProvider(
    ['2082-01-01', '2082-09-03'],
    name='Bank Holiday',
)

widget = NepaliDatePickerWidget(
    holiday_provider=provider,
    disable_holidays=True,
)

Django Admin Integration

The date picker automatically integrates with Django admin. Use admin_theme=True to match Django admin's styling:

from django.contrib import admin
from npdt.widgets import NepaliDatePickerWidget

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    list_display = ['name', 'birth_date_bs']
    fields = ['name', 'birth_date_bs']

For explicit admin theme on individual widgets:

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = ['name', 'birth_date_bs']
        widgets = {
            'birth_date_bs': NepaliDatePickerWidget(
                admin_theme=True,
                disable_holidays=True,
            ),
        }

Examples

Complete Model Example

from django.db import models
from npdt.models import NepaliDateField, NepaliDateTimeField

class Employee(models.Model):
    name = models.CharField(max_length=100)
    join_date_bs = NepaliDateField(help_text="Joining date in BS")
    birth_date_bs = NepaliDateField(blank=True, null=True)
    last_login_bs = NepaliDateTimeField(blank=True, null=True)
    
    class Meta:
        ordering = ['-join_date_bs']
    
    def __str__(self):
        return f"{self.name} (Joined: {self.join_date_bs})"

ModelForm Example

from django import forms
from npdt.widgets import NepaliDatePickerWidget
from .models import Employee

class EmployeeForm(forms.ModelForm):
    class Meta:
        model = Employee
        fields = ['name', 'join_date_bs', 'birth_date_bs']
        widgets = {
            'join_date_bs': NepaliDatePickerWidget(
                mode='BS',
                language='np',
                theme='light'
            ),
            'birth_date_bs': NepaliDatePickerWidget(
                mode='BS',
                language='np'
            ),
        }

Production Example: Banking Form

from django import forms
from npdt.widgets import NepaliDatePickerWidget

class FacilityForm(forms.ModelForm):
    class Meta:
        model = CustomerFacility
        fields = ['reference', 'initiated_date', 'approval_date']
        widgets = {
            'initiated_date': NepaliDatePickerWidget(
                mode='AD',
                disable_holidays=True,
            ),
            'approval_date': NepaliDatePickerWidget(
                disable_holidays=True,
            ),
        }

Converting BS ↔ AD in Forms

When storing AD dates in the database but displaying BS in the picker:

from django import forms
from npdt.widgets import NepaliDatePickerWidget

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Convert AD dates to BS for initial display
        for date_field in ('initiated_date', 'approval_date'):
            if date_field in self.fields and self.initial.get(date_field):
                try:
                    from npdatetime import NepaliDate
                    ad_val = self.initial[date_field]
                    if hasattr(ad_val, 'year'):
                        np_date = NepaliDate.from_gregorian(
                            ad_val.year, ad_val.month, ad_val.day
                        )
                        self.initial[date_field] = str(np_date)
                except Exception:
                    pass

    def _clean_bs_date(self, field_name):
        """Convert BS date string from picker to AD date for the model."""
        value = self.cleaned_data.get(field_name)
        if not value:
            return value
        if hasattr(value, 'year') and not isinstance(value, str):
            return value
        try:
            from npdatetime import NepaliDate
            import datetime
            parts = [int(p) for p in str(value).split('-')]
            np_date = NepaliDate(parts[0], parts[1], parts[2])
            gy, gm, gd = np_date.to_gregorian()
            return datetime.date(gy, gm, gd)
        except Exception:
            raise forms.ValidationError(f'Invalid BS date format for {field_name}.')

    def clean_initiated_date(self):
        return self._clean_bs_date('initiated_date')

Template Example

{% load nepali_date %}
<!DOCTYPE html>
<html>
<head>
    <title>Employee Details</title>
</head>
<body>
    <h1>{{ employee.name }}</h1>
    
    <div>
        <strong>Join Date (BS):</strong> 
        {{ employee.join_date_bs|format_nepali_date:"%Y/%m/%d" }}
    </div>
    
    <div>
        <strong>Join Date (AD):</strong> 
        {{ employee.join_date_bs|to_gregorian_date:"%B %d, %Y" }}
    </div>
    
    <div>
        <strong>Birth Month:</strong>
        {{ employee.birth_date_bs|slice:":7"|last|int|nepali_month_name:"np" }}
    </div>
</body>
</html>

Requirements

  • Python >= 3.8
  • Django >= 3.2
  • npdatetime >= 0.1.0

Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile)

License

MIT License - see LICENSE file for details

Contributing

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

Credits

Built on top of:

Support

For bugs and feature requests, please open an issue on GitHub.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_npdt-0.2.7.tar.gz (127.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_npdt-0.2.7-py3-none-any.whl (121.1 kB view details)

Uploaded Python 3

File details

Details for the file django_npdt-0.2.7.tar.gz.

File metadata

  • Download URL: django_npdt-0.2.7.tar.gz
  • Upload date:
  • Size: 127.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for django_npdt-0.2.7.tar.gz
Algorithm Hash digest
SHA256 f89c12273a3c9969b272801c49427e1e0a45e341d9186851cb24a9e7a328b4c1
MD5 26b083b02a61cbb70c75e8ca4de138f7
BLAKE2b-256 6b5132116c98e8c9e727ba01b164aeb383613dbc8b96812a36e1269a1c68a184

See more details on using hashes here.

File details

Details for the file django_npdt-0.2.7-py3-none-any.whl.

File metadata

  • Download URL: django_npdt-0.2.7-py3-none-any.whl
  • Upload date:
  • Size: 121.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for django_npdt-0.2.7-py3-none-any.whl
Algorithm Hash digest
SHA256 371a68613029e8a24c0980bce691ad292e10c5c96c7bca03fa9d492d09ae8fc0
MD5 dd754341ecd84c15dd0a3b182e3a0a66
BLAKE2b-256 956f7b5064b4254545504999281628104a870a53f576c23efc20a9c017fb7c7a

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.10

2 files

0.2.9

2 files

0.2.8

2 files

This release

0.2.7 This release

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page