NepaliDateTime — Bikram Sambat Date/Time for Perl
A pure-Perl implementation of the Bikram Sambat (B.S.) calendar, modelled after the Python nepali_datetime library and extended with additional features.
Supported date range: BS 1975-01-01 to BS 2100-12-30
Reference anchor: AD 1918-04-13 ≡ BS 1975-01-01
Nepal Standard Time: UTC+05:45
Installation
Standard toolchain (ExtUtils::MakeMaker, ships with every Perl):
perl Makefile.PL
make
make test
make install
Or, with cpanm:
cpanm .
Both install NepaliDateTime, NepaliDateTime::Date, and
NepaliDateTime::DateTime (plus man pages) into your normal Perl library
path. To install somewhere else instead (e.g. a local lib, for
local::lib-style setups), pass INSTALL_BASE:
perl Makefile.PL INSTALL_BASE=$HOME/perl5
make install
The runtime code has no external CPAN dependencies — only core modules
(Carp, POSIX). The test suite uses Test2::Suite
(for Test2::V0), which is not core — install it with cpanm Test2::Suite
or your OS package (e.g. libtest2-suite-perl on Debian/Ubuntu) if
make test can't find it. Requires Perl 5.10 or newer.
If you'd rather not install it at all, see Running Tests
and Running the Demo below for running straight from
the source tree with -Ilib.
Modules
| Module | Purpose |
|---|---|
| NepaliDateTime::Date | BS date object |
| NepaliDateTime::DateTime | BS date + time object |
| NepaliDateTime::Data | Internal calendar data (not for direct use) |
Quick Start
use NepaliDateTime::Date;
use NepaliDateTime::DateTime;
# Today in BS
my $today = NepaliDateTime::Date->today();
print $today->isoformat(); # e.g. "2081-03-15"
print $today->format_devanagari(); # e.g. "२०८१ असार १५, बुधवार"
# Now (Nepal Standard Time)
my $now = NepaliDateTime::DateTime->now();
print $now->isoformat(); # "2081-03-15T14:30:00+05:45"
NepaliDateTime::Date
Construction
# By year, month, day (BS)
my $d = NepaliDateTime::Date->new(2081, 3, 15);
# Today in Nepal Standard Time
my $today = NepaliDateTime::Date->today();
# From AD (Gregorian) date
my $bs = NepaliDateTime::Date->from_ad(2024, 7, 15);
# From ISO string
my $d2 = NepaliDateTime::Date->from_iso('2081-03-15');
# From ordinal (BS 1975-01-01 = 1)
my $d3 = NepaliDateTime::Date->from_ordinal(1);
# From Unix timestamp (converted to Nepal time)
my $d4 = NepaliDateTime::Date->from_timestamp(time());
Conversion
my ($ay, $am, $ad) = $d->to_ad(); # BS → AD (year, month, day)
my $ad_str = $d->to_ad_string(); # "2024-07-15"
my $ordinal = $d->toordinal(); # integer ordinal
my $epoch = $d->to_timestamp(); # Unix timestamp (midnight NST)
Date Attributes
$d->year(); # 2081
$d->month(); # 3
$d->day(); # 15
$d->weekday(); # 0=Sun … 6=Sat
$d->weekday_iso(); # 1=Mon … 7=Sun
$d->day_name(); # "Wednesday"
$d->day_name_abbr(); # "Wed"
$d->day_name_np(); # "बुधवार"
$d->month_name(); # "Asar"
$d->month_name_abbr(); # "Asa"
$d->month_name_np(); # "असार"
$d->days_in_month(); # 32
$d->days_in_year(); # 365 or 366
$d->day_of_year(); # 1–366
$d->week_of_year(); # 1–53
$d->quarter(); # 1–4 (Q1 = Baisakh–Asar)
$d->is_weekend(); # 1 if Saturday
$d->is_weekend(1); # 1 if Saturday or Friday
Nepal Fiscal Year
Nepal's fiscal year runs 1 Shrawan → last day of Ashadh (month 4 → month 3 of next year).
my ($fy_start, $fy_end) = $d->fiscal_year(); # (2080, 2081)
my $fq = $d->fiscal_quarter(); # 1–4
my $fy_s = $d->fiscal_year_start();
my $fy_e = $d->fiscal_year_end();
Arithmetic
$d->add_days(10); # new date 10 days later (negative for past)
$d->add_months(3); # new date 3 months later (day clamped to month end)
$d->add_years(1); # new date 1 year later
$d->month_start(); # first day of current month
$d->month_end(); # last day of current month
$d->year_start(); # 1 Baishakh of current year
$d->year_end(); # last day of Chaitra of current year
$d->replace(day => 1); # copy with day replaced
$d->clone(); # exact copy
Overloaded operators:
my $d2 = $d + 5; # add 5 days
my $d3 = $d - 5; # subtract 5 days
my $n = $d2 - $d; # integer day difference
# Comparison: == != < <= > >= <=>
# Stringification: "$d" → "2081-03-15"
Age & Distance
$today->age_from($birth_date); # integer years
$d->days_until($other); # +n if $other is future
$d->days_since($other); # +n if $other is past
Weekday Helpers
# n-th occurrence of a weekday in the current month (0=Sun..6=Sat)
my $sat = $d->nth_weekday_of_month(1, 6); # first Saturday
my $sat = $d->last_weekday_of_month(6); # last Saturday
my $sat = $d->next_weekday(6); # next Saturday on/after $d
my $sat = $d->prev_weekday(6); # prev Saturday on/before $d
Date Range
my @dates = NepaliDateTime::Date->date_range($start, $end);
Formatting
$d->isoformat(); # "2081-03-15"
$d->to_string(); # alias for isoformat
$d->ctime(); # "Wed Asa 15 00:00:00 2081"
$d->format_devanagari(); # "२०८१ असार १५, बुधवार"
$d->format_nepali_date(); # "15 Asar 2081"
$d->strftime($format);
strftime directives:
| Code | Meaning | Example |
|---|---|---|
| %Y | 4-digit BS year | 2081 |
| %y | 2-digit BS year | 81 |
| %K | 4-digit year in Devanagari | २०८१ |
| %k | 2-digit year in Devanagari | ८१ |
| %m | Month number 01–12 | 03 |
| %n | Month number in Devanagari | ०३ |
| %B | Full month name | Asar |
| %b | Abbreviated month name | Asa |
| %N | Month name in Nepali | असार |
| %d | Day of month 01–32 | 15 |
| %D | Day in Devanagari | १५ |
| %A | Full weekday name | Wednesday |
| %a | Abbreviated weekday | Wed |
| %G | Weekday in Nepali | बुधवार |
| %w | Weekday integer 0=Sun..6=Sat | 3 |
| %j | Day of year 001–366 | 075 |
| %U | Week of year | 11 |
| %H | Hour 00–23 | 14 |
| %I | Hour 01–12 | 02 |
| %p | AM/PM | PM |
| %M | Minute 00–59 | 30 |
| %S | Second 00–59 | 00 |
| %f | Microseconds | 000000 |
| %h | Hour in Devanagari | १४ |
| %l | Minute in Devanagari | ३० |
| %s | Second in Devanagari | ०० |
| %z | UTC offset | +0545 |
| %Z | Timezone name | NST |
| %% | Literal % | % |
Parsing
my $d = NepaliDateTime::Date->strptime('2081-03-15', '%Y-%m-%d');
my $d = NepaliDateTime::Date->strptime('15 Asar 2081','%d %B %Y');
my $d = NepaliDateTime::Date->strptime('15 असार', '%d %N');
Calendar Display
$d->print_calendar(); # English, ASCII
$d->print_calendar(devanagari => 1); # Devanagari digits & names
$d->print_year_calendar(); # All 12 months
Validation
NepaliDateTime::Date->is_valid(2081, 3, 15); # true/false
NepaliDateTime::DateTime
Inherits all methods of NepaliDateTime::Date plus time support.
Construction
my $dt = NepaliDateTime::DateTime->new(2081, 3, 15, 14, 30, 0); # hh mm ss
my $dt = NepaliDateTime::DateTime->new(2081, 3, 15, 14, 30, 0, 500000); # with µs
my $now = NepaliDateTime::DateTime->now(); # current Nepal time
my $utc = NepaliDateTime::DateTime->utcnow(); # current UTC time in BS date
my $dt = NepaliDateTime::DateTime->from_timestamp(time());
my $dt = NepaliDateTime::DateTime->from_ad_datetime(2024, 7, 15, 14, 30, 0);
my $dt = NepaliDateTime::DateTime->combine($date, 14, 30, 0);
my $dt = NepaliDateTime::DateTime->strptime('2081-03-15 14:30:00', '%Y-%m-%d %H:%M:%S');
Time Accessors
$dt->hour(); $dt->minute();
$dt->second(); $dt->microsecond();
$dt->date(); # NepaliDateTime::Date part
$dt->time_string(); # "14:30:00" or "14:30:00.500000"
$dt->tzname(); # "NST"
$dt->utcoffset_string(); # "+05:45"
Conversion
my ($y,$m,$d,$h,$mi,$s,$us) = $dt->to_ad_datetime();
my $epoch = $dt->to_timestamp();
Arithmetic
$dt->add_seconds(90);
$dt->add_minutes(30);
$dt->add_hours(5);
$dt->add_days(1);
$dt->add_months(2);
$dt->add_years(1);
$dt->replace(hour => 9, minute => 0);
my $secs = $dt2 - $dt1; # seconds (float) between two datetimes
Formatting
$dt->isoformat(); # "2081-03-15T14:30:00+05:45"
$dt->isoformat(' '); # "2081-03-15 14:30:00+05:45"
$dt->ctime(); # "Wed Asa 15 14:30:00 2081"
$dt->strftime($format); # same directives as Date::strftime
$dt->format_devanagari(); # "२०८१ असार १५, बुधवार १४:३०:००"
Features Beyond the Python Package
| Feature | Description |
|---|---|
| fiscal_year() | Nepal fiscal year (Shrawan → Ashadh) |
| fiscal_quarter() | FQ1–FQ4 within fiscal year |
| fiscal_year_start() / fiscal_year_end() | First/last day of fiscal year |
| quarter() | Calendar quarter Q1–Q4 |
| days_in_year() | Total days in the BS year |
| day_of_year() | Day number within year |
| week_of_year() | Week number |
| weekday_iso() | ISO weekday 1=Mon..7=Sun |
| is_weekend() | Saturday (and optionally Friday) check |
| add_months() / add_years() | Arithmetic with clamping |
| month_start() / month_end() | Boundary dates of month |
| year_start() / year_end() | Boundary dates of year |
| fiscal_year_start() / fiscal_year_end() | Fiscal year boundaries |
| age_from($birth) | Age in complete years |
| days_until() / days_since() | Day distances |
| date_range($s,$e) | List of all dates between two dates |
| nth_weekday_of_month($n,$wd) | n-th weekday of the month |
| last_weekday_of_month($wd) | Last weekday in month |
| next_weekday($wd) / prev_weekday($wd) | Next/previous occurrence |
| format_devanagari() | Full Devanagari string |
| format_nepali_date() | "15 Asar 2081" |
| print_calendar() | Terminal month calendar |
| print_year_calendar() | 12-month terminal calendar |
| is_valid($y,$m,$d) | Validation without exceptions |
| from_iso($str) | Parse ISO string |
| to_timestamp() | Unix epoch conversion |
| Overloaded operators | +, -, ==, !=, <, >, <=, >=, <=>, "" |
Running Tests
After perl Makefile.PL:
make test
Or, straight from the source tree without building/installing anything:
prove -Ilib t/
# or
perl -Ilib t/01_date.t
perl -Ilib t/02_datetime.t
Running the Demo
perl -Ilib examples/demo.pl
Weekday Convention
This module follows the same convention as the Python nepali_datetime library:
| Value | Weekday | |---|---| | 0 | Sunday | | 1 | Monday | | 2 | Tuesday | | 3 | Wednesday | | 4 | Thursday | | 5 | Friday | | 6 | Saturday |
(Note: Python's built-in datetime.weekday() uses Monday=0. weekday_iso() uses the ISO convention: Monday=1 … Sunday=7.)
License
MIT — see LICENSE.