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.js→static/npdt/js/picker.min.jspicker.css→static/npdt/css/picker.csspkg/→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 one timezone-aware instant in the database. With Django USE_TZ=True,
Django persists the value in UTC; the picker converts to and from the selected
local IANA timezone. mode chooses whether the field accepts and displays BS
or AD. The calendar toggle lets users browse either calendar without changing
the stored instant.
from npdt.models import NepaliDateTimeField
class Meeting(models.Model):
title = models.CharField(max_length=200)
scheduled_at = NepaliDateTimeField(mode="BS", timezone_name="Asia/Kathmandu")
The form value 2081-01-01 09:30 is interpreted as Nepal time, converted to
UTC before persistence, and rendered back as Nepal time. Use an explicit IANA
timezone for cross-border deployments. Invalid and ambiguous daylight-saving
wall times are rejected instead of being silently changed.
NepaliDateTimeField now uses a database datetime column. Existing releases
that used its former text column must migrate their legacy values to UTC before
altering the column. Do this with a project-specific data migration because the
legacy value does not record its timezone.
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:
- npdatetime(Rust) - High-performance Nepali datetime library (Rust)
Support
For bugs and feature requests, please open an issue on GitHub.
Metadata
Release files for django-npdt 0.2.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_npdt-0.2.11.tar.gz | 140.5 kB | Without provenance | Details for django_npdt-0.2.11.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_npdt-0.2.11-py3-none-any.whl | Python 3 | none | any | Details for django_npdt-0.2.11-py3-none-any.whl |
Total release size: 274.6 kB
Release files / django_npdt-0.2.11.tar.gz
| Download URL | django_npdt-0.2.11.tar.gz |
|---|---|
| Size | 140.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
39075a630a33630c2e99945e6f19f5e2cd6937ea6cab4c2ab1c5bb4e92c9ddbb
|
|
BLAKE2b-256 checksum How to use checksums |
c8d9367efffdd4b705d5ac26eeeea50ab3cdddeb1264defa21b84c13a6075405
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / django_npdt-0.2.11-py3-none-any.whl
| Download URL | django_npdt-0.2.11-py3-none-any.whl |
|---|---|
| Size | 134.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b9d516c294a7466fa87dfc23148bc6d15ccbf1860e643cd0537d4528dffc7794
|
|
BLAKE2b-256 checksum How to use checksums |
4ddae71a4fd3bba362481eacadcbc66028a4788b25e251fffbeb2dc19f97af4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for django-npdt 0.2.11
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.