package Calendar::Bahai;
use strict; use warnings;
=head1 NAME
Calendar::Bahai - Interface to Bahai Calendar.
=head1 VERSION
Version 0.07
=cut
our $VERSION = '0.07';
use Carp;
use Readonly;
use Data::Dumper;
use POSIX qw/floor/;
use Time::localtime;
use List::Util qw/min/;
use Date::Calc qw/Delta_Days Day_of_Week Add_Delta_Days/;
Readonly my $BAHAI_EPOCH => 2394646.5;
Readonly my $GREGORIAN_EPOCH => 1721425.5;
Readonly my $MONTHS =>
[
undef,
'Baha', 'Jalal', 'Jamal', 'Azamat', 'Nur', 'Rahmat',
'Kalimat', 'Kamal', 'Asma', 'Izzat', 'Mashiyyat', 'Ilm',
'Qudrat', 'Qawl', 'Masail', 'Sharaf', 'Sultan', 'Mulk',
'Ala'
];
Readonly my $CYCLES =>
[
undef,
'Alif', 'Ba', 'Ab', 'Dal', 'Bab', 'Vav',
'Abad', 'Jad', 'Baha', 'Hubb', 'Bahhaj', 'Javab',
'Ahad', 'Vahhab', 'Vidad', 'Badi', 'Bahi', 'Abha',
'Vahid'
];
Readonly my $DAYS =>
[
'Jamal', 'Kamal', 'Fidal', 'Idal',
'Istijlal', 'Istiqlal', 'Jalal'
];
sub new
{
my $class = shift;
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
my ($major, $cycle);
my $self = {};
bless $self, $class;
if (defined($yyyy) && defined($mm) && defined($dd))
{
_validate_date($yyyy, $mm, $dd);
($major, $cycle, $yyyy) = _get_major_cycle_year($yyyy-1);
}
else
{
my $today = localtime;
$yyyy = ($today->year+1900) unless defined $yyyy;
$mm = ($today->mon+1) unless defined $mm;
$dd = $today->mday unless defined $dd;
($major, $cycle, $yyyy, $mm, $dd) = $self->from_gregorian($yyyy, $mm, $dd);
}
$self->{major} = $major;
$self->{cycle} = $cycle;
$self->{yyyy} = $yyyy;
$self->{mm} = $mm;
$self->{dd} = $dd;
return $self;
}
=head1 DESCRIPTION
The Bahai calendar started from the original Badi calendar, created by the Bab. The Bahai
calendar is composed of 19 months, each with 19 days. Years in the Bahai calendar are counted
from Thursday, 21 March 1844,the beginning of the Bahai Era or Badi Era (abbreviated BE/B.E.).
Year 1 BE thus began at sundown 20 March 1844. Using the Bahai names for the weekday and month
day one of the Bahai Era was Istijlal (Majesty), 1 Baha (Splendour) 1 BE.
Bahai Calendar for the month of Baha year 168 BE.
Baha [168 BE]
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19
=head1 MONTHS
+-------+---------------+----------------+------------------+
| Month | Name (Arabic) | Name (English) | Gregorian Dates |
+-------+---------------+----------------+------------------+
| 1 | Baha | Splendour | 21 Mar - 08 Apr |
| 2 | Jalal | Glory | 09 Apr - 27 Apr |
| 3 | Jamal | Beauty | 28 Apr - 16 May |
| 4 | Azamat | Grandeur | 17 May - 04 Jun |
| 5 | Nur | Light | 05 Jun - 23 Jun |
| 6 | Rahmat | Mercy | 24 Jun - 12 Jul |
| 7 | Kalimat | Words | 13 Jul - 31 Jul |
| 8 | Kamal | Perfection | 01 Aug - 19 Aug |
| 9 | Asma | Names | 20 Aug - 07 Sep |
| 10 | Izzat | Might | 08 Sep - 26 Sep |
| 11 | Mashiyyat | Will | 27 Sep - 15 Oct |
| 12 | Ilm | Knowledge | 16 Oct - 03 Nov |
| 13 | Qudrat | Power | 04 Nov - 22 Nov |
| 14 | Qawl | Speech | 23 Nov - 11 Dec |
| 15 | Masail | Questions | 12 Dec - 30 Dec |
| 16 | Sharaf | Honour | 31 Dec - 18 Jan |
| 17 | Sultan | Sovereignty | 19 Jan - 06 Feb |
| 18 | Mulk | Dominion | 07 Feb - 25 Feb |
| | Ayyam-i-Ha | The Days of Ha | 26 Feb - 01 Mar |
| 19 | Ala | Loftiness | 02 Mar - 20 Mar |
| | | |(Month of fasting)|
+-------+---------------+----------------+------------------+
=head1 WEEKDAYS
+---------------+----------------+-------------+
| Name (Arabic) | Name (English) | Day of Week |
+---------------+----------------+-------------+
| Jalal | Glory | Saturday |
| Jamal | Beauty | Sunday |
| Kamal | Perfection | Monday |
| Fidal | Grace | Tuesday |
| Idal | Justice | Wednesday |
| Istijlal | Majesty | Thursday |
| Istiqlal | Independence | Friday |
+---------------+----------------+-------------+
=head1 Kull-i-Shay and Vahid
Also existing in the Bahai calendar system is a 19-year cycle called Vahid and a 361-year
(19x19) supercycle called Kull-i-Shay (literally, "All Things"). Each of the 19 years in a
Vahid has been given a name as shown in the table below. The 9th Vahid of the 1st Kull-i-Shay
started on 21 March 1996, and the 10th Vahid will begin in 2015. The current Bahai year, year
168 BE (21 March 2011 - 20 March 2012), is year Badi of the 9th Vahid of the 1st Kull-i-Shay.
The 2nd Kull-i-Shay will begin in 2205.
+-----+----------+---------------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| No. | Name | Meaning | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
+-----+----------+---------------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| 1 | Alif | A | 1844 | 1863 | 1882 | 1901 | 1920 | 1939 | 1958 | 1977 | 1996 | 2015 | 2034 | 2053 | 2072 | 2091 | 2110 | 2129 | 2148 | 2167 | 2186 |
| 2 | Ba | B | 1845 | 1864 | 1883 | 1902 | 1921 | 1940 | 1959 | 1978 | 1997 | 2016 | 2035 | 2054 | 2073 | 2092 | 2111 | 2130 | 2149 | 2168 | 2187 |
| 3 | Ab | Father | 1846 | 1865 | 1884 | 1903 | 1922 | 1941 | 1960 | 1979 | 1998 | 2017 | 2036 | 2055 | 2074 | 2093 | 2112 | 2131 | 2150 | 2169 | 2188 |
| 4 | Dal | D | 1847 | 1866 | 1885 | 1904 | 1923 | 1942 | 1961 | 1980 | 1999 | 2018 | 2037 | 2056 | 2075 | 2094 | 2113 | 2132 | 2151 | 2170 | 2189 |
| 5 | Bab | Gate | 1848 | 1867 | 1886 | 1905 | 1924 | 1943 | 1962 | 1981 | 2000 | 2019 | 2038 | 2057 | 2076 | 2095 | 2114 | 2133 | 2152 | 2171 | 2190 |
| 6 | Vav | V | 1849 | 1868 | 1887 | 1906 | 1925 | 1944 | 1963 | 1982 | 2001 | 2020 | 2039 | 2058 | 2077 | 2096 | 2115 | 2134 | 2153 | 2172 | 2191 |
| 7 | Abad | Eternity | 1850 | 1869 | 1888 | 1907 | 1926 | 1945 | 1964 | 1983 | 2002 | 2021 | 2040 | 2059 | 2078 | 2097 | 2116 | 2135 | 2154 | 2173 | 2192 |
| 8 | Jad | Generosity | 1851 | 1870 | 1889 | 1908 | 1927 | 1946 | 1965 | 1984 | 2003 | 2022 | 2041 | 2060 | 2079 | 2098 | 2117 | 2136 | 2155 | 2174 | 2193 |
| 9 | Baha | Splendour | 1852 | 1871 | 1890 | 1909 | 1928 | 1947 | 1966 | 1985 | 2004 | 2023 | 2042 | 2061 | 2080 | 2099 | 2118 | 2137 | 2156 | 2175 | 2194 |
| 10 | Hubb | Love | 1853 | 1872 | 1891 | 1910 | 1929 | 1948 | 1967 | 1986 | 2005 | 2024 | 2043 | 2062 | 2081 | 2100 | 2119 | 2138 | 2157 | 2176 | 2195 |
| 11 | Bahhaj | Delightful | 1854 | 1873 | 1892 | 1911 | 1930 | 1949 | 1968 | 1987 | 2006 | 2025 | 2044 | 2063 | 2082 | 2101 | 2120 | 2139 | 2158 | 2177 | 2196 |
| 12 | Javab | Answer | 1855 | 1874 | 1893 | 1912 | 1931 | 1950 | 1969 | 1988 | 2007 | 2026 | 2045 | 2064 | 2083 | 2102 | 2121 | 2140 | 2159 | 2178 | 2197 |
| 13 | Ahad | Single | 1856 | 1875 | 1894 | 1913 | 1932 | 1951 | 1970 | 1989 | 2008 | 2027 | 2046 | 2065 | 2084 | 2103 | 2122 | 2141 | 2160 | 2179 | 2198 |
| 14 | Vahhab | Bountiful | 1857 | 1876 | 1895 | 1914 | 1933 | 1952 | 1971 | 1990 | 2009 | 2028 | 2047 | 2066 | 2085 | 2104 | 2123 | 2142 | 2161 | 2180 | 2199 |
| 15 | Vidad | Affection | 1858 | 1877 | 1896 | 1915 | 1934 | 1953 | 1972 | 1991 | 2010 | 2029 | 2048 | 2067 | 2086 | 2105 | 2124 | 2143 | 2162 | 2181 | 2200 |
| 16 | Badi | Beginning | 1859 | 1878 | 1897 | 1916 | 1935 | 1954 | 1973 | 1992 | 2011 | 2030 | 2049 | 2068 | 2087 | 2106 | 2125 | 2144 | 2163 | 2182 | 2201 |
| 17 | Bahi | Luminous | 1860 | 1879 | 1898 | 1917 | 1936 | 1955 | 1974 | 1993 | 2012 | 2031 | 2050 | 2069 | 2088 | 2107 | 2126 | 2145 | 2164 | 2183 | 2202 |
| 18 | Abha | Most Luminous | 1861 | 1880 | 1899 | 1918 | 1937 | 1956 | 1975 | 1994 | 2013 | 2032 | 2051 | 2070 | 2089 | 2108 | 2127 | 2146 | 2165 | 2184 | 2203 |
| 19 | Vahid | Unity | 1862 | 1881 | 1900 | 1919 | 1938 | 1957 | 1976 | 1995 | 2014 | 2033 | 2052 | 2071 | 2090 | 2109 | 2128 | 2147 | 2166 | 2185 | 2204 |
+-----+----------+---------------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
=head1 METHODS
=head2 as_string()
Return Bahai date in human readable format.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
print "Bahai date is " . $calendar->as_string() . "\n";
=cut
sub as_string
{
my $self = shift;
my $yyyy = $self->{major} * (19 * ($self->{cycle} - 1) + $self->{yyyy});
return sprintf("%02d, %s %d BE", $self->{dd}, $MONTHS->[$self->{mm}], $yyyy);
}
=head2 today()
Return today's date in Bahai calendar as list in the format major,cycle,yyyy,mm,dd.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
my ($major, $cycle, $yyyy, $mm, $dd) = $calendar->today();
print "Major [$major] Cycle [$cycle] Year [$yyyy] Month [$mm] Day [$dd]\n";
=cut
sub today
{
my $self = shift;
my $today = localtime;
return $self->from_gregorian($today->year+1900, $today->mon+1, $today->mday);
}
=head2 dow()
Get day of the week of the given Bahai date, starting with sunday (0).
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
print "Day of the week; [" . $calendar->dow() . "]\n";
=cut
sub dow
{
my $self = shift;
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
$yyyy = $self->{yyyy} unless defined $yyyy;
$mm = $self->{mm} unless defined $mm;
$dd = $self->{dd} unless defined $dd;
_validate_date($yyyy, $mm, $dd);
my ($major, $cycle);
($major, $cycle, undef) = _get_major_cycle_year($yyyy);
my @gregorian = $self->to_gregorian($major, $cycle, $yyyy, $mm, $dd);
return Day_of_Week(@gregorian);
}
=head2 get_calendar()
Return calendar for given year and month in Bahai calendar. It returns current month of Bahai
calendar if no argument is passed in.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
print $calendar->get_calendar();
# Print calendar for year 168 and month 1.
print $calendar->get_calendar(168, 1);
=cut
sub get_calendar
{
my $self = shift;
my $yyyy = shift;
my $mm = shift;
my ($major, $cycle, $year);
my ($calendar, $start_index, $days);
if (defined($yyyy) && defined($mm))
{
_validate_date($yyyy, $mm, 1);
$calendar = sprintf("\n\t%s [%d BE]\n", $MONTHS->[$mm], $yyyy);
}
else
{
$yyyy = $self->{major} * (19 * ($self->{cycle} - 1) + $self->{yyyy});
$calendar = sprintf("\n\t%s [%d BE]\n", $MONTHS->[$self->{mm}], $yyyy);
$mm = $self->{mm};
}
$calendar .= "\nSun Mon Tue Wed Thu Fri Sat\n";
$start_index = $self->dow($yyyy, $mm, 1);
map { $calendar .= " " } (1..($start_index%=7));
foreach (1 .. 19)
{
$calendar .= sprintf("%3d ", $_);
$calendar .= "\n" unless (($start_index+$_)%7);
}
return sprintf("%s\n\n", $calendar);
}
=head2 from_gregorian()
Convert Gregorian date to Bahai date.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
my ($major, $cycle, $yyyy, $mm, $dd) = $calendar->from_gregorian(2011, 3, 25);
=cut
sub from_gregorian
{
my $self = shift;
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
return $self->from_julian(_gregorian_to_julian($yyyy, $mm, $dd));
}
=head2 to_gregorian()
Convert Bahai date to Gregorian date.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
my ($yyyy, $mm, $dd) = $calendar->to_gregorian();
=cut
sub to_gregorian
{
my $self = shift;
my $major = shift;
my $cycle = shift;
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
$major = $self->{major} unless defined $major;
$cycle = $self->{cycle} unless defined $cycle;
$yyyy = $self->{yyyy} unless defined $yyyy;
$mm = $self->{mm} unless defined $mm;
$dd = $self->{dd} unless defined $dd;
return _julian_to_gregorian($self->to_julian($major, $cycle, $yyyy, $mm, $dd));
}
=head2 from_julian()
Convert Julian date to Bahai date.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
my ($major, $cycle, $yyyy, $mm, $dd) = $calendar->from_julian(2400124.5);
=cut
sub from_julian
{
my $self = shift;
my $julian = shift;
my ($major, $cycle, $yyyy, $mm, $dd);
my ($gy, $bstarty, $j1, $j2, $bys, $bld);
$julian = floor($julian) + 0.5;
($gy) = _julian_to_gregorian($julian);
($bstarty) = _julian_to_gregorian($BAHAI_EPOCH);
$j1 = _gregorian_to_julian($gy, 1, 1);
$j2 = _gregorian_to_julian($gy, 3, 20);
$bys = $gy - ($bstarty + ((($j1 <= $julian) && ($julian <= $j2)) ? 1 : 0));
($major, $cycle, $yyyy) = _get_major_cycle_year($bys);
$dd = $julian - $self->to_julian($major, $cycle, $yyyy, 1, 1);
$bld = $self->to_julian($major, $cycle, $yyyy, 20, 1);
$mm = ($julian >= $bld) ? 20 : (floor($dd / 19) + 1);
$dd = ($julian + 1) - $self->to_julian($major, $cycle, $yyyy, $mm, 1);
return ($major, $cycle, $yyyy, $mm, $dd);
}
=head2 to_julian()
Convert Bahai date to Julian date.
use strict; use warnings;
use Calendar::Bahai;
my $calendar = Calendar::Bahai->new();
my $julian = $calendar->to_julian();
=cut
sub to_julian
{
my $self = shift;
my $major = shift;
my $cycle = shift;
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
$major = $self->{major} unless defined $major;
$cycle = $self->{cycle} unless defined $cycle;
$yyyy = $self->{yyyy} unless defined $yyyy;
$mm = $self->{mm} unless defined $mm;
$dd = $self->{dd} unless defined $dd;
my ($y) = _julian_to_gregorian($BAHAI_EPOCH);
my $gy = (361 * ($major - 1)) + (19 * ($cycle - 1)) + ($yyyy - 1) + $y;
return _gregorian_to_julian($gy, 3, 20)
+
(19 * ($mm - 1))
+
(($mm != 20) ? 0 : (_is_gregorian_leap($gy + 1) ? -14 : -15))
+
$dd;
}
sub _gregorian_to_julian
{
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
return ($GREGORIAN_EPOCH - 1) +
(365 * ($yyyy - 1)) +
floor(($yyyy - 1) / 4) +
(-floor(($yyyy - 1) / 100)) +
floor(($yyyy - 1) / 400) +
floor((((367 * $mm) - 362) / 12) +
(($mm <= 2) ? 0 : (_is_gregorian_leap($yyyy) ? -1 : -2)) +
$dd);
}
sub _julian_to_gregorian
{
my $julian = shift;
my $wjd = floor($julian - 0.5) + 0.5;
my $depoch = $wjd - $GREGORIAN_EPOCH;
my $quadricent = floor($depoch / 146097);
my $dqc = $depoch % 146097;
my $cent = floor($dqc / 36524);
my $dcent = $dqc % 36524;
my $quad = floor($dcent / 1461);
my $dquad = $dcent % 1461;
my $yindex = floor($dquad / 365);
my $year = ($quadricent * 400) + ($cent * 100) + ($quad * 4) + $yindex;
$year++ unless (($cent == 4) || ($yindex == 4));
my $yearday = $wjd - _gregorian_to_julian($year, 1, 1);
my $leapadj = (($wjd < _gregorian_to_julian($year, 3, 1)) ? 0 : ((_is_gregorian_leap($year) ? 1 : 2)));
my $month = floor(((($yearday + $leapadj) * 12) + 373) / 367);
my $day = ($wjd - _gregorian_to_julian($year, $month, 1)) + 1;
return ($year, $month, $day);
}
sub _get_major_cycle_year
{
my $bys = shift;
my $major = floor($bys / 361) + 1;
my $cycle = floor(($bys % 361) / 19) + 1;
my $yyyy = ($bys % 19) + 1;
return ($major, $cycle, $yyyy);
}
sub _is_gregorian_leap
{
my $yyyy = shift;
return (($yyyy % 4) == 0) &&
(!((($yyyy % 100) == 0) && (($yyyy % 400) != 0)));
}
sub _validate_date
{
my $yyyy = shift;
my $mm = shift;
my $dd = shift;
croak("ERROR: Invalid year [$yyyy].\n")
unless (defined($yyyy) && ($yyyy =~ /^\d+$/) && ($yyyy > 0));
croak("ERROR: Invalid month [$mm].\n")
unless (defined($mm) && ($mm =~ /^\d{1,2}$/) && ($mm >= 1) && ($mm <= 19));
croak("ERROR: Invalid day [$dd].\n")
unless (defined($dd) && ($dd =~ /^\d{1,2}$/) && ($dd >= 1) && ($dd <= 19));
}
=head1 AUTHOR
Mohammad S Anwar, C<< <mohammad.anwar at yahoo.com> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-calendar-bahai at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Calendar-Bahai>. I will
be notified , and then you will automatically be notified of progress on your bug as I make
changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Calendar::Bahai
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Calendar-Bahai>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Calendar-Bahai>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/Calendar-Bahai>
=item * Search CPAN
L<http://search.cpan.org/dist/Calendar-Bahai/>
=back
=head1 LICENSE AND COPYRIGHT
Copyright 2011 Mohammad S Anwar.
This program is free software; you can redistribute it and/or modify it under the terms of
either : the GNU General Public License as published by the Free Software Foundation; or the
Artistic License.
See http://dev.perl.org/licenses/ for more information.
=head1 DISCLAIMER
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
=cut
1; # End of Calendar::Bahai