bsdatetime 1.0.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.
bsdatetime/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """Bikram Sambat (Nepali) date & datetime utilities.
2
+
3
+ Renamed distribution: bsdatetime (formerly bikram-sambat).
4
+ This module preserves the public API from the former bikram_sambat package.
5
+ """
6
+
7
+ __version__ = "1.1.0"
8
+
9
+ from .config import (
10
+ BASE_AD,
11
+ BASE_BS,
12
+ MIN_BS_YEAR,
13
+ MAX_BS_YEAR,
14
+ BS_MONTH_NAMES,
15
+ BS_WEEKDAY_NAMES,
16
+ )
17
+ from . import utils
18
+ from .conversion import ad_to_bs, bs_to_ad
19
+ from .validation import is_valid_bs_date, get_bs_month_days
20
+ from .formatting import (
21
+ format_bs_date,
22
+ parse_bs_date,
23
+ format_bs_datetime,
24
+ parse_bs_datetime,
25
+ )
26
+
27
+ __all__ = [
28
+ "__version__",
29
+ "BASE_AD",
30
+ "BASE_BS",
31
+ "MIN_BS_YEAR",
32
+ "MAX_BS_YEAR",
33
+ "BS_MONTH_NAMES",
34
+ "BS_WEEKDAY_NAMES",
35
+ "utils",
36
+ "ad_to_bs",
37
+ "bs_to_ad",
38
+ "is_valid_bs_date",
39
+ "get_bs_month_days",
40
+ "format_bs_date",
41
+ "parse_bs_date",
42
+ "format_bs_datetime",
43
+ "parse_bs_datetime",
44
+ ]
@@ -0,0 +1,10 @@
1
+ import csv, os
2
+ BS_YEARS = {}
3
+ csv_path = os.path.join(os.path.dirname(__file__), "data", "calendar_bs.csv")
4
+ with open(csv_path, newline="", encoding="utf-8") as f:
5
+ reader = csv.reader(f)
6
+ next(reader)
7
+ for row in reader:
8
+ year = int(row[0])
9
+ months = list(map(int, row[1:]))
10
+ BS_YEARS[year] = months
bsdatetime/config.py ADDED
@@ -0,0 +1,34 @@
1
+ """Configuration and constant values for the Bikram Sambat date utilities.
2
+ """
3
+ from __future__ import annotations
4
+ import datetime as _dt
5
+ from .bs_lookup import BS_YEARS # local copy of calendar data
6
+
7
+ # Core Conversion Anchors
8
+ BASE_AD: _dt.date = _dt.date(2018, 4, 14) # 2018-04-14 AD
9
+ BASE_BS: tuple[int, int, int] = (2075, 1, 1) # 2075-01-01 BS
10
+
11
+ # Calendar Ranges
12
+ MIN_BS_YEAR: int = min(BS_YEARS.keys())
13
+ MAX_BS_YEAR: int = max(BS_YEARS.keys())
14
+
15
+ # Localized Names
16
+ BS_MONTH_NAMES = [
17
+ "बैशाख", "जेठ", "असार", "साउन", "भदौ", "आश्विन",
18
+ "कार्तिक", "मंसिर", "पौष", "माघ", "फाल्गुन", "चैत्र"
19
+ ]
20
+
21
+ # Weekday mapping: Sunday=0 ... Saturday=6
22
+ BS_WEEKDAY_NAMES = [
23
+ "आइतबार", "सोमबार", "मंगलबार", "बुधबार", "बिहिबार", "शुक्रबार", "शनिबार"
24
+ ]
25
+
26
+ __all__ = [
27
+ "BASE_AD",
28
+ "BASE_BS",
29
+ "MIN_BS_YEAR",
30
+ "MAX_BS_YEAR",
31
+ "BS_MONTH_NAMES",
32
+ "BS_WEEKDAY_NAMES",
33
+ "BS_YEARS",
34
+ ]
@@ -0,0 +1,114 @@
1
+ """Conversion between AD (Gregorian) and BS (Bikram Sambat) calendars."""
2
+ from __future__ import annotations
3
+ import datetime as _dt
4
+ from .config import BASE_AD, BASE_BS, BS_YEARS, MIN_BS_YEAR, MAX_BS_YEAR
5
+
6
+ __all__ = ["ad_to_bs", "bs_to_ad"]
7
+
8
+ def ad_to_bs(ad_date: _dt.date):
9
+ """Convert an AD (Gregorian) date to a BS date tuple (year, month, day).
10
+
11
+ Args:
12
+ ad_date: A datetime.date object in Gregorian calendar
13
+
14
+ Returns:
15
+ tuple: (year, month, day) in Bikram Sambat calendar
16
+
17
+ Raises:
18
+ ValueError: If the date is outside the supported range
19
+ TypeError: If ad_date is not a datetime.date object
20
+ """
21
+ if not isinstance(ad_date, _dt.date):
22
+ raise TypeError("ad_date must be a datetime.date object")
23
+
24
+ days = (ad_date - BASE_AD).days
25
+ year, month, day = BASE_BS
26
+
27
+ # Handle negative days (dates before BASE_AD)
28
+ if days < 0:
29
+ days = abs(days)
30
+ while days > 0:
31
+ day -= 1
32
+ if day < 1:
33
+ month -= 1
34
+ if month < 1:
35
+ month = 12
36
+ year -= 1
37
+ if year < MIN_BS_YEAR:
38
+ raise ValueError(f"Date {ad_date} is before supported BS range")
39
+ day = BS_YEARS[year][month - 1]
40
+ days -= 1
41
+ else:
42
+ # Handle positive days (dates after BASE_AD)
43
+ while days > 0:
44
+ if year > MAX_BS_YEAR:
45
+ raise ValueError(f"Date {ad_date} is after supported BS range")
46
+ month_days = BS_YEARS[year][month - 1]
47
+ if day + days <= month_days:
48
+ day += days
49
+ days = 0
50
+ else:
51
+ days -= (month_days - day + 1)
52
+ day = 1
53
+ month += 1
54
+ if month > 12:
55
+ month = 1
56
+ year += 1
57
+
58
+ return (year, month, day)
59
+
60
+ def bs_to_ad(bs_year: int, bs_month: int, bs_day: int) -> _dt.date:
61
+ """Convert a BS date (y,m,d) to an AD date object.
62
+
63
+ Args:
64
+ bs_year: Bikram Sambat year
65
+ bs_month: Bikram Sambat month (1-12)
66
+ bs_day: Bikram Sambat day
67
+
68
+ Returns:
69
+ datetime.date: Corresponding Gregorian date
70
+
71
+ Raises:
72
+ ValueError: If the BS date is invalid or outside supported range
73
+ TypeError: If inputs are not integers
74
+ """
75
+ # Input validation
76
+ if not all(isinstance(x, int) for x in [bs_year, bs_month, bs_day]):
77
+ raise TypeError("BS date components must be integers")
78
+
79
+ if bs_year < MIN_BS_YEAR or bs_year > MAX_BS_YEAR:
80
+ raise ValueError(f"BS year {bs_year} is outside supported range ({MIN_BS_YEAR}-{MAX_BS_YEAR})")
81
+
82
+ if bs_month < 1 or bs_month > 12:
83
+ raise ValueError(f"BS month {bs_month} must be between 1 and 12")
84
+
85
+ if bs_year not in BS_YEARS:
86
+ raise ValueError(f"No calendar data available for BS year {bs_year}")
87
+
88
+ if bs_day < 1 or bs_day > BS_YEARS[bs_year][bs_month - 1]:
89
+ raise ValueError(f"BS day {bs_day} is invalid for month {bs_month} of year {bs_year}")
90
+
91
+ ad_date = BASE_AD
92
+ year, month, day = BASE_BS
93
+
94
+ # Prevent infinite loops with a safety counter
95
+ max_iterations = 50000 # Roughly 137 years worth of days
96
+ iterations = 0
97
+
98
+ while (year, month, day) != (bs_year, bs_month, bs_day):
99
+ iterations += 1
100
+ if iterations > max_iterations:
101
+ raise ValueError("Conversion exceeded maximum iterations - possible infinite loop")
102
+
103
+ ad_date += _dt.timedelta(days=1)
104
+ day += 1
105
+ if day > BS_YEARS[year][month - 1]:
106
+ day = 1
107
+ month += 1
108
+ if month > 12:
109
+ month = 1
110
+ year += 1
111
+ if year > MAX_BS_YEAR:
112
+ raise ValueError("BS date is outside supported range")
113
+
114
+ return ad_date
@@ -0,0 +1,100 @@
1
+ """Formatting and parsing for BS dates and datetimes."""
2
+ from __future__ import annotations
3
+ import datetime as _dt
4
+ from .config import BS_MONTH_NAMES, BS_WEEKDAY_NAMES
5
+ from .conversion import bs_to_ad
6
+ from .validation import is_valid_bs_date
7
+
8
+ __all__ = [
9
+ "format_bs_date",
10
+ "parse_bs_date",
11
+ "format_bs_datetime",
12
+ "parse_bs_datetime",
13
+ ]
14
+
15
+ def _weekday_to_custom(ad_weekday: int) -> int:
16
+ """Convert Python weekday to custom Sunday=0 mapping."""
17
+ return (ad_weekday + 1) % 7
18
+
19
+ def format_bs_date(bs_year: int, bs_month: int, bs_day: int, fmt: str = "%Y-%m-%d") -> str:
20
+ """Format a BS date according to the given format string."""
21
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
22
+ weekday = _weekday_to_custom(ad_date.weekday())
23
+ replacements = {
24
+ "%Y": f"{bs_year:04d}",
25
+ "%m": f"{bs_month:02d}",
26
+ "%d": f"{bs_day:02d}",
27
+ "%B": BS_MONTH_NAMES[bs_month - 1],
28
+ "%A": BS_WEEKDAY_NAMES[weekday],
29
+ }
30
+ out = fmt
31
+ for k, v in replacements.items():
32
+ out = out.replace(k, v)
33
+ return out
34
+
35
+ def parse_bs_date(text: str, fmt: str = "%Y-%m-%d"):
36
+ """Parse a BS date string according to the given format."""
37
+ fmt_parts = fmt.split('-')
38
+ txt_parts = text.split('-')
39
+ if len(fmt_parts) != len(txt_parts):
40
+ raise ValueError("Date string does not match format")
41
+ y = m = d = None
42
+ for f, t in zip(fmt_parts, txt_parts):
43
+ if f == "%Y":
44
+ y = int(t)
45
+ elif f == "%m":
46
+ m = int(t)
47
+ elif f == "%d":
48
+ d = int(t)
49
+ elif f == "%B":
50
+ if t in BS_MONTH_NAMES:
51
+ m = BS_MONTH_NAMES.index(t) + 1
52
+ else:
53
+ raise ValueError("Invalid month name")
54
+ else:
55
+ raise ValueError("Unsupported token")
56
+ if None in (y, m, d):
57
+ raise ValueError("Incomplete date")
58
+ if not is_valid_bs_date(y, m, d):
59
+ raise ValueError("Invalid BS date")
60
+ return (y, m, d)
61
+
62
+ def format_bs_datetime(bs_year: int, bs_month: int, bs_day: int, hour: int=0, minute: int=0, second: int=0, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
63
+ """Format a BS datetime according to the given format string."""
64
+ date_fmt, time_fmt = (fmt.split(' ', 1) + [""])[:2] if ' ' in fmt else (fmt, "")
65
+ date_part = format_bs_date(bs_year, bs_month, bs_day, date_fmt)
66
+ if not time_fmt:
67
+ return date_part
68
+ rep = {
69
+ "%H": f"{hour:02d}",
70
+ "%M": f"{minute:02d}",
71
+ "%S": f"{second:02d}",
72
+ }
73
+ time_out = time_fmt
74
+ for k, v in rep.items():
75
+ time_out = time_out.replace(k, v)
76
+ return f"{date_part} {time_out}".strip()
77
+
78
+ def parse_bs_datetime(text: str, fmt: str = "%Y-%m-%d %H:%M:%S"):
79
+ """Parse a BS datetime string according to the given format."""
80
+ if ' ' not in fmt:
81
+ y, m, d = parse_bs_date(text, fmt)
82
+ return (y, m, d, 0, 0, 0)
83
+ date_fmt, time_fmt = fmt.split(' ', 1)
84
+ date_part, time_part = text.split(' ', 1)
85
+ y, m, d = parse_bs_date(date_part, date_fmt)
86
+ t_tokens = time_fmt.split(':')
87
+ v_tokens = time_part.split(':')
88
+ if len(t_tokens) != len(v_tokens):
89
+ raise ValueError("Time portion mismatch")
90
+ hour = minute = second = 0
91
+ for f, v in zip(t_tokens, v_tokens):
92
+ if f == "%H":
93
+ hour = int(v)
94
+ elif f == "%M":
95
+ minute = int(v)
96
+ elif f == "%S":
97
+ second = int(v)
98
+ else:
99
+ raise ValueError("Unsupported time token")
100
+ return (y, m, d, hour, minute, second)
bsdatetime/utils.py ADDED
@@ -0,0 +1,194 @@
1
+ """Utility functions for Bikram Sambat date operations."""
2
+
3
+ import datetime
4
+ from .config import MIN_BS_YEAR, MAX_BS_YEAR, BS_WEEKDAY_NAMES, BS_MONTH_NAMES
5
+ from . import conversion as _conversion
6
+ from . import validation as _validation
7
+ from . import formatting as _formatting
8
+
9
+ # Public re-exports
10
+ ad_to_bs = _conversion.ad_to_bs
11
+ bs_to_ad = _conversion.bs_to_ad
12
+ is_valid_bs_date = _validation.is_valid_bs_date
13
+ get_bs_month_days = _validation.get_bs_month_days
14
+ format_bs_date = _formatting.format_bs_date
15
+ parse_bs_date = _formatting.parse_bs_date
16
+ format_bs_datetime = _formatting.format_bs_datetime
17
+ parse_bs_datetime = _formatting.parse_bs_datetime
18
+
19
+ __all__ = [
20
+ # conversion
21
+ "ad_to_bs", "bs_to_ad",
22
+ # validation
23
+ "is_valid_bs_date", "get_bs_month_days", "get_bs_year_range",
24
+ # formatting
25
+ "format_bs_date", "parse_bs_date", "format_bs_datetime", "parse_bs_datetime",
26
+ # misc helpers
27
+ "difference_between_bs_dates", "add_days_to_bs_date", "subtract_days_from_bs_date",
28
+ "get_current_bs_date", "get_current_bs_datetime", "bs_date_to_ordinal", "ordinal_to_bs_date",
29
+ "is_leap_year_bs", "get_bs_week_number", "get_bs_quarter", "get_bs_fiscal_year",
30
+ "get_bs_date_components", "get_bs_date_range", "get_bs_date_from_string",
31
+ "bs_date_to_timestamp", "timestamp_to_bs_date", "bs_datetime_to_timestamp", "timestamp_to_bs_datetime",
32
+ "get_bs_month_name", "get_bs_weekday_name",
33
+ ]
34
+
35
+ def get_bs_year_range():
36
+ """Get the range of supported BS years."""
37
+ return (MIN_BS_YEAR, MAX_BS_YEAR)
38
+
39
+ def get_bs_month_name(bs_month):
40
+ """Get the Nepali name of the BS month."""
41
+ if not isinstance(bs_month, int):
42
+ raise TypeError("BS month must be an integer")
43
+ if 1 <= bs_month <= 12:
44
+ return BS_MONTH_NAMES[bs_month - 1]
45
+ raise ValueError(f"Invalid BS month: {bs_month}. Must be between 1 and 12")
46
+
47
+ def get_bs_weekday_name(weekday):
48
+ """Get the Nepali name of the weekday."""
49
+ if not isinstance(weekday, int):
50
+ raise TypeError("Weekday must be an integer")
51
+ if 0 <= weekday <= 6:
52
+ return BS_WEEKDAY_NAMES[weekday]
53
+ raise ValueError(f"Invalid weekday: {weekday}. Must be between 0 and 6")
54
+
55
+ def add_days_to_bs_date(bs_year, bs_month, bs_day, days_to_add):
56
+ """Add days to a BS date."""
57
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
58
+ new_ad_date = ad_date + datetime.timedelta(days=days_to_add)
59
+ return ad_to_bs(new_ad_date)
60
+
61
+ def subtract_days_from_bs_date(bs_year, bs_month, bs_day, days_to_subtract):
62
+ """Subtract days from a BS date."""
63
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
64
+ new_ad_date = ad_date - datetime.timedelta(days=days_to_subtract)
65
+ return ad_to_bs(new_ad_date)
66
+
67
+ def difference_between_bs_dates(bs_date1, bs_date2):
68
+ """Calculate difference in days between two BS dates."""
69
+ ad_date1 = bs_to_ad(*bs_date1)
70
+ ad_date2 = bs_to_ad(*bs_date2)
71
+ return (ad_date2 - ad_date1).days
72
+
73
+ def get_current_bs_date():
74
+ """Get current BS date."""
75
+ ad_date = datetime.date.today()
76
+ return ad_to_bs(ad_date)
77
+
78
+ def get_current_bs_datetime():
79
+ """Get current BS date and time."""
80
+ now = datetime.datetime.now()
81
+ bs_date = ad_to_bs(now.date())
82
+ return bs_date + (now.hour, now.minute, now.second)
83
+
84
+ def bs_date_to_ordinal(bs_year, bs_month, bs_day):
85
+ """Convert BS date to ordinal number."""
86
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
87
+ return ad_date.toordinal()
88
+
89
+ def ordinal_to_bs_date(ordinal):
90
+ """Convert ordinal number to BS date."""
91
+ ad_date = datetime.date.fromordinal(ordinal)
92
+ return ad_to_bs(ad_date)
93
+
94
+ def is_leap_year_bs(bs_year):
95
+ """Check if BS year is a leap year (simplified)."""
96
+ if bs_year < MIN_BS_YEAR or bs_year > MAX_BS_YEAR:
97
+ raise ValueError("BS year out of supported range")
98
+ ad_date = bs_to_ad(bs_year, 1, 1)
99
+ ad_year = ad_date.year
100
+ return (ad_year % 4 == 0 and ad_year % 100 != 0) or (ad_year % 400 == 0)
101
+
102
+ def get_bs_week_number(bs_year, bs_month, bs_day):
103
+ """Get ISO week number for BS date."""
104
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
105
+ return ad_date.isocalendar()[1]
106
+
107
+ def get_bs_quarter(bs_month):
108
+ """Get quarter for BS month."""
109
+ if 1 <= bs_month <= 3:
110
+ return 1
111
+ elif 4 <= bs_month <= 6:
112
+ return 2
113
+ elif 7 <= bs_month <= 9:
114
+ return 3
115
+ elif 10 <= bs_month <= 12:
116
+ return 4
117
+ else:
118
+ raise ValueError("Invalid BS month")
119
+
120
+ def get_bs_fiscal_year(bs_year, bs_month):
121
+ """Get fiscal year for BS date (starts in Ashwin)."""
122
+ if bs_month >= 7:
123
+ start_year = bs_year
124
+ end_year = bs_year + 1
125
+ else:
126
+ start_year = bs_year - 1
127
+ end_year = bs_year
128
+ return f"{start_year}-{end_year}"
129
+
130
+ def get_bs_date_components(bs_year, bs_month, bs_day):
131
+ """Get BS date components as dictionary."""
132
+ if not is_valid_bs_date(bs_year, bs_month, bs_day):
133
+ raise ValueError("Invalid BS date")
134
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
135
+ weekday = (ad_date.weekday() + 1) % 7
136
+ return {
137
+ "year": bs_year,
138
+ "month": bs_month,
139
+ "day": bs_day,
140
+ "month_name": get_bs_month_name(bs_month),
141
+ "weekday_name": get_bs_weekday_name(weekday)
142
+ }
143
+
144
+ def get_bs_date_range(start_bs_date, end_bs_date):
145
+ """Generate list of BS dates between two dates."""
146
+ start_ad_date = bs_to_ad(*start_bs_date)
147
+ end_ad_date = bs_to_ad(*end_bs_date)
148
+ if start_ad_date > end_ad_date:
149
+ raise ValueError("Start date must be before or equal to end date")
150
+ delta_days = (end_ad_date - start_ad_date).days
151
+ bs_dates = []
152
+ for i in range(delta_days + 1):
153
+ current_ad_date = start_ad_date + datetime.timedelta(days=i)
154
+ bs_dates.append(ad_to_bs(current_ad_date))
155
+ return bs_dates
156
+
157
+ def get_bs_date_from_string(date_string):
158
+ """Parse BS date from string in various formats."""
159
+ common_formats = [
160
+ "%Y-%m-%d",
161
+ "%Y/%m/%d",
162
+ "%d-%m-%Y",
163
+ "%d/%m/%Y",
164
+ ]
165
+ for fmt in common_formats:
166
+ try:
167
+ return parse_bs_date(date_string, fmt)
168
+ except ValueError:
169
+ continue
170
+ raise ValueError("Date string does not match any known format")
171
+
172
+ # Timestamp helpers
173
+ def bs_date_to_timestamp(bs_year, bs_month, bs_day):
174
+ """Convert BS date to Unix timestamp."""
175
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
176
+ return int(datetime.datetime(ad_date.year, ad_date.month, ad_date.day).timestamp())
177
+
178
+ def timestamp_to_bs_date(ts):
179
+ """Convert Unix timestamp to BS date."""
180
+ ad_dt = datetime.datetime.fromtimestamp(ts)
181
+ return ad_to_bs(ad_dt.date())
182
+
183
+ def bs_datetime_to_timestamp(bs_year, bs_month, bs_day, hour=0, minute=0, second=0):
184
+ """Convert BS datetime to Unix timestamp."""
185
+ ad_date = bs_to_ad(bs_year, bs_month, bs_day)
186
+ ad_dt = datetime.datetime(ad_date.year, ad_date.month, ad_date.day, hour, minute, second)
187
+ return int(ad_dt.timestamp())
188
+
189
+ def timestamp_to_bs_datetime(ts):
190
+ """Convert Unix timestamp to BS datetime."""
191
+ ad_dt = datetime.datetime.fromtimestamp(ts)
192
+ y, m, d = ad_to_bs(ad_dt.date())
193
+ return (y, m, d, ad_dt.hour, ad_dt.minute, ad_dt.second)
194
+
@@ -0,0 +1,21 @@
1
+ """Validation helpers for BS dates."""
2
+ from __future__ import annotations
3
+ from .config import BS_YEARS
4
+
5
+ __all__ = ["is_valid_bs_date", "get_bs_month_days"]
6
+
7
+ def is_valid_bs_date(year: int, month: int, day: int) -> bool:
8
+ """Check if a BS date is valid."""
9
+ if year not in BS_YEARS:
10
+ return False
11
+ if not (1 <= month <= 12):
12
+ return False
13
+ if not (1 <= day <= BS_YEARS[year][month - 1]):
14
+ return False
15
+ return True
16
+
17
+ def get_bs_month_days(year: int, month: int) -> int:
18
+ """Get the number of days in a BS month."""
19
+ if year in BS_YEARS and 1 <= month <= 12:
20
+ return BS_YEARS[year][month - 1]
21
+ raise ValueError("Invalid BS year or month")
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: bsdatetime
3
+ Version: 1.0.0
4
+ Summary: Bikram Sambat (Nepali) date/datetime conversion and utilities
5
+ Home-page: https://github.com/Rajendra-Katuwal/bsdatetime
6
+ Author: Rajendra Katuwal
7
+ Author-email: Rajendra Katuwal <raj.katuwal2061@gmail.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/Rajendra-Katuwal/bsdatetime
10
+ Project-URL: Documentation, https://rajendra-katuwal.github.io/bsdatetime.docs/
11
+ Project-URL: Issues, https://github.com/Rajendra-Katuwal/bsdatetime/issues
12
+ Project-URL: Source, https://github.com/Rajendra-Katuwal/bsdatetime
13
+ Keywords: bikram sambat,nepali,calendar,date,datetime,conversion
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Software Development :: Internationalization
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: License :: OSI Approved :: MIT License
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Development Status :: 5 - Production/Stable
25
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Dynamic: author
30
+ Dynamic: home-page
31
+ Dynamic: license-file
32
+ Dynamic: requires-python
33
+
34
+ # bsdatetime
35
+
36
+ Lightweight, dependency‑free Bikram Sambat (Nepali) calendar utilities for Python.
37
+
38
+ Documentation: https://rajendra-katuwal.github.io/bsdatetime.docs/
39
+
40
+ ## What it does
41
+ * Convert between Gregorian (AD) and Bikram Sambat (BS)
42
+ * Format / parse BS dates (localized month + weekday names)
43
+ * Validate dates, get fiscal year, week number, ranges
44
+ * Provide current BS date/time helpers
45
+
46
+ ## Install
47
+ ```bash
48
+ pip install bsdatetime
49
+ ```
50
+
51
+ ## Quick start
52
+ ```python
53
+ import datetime, bsdatetime as bs
54
+
55
+ ad = datetime.date(2024, 12, 25)
56
+ bs_tuple = bs.ad_to_bs(ad) # (2081, 9, 9)
57
+ ad_back = bs.bs_to_ad(*bs_tuple) # 2024-12-25
58
+ text = bs.format_bs_date(*bs_tuple, "%B %d, %Y") # भदौ 09, 2081
59
+ current_bs = bs.utils.get_current_bs_date()
60
+ ```
61
+
62
+ Core API (most used)
63
+ * ad_to_bs(date)
64
+ * bs_to_ad(y, m, d)
65
+ * format_bs_date(y, m, d, fmt)
66
+ * parse_bs_date(text, fmt)
67
+ * is_valid_bs_date(y, m, d)
68
+ * utils.get_current_bs_date()
69
+
70
+ Supported range: BS 1975–2100 (≈ AD 1918–2043)
71
+
72
+ ## Django?
73
+ Use the companion package for model fields:
74
+ ```bash
75
+ pip install django-bsdatetime
76
+ ```
77
+
78
+ ## License
79
+ MIT
@@ -0,0 +1,12 @@
1
+ bsdatetime/__init__.py,sha256=sWuzyWMSrr2L9YmIU5SxgMWhsrwq-8mVGBJfeEBaUdQ,960
2
+ bsdatetime/bs_lookup.py,sha256=mLqhdOIwLc_28xqIwd7yGb3oTTyCgS9YKJTZlHiiJ_w,340
3
+ bsdatetime/config.py,sha256=Vh3z8JS8eW_2Effe4T5Apjo-WpWG6pbFd2Vu0H8LPwM,1092
4
+ bsdatetime/conversion.py,sha256=j6nXTqgbFCxa6oReywzkyiXg92aTFJf_Oz5AWvYmaq8,4042
5
+ bsdatetime/formatting.py,sha256=-4KlTRL6t8ZLaih4e0rMkxcFT1HiM81LYrkpIQFaO_0,3601
6
+ bsdatetime/utils.py,sha256=BzQNiFoWk8CnrRRFDxShXUaqtSXz41s76d4jFUpHMRE,7300
7
+ bsdatetime/validation.py,sha256=nm-2wQ3huIPeUFVkVJtn2xENc8bavJJZql0XGzbyElg,709
8
+ bsdatetime-1.0.0.dist-info/licenses/LICENSE,sha256=ZyF1-rB6W4yPWcNMiDCd_vg6t1yXGtmVx5Ewd2f7jMo,1094
9
+ bsdatetime-1.0.0.dist-info/METADATA,sha256=lj8_t_BqJYo2q7l3wwucMcIOfOOaNrSMPC7JcwC6AGw,2638
10
+ bsdatetime-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ bsdatetime-1.0.0.dist-info/top_level.txt,sha256=N0jO3BS_m6QK9Cy815lMRFsWspi-IducsC59bE88YaE,11
12
+ bsdatetime-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rajendra Katuwal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ bsdatetime