django-npdatetime 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_npdatetime-0.1.0.dist-info/METADATA +397 -0
- django_npdatetime-0.1.0.dist-info/RECORD +23 -0
- django_npdatetime-0.1.0.dist-info/WHEEL +5 -0
- django_npdatetime-0.1.0.dist-info/licenses/LICENSE +21 -0
- django_npdatetime-0.1.0.dist-info/top_level.txt +1 -0
- npdatetime_django/__init__.py +22 -0
- npdatetime_django/apps.py +13 -0
- npdatetime_django/forms.py +239 -0
- npdatetime_django/models.py +198 -0
- npdatetime_django/static/npdatetime_django/css/date_picker.css +746 -0
- npdatetime_django/static/npdatetime_django/js/date_picker.min.js +1381 -0
- npdatetime_django/static/npdatetime_django/js/pkg/README.md +345 -0
- npdatetime_django/static/npdatetime_django/js/pkg/npdatetime_wasm.d.ts +205 -0
- npdatetime_django/static/npdatetime_django/js/pkg/npdatetime_wasm.js +813 -0
- npdatetime_django/static/npdatetime_django/js/pkg/npdatetime_wasm_bg.wasm +0 -0
- npdatetime_django/static/npdatetime_django/js/pkg/npdatetime_wasm_bg.wasm.d.ts +36 -0
- npdatetime_django/static/npdatetime_django/js/pkg/package.json +15 -0
- npdatetime_django/templates/npdatetime_django/widgets/date_picker.html +28 -0
- npdatetime_django/templates/npdatetime_django/widgets/date_range.html +25 -0
- npdatetime_django/templatetags/__init__.py +259 -0
- npdatetime_django/templatetags/nepali_date.py +1 -0
- npdatetime_django/utils.py +131 -0
- npdatetime_django/widgets.py +168 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
export const memory: WebAssembly.Memory;
|
|
4
|
+
export const __wbg_bsdate_free: (a: number, b: number) => void;
|
|
5
|
+
export const bsdate_day: (a: number) => number;
|
|
6
|
+
export const bsdate_fromGregorian: (a: number, b: number, c: number, d: number) => void;
|
|
7
|
+
export const bsdate_month: (a: number) => number;
|
|
8
|
+
export const bsdate_new: (a: number, b: number, c: number, d: number) => void;
|
|
9
|
+
export const bsdate_tithi: (a: number, b: number) => void;
|
|
10
|
+
export const bsdate_toGregorian: (a: number, b: number) => void;
|
|
11
|
+
export const bsdate_toString: (a: number, b: number) => void;
|
|
12
|
+
export const bsdate_year: (a: number) => number;
|
|
13
|
+
export const init: () => void;
|
|
14
|
+
export const nepalidate_addDays: (a: number, b: number, c: number) => void;
|
|
15
|
+
export const nepalidate_fiscalQuarter: (a: number) => number;
|
|
16
|
+
export const nepalidate_fiscalYear: (a: number, b: number) => void;
|
|
17
|
+
export const nepalidate_format: (a: number, b: number, c: number, d: number) => void;
|
|
18
|
+
export const nepalidate_formatUnicode: (a: number, b: number) => void;
|
|
19
|
+
export const nepalidate_fromGregorian: (a: number, b: number, c: number, d: number) => void;
|
|
20
|
+
export const nepalidate_fromOrdinal: (a: number, b: number) => void;
|
|
21
|
+
export const nepalidate_monthCalendar: (a: number, b: number) => void;
|
|
22
|
+
export const nepalidate_new: (a: number, b: number, c: number, d: number) => void;
|
|
23
|
+
export const nepalidate_tithi: (a: number, b: number) => void;
|
|
24
|
+
export const nepalidate_toGregorian: (a: number, b: number) => void;
|
|
25
|
+
export const nepalidate_toOrdinal: (a: number) => number;
|
|
26
|
+
export const nepalidate_toString: (a: number, b: number) => void;
|
|
27
|
+
export const nepalidate_today: (a: number) => void;
|
|
28
|
+
export const nepalidate_year: (a: number) => number;
|
|
29
|
+
export const nepalidate_month: (a: number) => number;
|
|
30
|
+
export const nepalidate_day: (a: number) => number;
|
|
31
|
+
export const __wbg_nepalidate_free: (a: number, b: number) => void;
|
|
32
|
+
export const __wbindgen_export: (a: number, b: number, c: number) => void;
|
|
33
|
+
export const __wbindgen_export2: (a: number, b: number) => number;
|
|
34
|
+
export const __wbindgen_export3: (a: number, b: number, c: number, d: number) => number;
|
|
35
|
+
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
|
|
36
|
+
export const __wbindgen_start: () => void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "npdatetime-wasm",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"files": [
|
|
6
|
+
"npdatetime_wasm_bg.wasm",
|
|
7
|
+
"npdatetime_wasm.js",
|
|
8
|
+
"npdatetime_wasm.d.ts"
|
|
9
|
+
],
|
|
10
|
+
"main": "npdatetime_wasm.js",
|
|
11
|
+
"types": "npdatetime_wasm.d.ts",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"./snippets/*"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
|
|
3
|
+
<input
|
|
4
|
+
type="{{ widget.type }}"
|
|
5
|
+
name="{{ widget.name }}"
|
|
6
|
+
{% if widget.value != None %}
|
|
7
|
+
value="{{ widget.value }}"
|
|
8
|
+
{% endif %}
|
|
9
|
+
{% include "django/forms/widgets/attrs.html" %}
|
|
10
|
+
>
|
|
11
|
+
|
|
12
|
+
<script type="module">
|
|
13
|
+
import { NepaliDatePicker } from '{% static "npdatetime_django/js/date_picker.min.js" %}';
|
|
14
|
+
|
|
15
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
16
|
+
const input = document.querySelector('input[name="{{ widget.name }}"]');
|
|
17
|
+
if (input && !input.dataset.npdInitialized) {
|
|
18
|
+
const options = {{ widget.picker_options|safe }};
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
new NepaliDatePicker(input, options);
|
|
22
|
+
input.dataset.npdInitialized = 'true';
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('Failed to initialize Nepali Date Picker:', error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
|
|
3
|
+
<div class="npd-date-range-widget">
|
|
4
|
+
<div class="npd-range-start">{{ widget.0 }}</div>
|
|
5
|
+
<span class="npd-range-separator">to</span>
|
|
6
|
+
<div class="npd-range-end">{{ widget.1 }}</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
.npd-date-range-widget {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: 10px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.npd-range-separator {
|
|
17
|
+
color: #666;
|
|
18
|
+
font-weight: 500;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.npd-range-start,
|
|
22
|
+
.npd-range-end {
|
|
23
|
+
flex: 1;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""Template tags and filters for Nepali date manipulation"""
|
|
2
|
+
from django import template
|
|
3
|
+
from django.utils.safestring import mark_safe
|
|
4
|
+
import datetime
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from npdatetime import NepaliDate
|
|
8
|
+
NPDATETIME_AVAILABLE = True
|
|
9
|
+
except ImportError:
|
|
10
|
+
NPDATETIME_AVAILABLE = False
|
|
11
|
+
|
|
12
|
+
register = template.Library()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@register.filter
|
|
16
|
+
def to_nepali_date(value, format_str='%Y-%m-%d'):
|
|
17
|
+
"""
|
|
18
|
+
Convert a Gregorian date to Nepali date.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
value: datetime.date object or string in YYYY-MM-DD format
|
|
22
|
+
format_str: Output format string (default: '%Y-%m-%d')
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Formatted Nepali date string or empty string if conversion fails
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
{{ person.birth_date|to_nepali_date }}
|
|
29
|
+
{{ person.birth_date|to_nepali_date:"%Y/%m/%d" }}
|
|
30
|
+
"""
|
|
31
|
+
if not value or not NPDATETIME_AVAILABLE:
|
|
32
|
+
return ''
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Handle datetime.date objects
|
|
36
|
+
if isinstance(value, datetime.date):
|
|
37
|
+
year, month, day = value.year, value.month, value.day
|
|
38
|
+
# Handle string format
|
|
39
|
+
elif isinstance(value, str):
|
|
40
|
+
if '-' in value:
|
|
41
|
+
year, month, day = map(int, value.split('-')[:3])
|
|
42
|
+
else:
|
|
43
|
+
return ''
|
|
44
|
+
else:
|
|
45
|
+
return ''
|
|
46
|
+
|
|
47
|
+
# Convert to Nepali date
|
|
48
|
+
nepali_date = NepaliDate.from_gregorian(year, month, day)
|
|
49
|
+
|
|
50
|
+
# Format output
|
|
51
|
+
result = format_str
|
|
52
|
+
result = result.replace('%Y', str(nepali_date.year))
|
|
53
|
+
result = result.replace('%m', f'{nepali_date.month:02d}')
|
|
54
|
+
result = result.replace('%d', f'{nepali_date.day:02d}')
|
|
55
|
+
result = result.replace('%B', get_nepali_month_name(nepali_date.month, 'en'))
|
|
56
|
+
result = result.replace('%b', get_nepali_month_name(nepali_date.month, 'en')[:3])
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
except Exception as e:
|
|
61
|
+
return ''
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@register.filter
|
|
65
|
+
def to_gregorian_date(value, format_str='%Y-%m-%d'):
|
|
66
|
+
"""
|
|
67
|
+
Convert a Nepali date to Gregorian date.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
value: Nepali date string in YYYY-MM-DD format
|
|
71
|
+
format_str: Output format string (default: '%Y-%m-%d')
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Formatted Gregorian date string or empty string if conversion fails
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
{{ person.birth_date_bs|to_gregorian_date }}
|
|
78
|
+
{{ person.birth_date_bs|to_gregorian_date:"%d/%m/%Y" }}
|
|
79
|
+
"""
|
|
80
|
+
if not value or not NPDATETIME_AVAILABLE:
|
|
81
|
+
return ''
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# Parse Nepali date
|
|
85
|
+
if isinstance(value, str):
|
|
86
|
+
year, month, day = map(int, value.split('-'))
|
|
87
|
+
else:
|
|
88
|
+
return ''
|
|
89
|
+
|
|
90
|
+
# Create NepaliDate and convert
|
|
91
|
+
nepali_date = NepaliDate(year, month, day)
|
|
92
|
+
gy, gm, gd = nepali_date.to_gregorian()
|
|
93
|
+
|
|
94
|
+
# Create datetime object for formatting
|
|
95
|
+
gregorian_date = datetime.date(gy, gm, gd)
|
|
96
|
+
|
|
97
|
+
# Use Python's strftime for formatting
|
|
98
|
+
return gregorian_date.strftime(format_str)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
return ''
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@register.filter
|
|
105
|
+
def format_nepali_date(value, format_str='%Y-%m-%d'):
|
|
106
|
+
"""
|
|
107
|
+
Format a Nepali date string.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
value: Nepali date string in YYYY-MM-DD format
|
|
111
|
+
format_str: Output format string
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Formatted date string
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
{{ date_bs|format_nepali_date:"%Y/%m/%d" }}
|
|
118
|
+
"""
|
|
119
|
+
if not value:
|
|
120
|
+
return ''
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
year, month, day = map(int, value.split('-'))
|
|
124
|
+
|
|
125
|
+
result = format_str
|
|
126
|
+
result = result.replace('%Y', str(year))
|
|
127
|
+
result = result.replace('%m', f'{month:02d}')
|
|
128
|
+
result = result.replace('%d', f'{day:02d}')
|
|
129
|
+
result = result.replace('%B', get_nepali_month_name(month, 'en'))
|
|
130
|
+
result = result.replace('%b', get_nepali_month_name(month, 'en')[:3])
|
|
131
|
+
|
|
132
|
+
return result
|
|
133
|
+
|
|
134
|
+
except Exception:
|
|
135
|
+
return value
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@register.filter
|
|
139
|
+
def nepali_month_name(month_num, language='en'):
|
|
140
|
+
"""
|
|
141
|
+
Get Nepali month name.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
month_num: Month number (1-12)
|
|
145
|
+
language: 'en' or 'np'
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Month name string
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
{{ 1|nepali_month_name }} # Returns "Baisakh"
|
|
152
|
+
{{ 1|nepali_month_name:"np" }} # Returns "बैशाख"
|
|
153
|
+
"""
|
|
154
|
+
return get_nepali_month_name(month_num, language)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@register.filter
|
|
158
|
+
def to_nepali_number(value):
|
|
159
|
+
"""
|
|
160
|
+
Convert English numerals to Nepali numerals.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
value: Number or string containing numbers
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
String with Nepali numerals
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
{{ 2081|to_nepali_number }} # Returns "२०८१"
|
|
170
|
+
"""
|
|
171
|
+
nepali_digits = {
|
|
172
|
+
'0': '०', '1': '१', '2': '२', '3': '३', '4': '४',
|
|
173
|
+
'5': '५', '6': '६', '7': '७', '8': '८', '9': '९'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
result = str(value)
|
|
177
|
+
for eng, nep in nepali_digits.items():
|
|
178
|
+
result = result.replace(eng, nep)
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@register.simple_tag
|
|
184
|
+
def nepali_date_today(format_str='%Y-%m-%d'):
|
|
185
|
+
"""
|
|
186
|
+
Get today's date in Nepali calendar.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
format_str: Output format string
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Formatted today's Nepali date
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
{% nepali_date_today %}
|
|
196
|
+
{% nepali_date_today "%Y/%m/%d" %}
|
|
197
|
+
"""
|
|
198
|
+
if not NPDATETIME_AVAILABLE:
|
|
199
|
+
return ''
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
today = NepaliDate.today()
|
|
203
|
+
|
|
204
|
+
result = format_str
|
|
205
|
+
result = result.replace('%Y', str(today.year))
|
|
206
|
+
result = result.replace('%m', f'{today.month:02d}')
|
|
207
|
+
result = result.replace('%d', f'{today.day:02d}')
|
|
208
|
+
result = result.replace('%B', get_nepali_month_name(today.month, 'en'))
|
|
209
|
+
result = result.replace('%b', get_nepali_month_name(today.month, 'en')[:3])
|
|
210
|
+
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
except Exception:
|
|
214
|
+
return ''
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@register.inclusion_tag('npdatetime_django/widgets/inline_picker.html')
|
|
218
|
+
def nepali_date_picker(field_name, value='', mode='BS', language='en', **kwargs):
|
|
219
|
+
"""
|
|
220
|
+
Include a Nepali date picker inline in templates.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
field_name: Name of the input field
|
|
224
|
+
value: Initial value
|
|
225
|
+
mode: 'BS' or 'AD'
|
|
226
|
+
language: 'en' or 'np'
|
|
227
|
+
**kwargs: Additional options
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
{% nepali_date_picker "birth_date" mode="BS" language="np" %}
|
|
231
|
+
"""
|
|
232
|
+
return {
|
|
233
|
+
'field_name': field_name,
|
|
234
|
+
'value': value,
|
|
235
|
+
'mode': mode,
|
|
236
|
+
'language': language,
|
|
237
|
+
'options': kwargs
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# Helper function
|
|
242
|
+
def get_nepali_month_name(month_num, language='en'):
|
|
243
|
+
"""Get the name of a Nepali month."""
|
|
244
|
+
months_en = [
|
|
245
|
+
'Baisakh', 'Jestha', 'Ashadh', 'Shrawan', 'Bhadra', 'Ashwin',
|
|
246
|
+
'Kartik', 'Mangshir', 'Poush', 'Magh', 'Falgun', 'Chaitra'
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
months_np = [
|
|
250
|
+
'बैशाख', 'जेठ', 'असार', 'साउन', 'भदौ', 'असोज',
|
|
251
|
+
'कात्तिक', 'मंसिर', 'पुस', 'माघ', 'फागुन', 'चैत'
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
if not (1 <= month_num <= 12):
|
|
255
|
+
return ''
|
|
256
|
+
|
|
257
|
+
if language == 'np':
|
|
258
|
+
return months_np[month_num - 1]
|
|
259
|
+
return months_en[month_num - 1]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Template tags module
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Utility functions for working with Nepali dates in Django"""
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from npdatetime import NepaliDate
|
|
5
|
+
NPDATETIME_AVAILABLE = True
|
|
6
|
+
except ImportError:
|
|
7
|
+
NPDATETIME_AVAILABLE = False
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def convert_bs_to_ad(bs_date_str):
|
|
11
|
+
"""
|
|
12
|
+
Convert a Nepali (BS) date string to Gregorian (AD) date.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
bs_date_str (str): Date in format "YYYY-MM-DD"
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: Gregorian date in format "YYYY-MM-DD" or None if conversion fails
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> convert_bs_to_ad("2081-01-15")
|
|
22
|
+
"2024-05-02"
|
|
23
|
+
"""
|
|
24
|
+
if not bs_date_str or not NPDATETIME_AVAILABLE:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
year, month, day = map(int, bs_date_str.split('-'))
|
|
29
|
+
nepali_date = NepaliDate(year, month, day)
|
|
30
|
+
gy, gm, gd = nepali_date.to_gregorian()
|
|
31
|
+
return f"{gy}-{gm:02d}-{gd:02d}"
|
|
32
|
+
except Exception:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def convert_ad_to_bs(ad_date_str):
|
|
37
|
+
"""
|
|
38
|
+
Convert a Gregorian (AD) date string to Nepali (BS) date.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
ad_date_str (str): Date in format "YYYY-MM-DD"
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
str: Nepali date in format "YYYY-MM-DD" or None if conversion fails
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> convert_ad_to_bs("2024-05-02")
|
|
48
|
+
"2081-01-15"
|
|
49
|
+
"""
|
|
50
|
+
if not ad_date_str or not NPDATETIME_AVAILABLE:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
year, month, day = map(int, ad_date_str.split('-'))
|
|
55
|
+
nepali_date = NepaliDate.from_gregorian(year, month, day)
|
|
56
|
+
return f"{nepali_date.year}-{nepali_date.month:02d}-{nepali_date.day:02d}"
|
|
57
|
+
except Exception:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_nepali_month_names(language='en'):
|
|
62
|
+
"""
|
|
63
|
+
Get list of Nepali month names.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
language (str): 'en' for English or 'np' for Nepali
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
list: List of month names
|
|
70
|
+
"""
|
|
71
|
+
months_en = [
|
|
72
|
+
'Baisakh', 'Jestha', 'Ashadh', 'Shrawan', 'Bhadra', 'Ashwin',
|
|
73
|
+
'Kartik', 'Mangshir', 'Poush', 'Magh', 'Falgun', 'Chaitra'
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
months_np = [
|
|
77
|
+
'बैशाख', 'जेठ', 'असार', 'साउन', 'भदौ', 'असोज',
|
|
78
|
+
'कात्तिक', 'मंसिर', 'पुस', 'माघ', 'फागुन', 'चैत'
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
return months_np if language == 'np' else months_en
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def validate_nepali_date(date_str):
|
|
85
|
+
"""
|
|
86
|
+
Validate a Nepali date string.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
date_str (str): Date string to validate
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
tuple: (is_valid, error_message)
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> validate_nepali_date("2081-01-15")
|
|
96
|
+
(True, None)
|
|
97
|
+
>>> validate_nepali_date("2081-13-01")
|
|
98
|
+
(False, "Month must be between 1 and 12")
|
|
99
|
+
"""
|
|
100
|
+
if not date_str:
|
|
101
|
+
return False, "Date string is empty"
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
parts = date_str.split('-')
|
|
105
|
+
if len(parts) != 3:
|
|
106
|
+
return False, "Invalid date format. Use YYYY-MM-DD"
|
|
107
|
+
|
|
108
|
+
year, month, day = map(int, parts)
|
|
109
|
+
|
|
110
|
+
if year < 1975 or year > 2100:
|
|
111
|
+
return False, "Year must be between 1975 and 2100"
|
|
112
|
+
|
|
113
|
+
if month < 1 or month > 12:
|
|
114
|
+
return False, "Month must be between 1 and 12"
|
|
115
|
+
|
|
116
|
+
if day < 1 or day > 32:
|
|
117
|
+
return False, "Day must be between 1 and 32"
|
|
118
|
+
|
|
119
|
+
# Validate with npdatetime if available
|
|
120
|
+
if NPDATETIME_AVAILABLE:
|
|
121
|
+
try:
|
|
122
|
+
NepaliDate(year, month, day)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
return False, str(e)
|
|
125
|
+
|
|
126
|
+
return True, None
|
|
127
|
+
|
|
128
|
+
except ValueError:
|
|
129
|
+
return False, "Invalid date format. Use YYYY-MM-DD with numeric values"
|
|
130
|
+
except Exception as e:
|
|
131
|
+
return False, str(e)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Custom widgets for Nepali date picker"""
|
|
2
|
+
from django import forms
|
|
3
|
+
from django.forms.widgets import Input
|
|
4
|
+
from django.utils.safestring import mark_safe
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NepaliDatePickerWidget(Input):
|
|
9
|
+
"""
|
|
10
|
+
A widget that renders a Nepali date picker.
|
|
11
|
+
|
|
12
|
+
This widget uses the JavaScript date picker from npdatetime-rust
|
|
13
|
+
to provide a rich, interactive date selection experience.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
mode (str): 'BS' for Bikram Sambat or 'AD' for Gregorian. Default: 'BS'
|
|
17
|
+
language (str): 'en' for English or 'np' for Nepali. Default: 'en'
|
|
18
|
+
include_time (bool): Whether to include time selection. Default: False
|
|
19
|
+
format (str): Date format string. Default: '%Y-%m-%d'
|
|
20
|
+
theme (str): 'auto', 'light', or 'dark'. Default: 'auto'
|
|
21
|
+
show_today_button (bool): Show the "Today" button. Default: True
|
|
22
|
+
show_clear_button (bool): Show the "Clear" button. Default: True
|
|
23
|
+
min_date (str): Minimum selectable date in YYYY-MM-DD format
|
|
24
|
+
max_date (str): Maximum selectable date in YYYY-MM-DD format
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
class PersonForm(forms.Form):
|
|
28
|
+
birth_date = forms.CharField(
|
|
29
|
+
widget=NepaliDatePickerWidget(
|
|
30
|
+
mode='BS',
|
|
31
|
+
language='np',
|
|
32
|
+
theme='light'
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
input_type = 'text'
|
|
38
|
+
template_name = 'npdatetime_django/widgets/date_picker.html'
|
|
39
|
+
|
|
40
|
+
class Media:
|
|
41
|
+
css = {
|
|
42
|
+
'all': ('npdatetime_django/css/date_picker.css',)
|
|
43
|
+
}
|
|
44
|
+
js = (
|
|
45
|
+
'npdatetime_django/js/date_picker.min.js',
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def __init__(self, attrs=None, mode='BS', language='en', include_time=False,
|
|
49
|
+
format='%Y-%m-%d', theme='auto', show_today_button=True,
|
|
50
|
+
show_clear_button=True, min_date=None, max_date=None, **kwargs):
|
|
51
|
+
super().__init__(attrs)
|
|
52
|
+
|
|
53
|
+
self.mode = mode
|
|
54
|
+
self.language = language
|
|
55
|
+
self.include_time = include_time
|
|
56
|
+
self.format = format
|
|
57
|
+
self.theme = theme
|
|
58
|
+
self.show_today_button = show_today_button
|
|
59
|
+
self.show_clear_button = show_clear_button
|
|
60
|
+
self.min_date = min_date
|
|
61
|
+
self.max_date = max_date
|
|
62
|
+
self.extra_options = kwargs
|
|
63
|
+
|
|
64
|
+
def get_context(self, name, value, attrs):
|
|
65
|
+
"""Build the context for rendering the widget template."""
|
|
66
|
+
context = super().get_context(name, value, attrs)
|
|
67
|
+
|
|
68
|
+
# Ensure attrs is not None
|
|
69
|
+
if attrs is None:
|
|
70
|
+
attrs = {}
|
|
71
|
+
|
|
72
|
+
# Build data attributes for the date picker
|
|
73
|
+
widget_attrs = context['widget']['attrs']
|
|
74
|
+
widget_attrs['data-mode'] = self.mode
|
|
75
|
+
widget_attrs['data-language'] = self.language
|
|
76
|
+
widget_attrs['data-theme'] = self.theme
|
|
77
|
+
|
|
78
|
+
if not widget_attrs.get('class'):
|
|
79
|
+
widget_attrs['class'] = 'npd-input'
|
|
80
|
+
else:
|
|
81
|
+
widget_attrs['class'] += ' npd-input'
|
|
82
|
+
|
|
83
|
+
# Build picker options as JSON
|
|
84
|
+
picker_options = {
|
|
85
|
+
'mode': self.mode,
|
|
86
|
+
'language': self.language,
|
|
87
|
+
'format': self.format,
|
|
88
|
+
'theme': self.theme,
|
|
89
|
+
'showTodayButton': self.show_today_button,
|
|
90
|
+
'showClearButton': self.show_clear_button,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if self.include_time:
|
|
94
|
+
picker_options['includeTime'] = True
|
|
95
|
+
|
|
96
|
+
if self.min_date:
|
|
97
|
+
picker_options['minDate'] = self.min_date
|
|
98
|
+
|
|
99
|
+
if self.max_date:
|
|
100
|
+
picker_options['maxDate'] = self.max_date
|
|
101
|
+
|
|
102
|
+
# Add any extra options
|
|
103
|
+
picker_options.update(self.extra_options)
|
|
104
|
+
|
|
105
|
+
context['widget']['picker_options'] = mark_safe(json.dumps(picker_options))
|
|
106
|
+
context['widget']['include_time'] = self.include_time
|
|
107
|
+
|
|
108
|
+
return context
|
|
109
|
+
|
|
110
|
+
def build_attrs(self, base_attrs, extra_attrs=None):
|
|
111
|
+
"""Build HTML attributes for the widget."""
|
|
112
|
+
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
113
|
+
|
|
114
|
+
# Add data attributes
|
|
115
|
+
attrs['data-mode'] = self.mode
|
|
116
|
+
attrs['data-language'] = self.language
|
|
117
|
+
attrs['data-theme'] = self.theme
|
|
118
|
+
|
|
119
|
+
# Set autocomplete off for date pickers
|
|
120
|
+
attrs['autocomplete'] = 'off'
|
|
121
|
+
|
|
122
|
+
# Add placeholder if not set
|
|
123
|
+
if 'placeholder' not in attrs:
|
|
124
|
+
if self.mode == 'BS':
|
|
125
|
+
attrs['placeholder'] = 'Select Nepali Date' if self.language == 'en' else 'मिति छान्नुहोस्'
|
|
126
|
+
else:
|
|
127
|
+
attrs['placeholder'] = 'Select Date'
|
|
128
|
+
|
|
129
|
+
return attrs
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class NepaliDateRangeWidget(forms.MultiWidget):
|
|
133
|
+
"""
|
|
134
|
+
A widget for selecting a date range with two Nepali date pickers.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
class ReportForm(forms.Form):
|
|
138
|
+
date_range = forms.CharField(
|
|
139
|
+
widget=NepaliDateRangeWidget(mode='BS', language='np')
|
|
140
|
+
)
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
template_name = 'npdatetime_django/widgets/date_range.html'
|
|
144
|
+
|
|
145
|
+
def __init__(self, attrs=None, mode='BS', language='en', **kwargs):
|
|
146
|
+
widgets = [
|
|
147
|
+
NepaliDatePickerWidget(attrs=attrs, mode=mode, language=language, **kwargs),
|
|
148
|
+
NepaliDatePickerWidget(attrs=attrs, mode=mode, language=language, **kwargs),
|
|
149
|
+
]
|
|
150
|
+
super().__init__(widgets, attrs)
|
|
151
|
+
|
|
152
|
+
def decompress(self, value):
|
|
153
|
+
"""
|
|
154
|
+
Split the value into start and end dates.
|
|
155
|
+
Expects value in format: "YYYY-MM-DD to YYYY-MM-DD"
|
|
156
|
+
"""
|
|
157
|
+
if value:
|
|
158
|
+
if ' to ' in value:
|
|
159
|
+
return value.split(' to ')
|
|
160
|
+
return [value, '']
|
|
161
|
+
return [None, None]
|
|
162
|
+
|
|
163
|
+
def value_from_datadict(self, data, files, name):
|
|
164
|
+
"""Combine the two date values into a single range string."""
|
|
165
|
+
values = super().value_from_datadict(data, files, name)
|
|
166
|
+
if values and len(values) == 2 and values[0] and values[1]:
|
|
167
|
+
return f"{values[0]} to {values[1]}"
|
|
168
|
+
return ''
|