http://qs1969.pair.com?node_id=285487
Category: Miscellaneous
Author/Contact Info Alex Pleiner <alex@zeitform.de>
Description: My wife needed to know some islamic festival dates, so I wrote a conversion module (stealing code from KDE). It still misses features like getting the day of week and others. If anybody is interested I will expand it and/or implement it into Date::Convert as a subclass. Please give me some comments.
package Date::Hijri;
require 5.001;
use strict;
use warnings;

require Exporter;

our @ISA = qw(Exporter);

our @EXPORT = qw(h2g g2h);
our @EXPORT_OK = qw();

our $VERSION = '0.01';

############################################################

use constant IslamicEpoch => 227014;

############################################################

sub g2h
  {
    my ($day, $month, $year) = @_;
    return Absolute2Islamic(Gregorian2Absolute($day, $month, $year));
  }

sub h2g
  {
    my ($day, $month, $year) = @_;
    return Absolute2Gregorian(Islamic2Absolute($day, $month, $year));
  }

sub lastDayOfGregorianMonth
  {
    # Compute the last date of the month for the Gregorian calendar.
    my ($month, $year) = @_;
    if ($month == 2)
      {
        return 29 if ($year % 4 == 0 && $year % 100 != 0) || ($year % 
+400 == 0);
      }
    return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$month - 1
+];
  }

sub Gregorian2Absolute
  {
    # Computes the absolute date from the Gregorian date.
    my ($day, $month, $year) = @_;
    my $N = $day;                          # days this month
    for (my $m = $month - 1; $m > 0; $m--) # days in prior months this
+ year
      {
        $N += lastDayOfGregorianMonth($m, $year);
      }
    return int($N                        # days this year
               + 365 * ($year - 1)       # days in previous years igno
+ring leap days
               + ($year - 1) / 4         # Julian leap days before thi
+s year...
               - ($year - 1) / 100       # ...minus prior century year
+s...
               + ($year - 1) / 400);     # ...plus prior years divisib
+le by 400
  }

sub Absolute2Gregorian
  {
    # Computes the Gregorian date from the absolute date.
    my ($d) = @_;
    # Search forward year by year from approximate year
    my $year = int($d / 366 + 0.5);
    while ($d >= Gregorian2Absolute(1,1,$year+1)) { $year++; }
    # Search forward month by month from January
    my $month = 1;
    while ($d > Gregorian2Absolute(lastDayOfGregorianMonth($month, $ye
+ar), $month, $year)) { $month++; }
    my $day = $d - Gregorian2Absolute(1, $month, $year) + 1;

    return ($day, $month, $year);
  }

sub IslamicLeapYear
  {
    # True if year is an Islamic leap year
    my ($year) = @_;
    return ((((11 * $year) + 14) % 30) < 11) ? 1 : 0;
  }

sub lastDayOfIslamicMonth
  {
    # Last day in month during year on the Islamic calendar.
    my ($month, $year) = @_;
    return ($month % 2 == 1) || ($month == 12 && IslamicLeapYear($year
+)) ? 30 : 29;
  }

sub Islamic2Absolute
  {
    # Computes the absolute date from the Islamic date.
    my ($day, $month, $year) = @_;
    return int($day                      # days so far this month
               + 29 * ($month - 1)       # days so far...
               + int($month /2)          # ...this year
               + 354 * ($year - 1)       # non-leap days in prior year
+s
               + (3 + (11 * $year)) / 30 # leap days in prior years
               + IslamicEpoch);          # days before start of calend
+ar
  }

sub Absolute2Islamic
  {
    # Computes the Islamic date from the absolute date.
    my ($d) = @_;
    my ($day, $month, $year);
    if ($d <= IslamicEpoch)
      {
        # Date is pre-Islamic
        $month = 0;
        $day = 0;
        $year = 0;
      }
    else
      {
        # Search forward year by year from approximate year
        $year = int(($d - IslamicEpoch) / 355);
        while ($d >= Islamic2Absolute(1,1,$year+1)) { $year++; }
        # Search forward month by month from Muharram
        $month = 1;
        while ($d > Islamic2Absolute(lastDayOfIslamicMonth($month,$yea
+r), $month, $year)) { $month++ }
        $day = $d - Islamic2Absolute(1, $month, $year) + 1;
    }
    return ($day, $month, $year);

  }

1;
__END__
############################################################

=head1 NAME

Date::Hijri - Perl extension to convert islamic (hijri) and gregorian 
+dates.

=head1 SYNOPSIS

  use Date::Hijri;

  # convert gregorian to hijri date
  my ($hd, $hm, $hy) = g2h($gd, $gm, $gy);

  # convert hijri to gregorian date
  my ($gd, $gm, $gy) = h2g($hd, $hm, $hy);

=head1 DESCRIPTION

This simple module converts gregorian dates to islamic (hijri) and vic
+e versa.

The dates must be given as an array containing the day, month and year
+, and return the 
corresponding date as a list with the same elements.

=head1 EXAMPLES

  #!/usr/bin/perl -w

  use Date::Hijri;

  print join("-", g2h(22,8,2003));  # prints 23-6-1424
  print join("-", h2g(23,6,1424));  # prints 22-8-2003

=head1 SEE ALSO

This code is just stolen from KDE's L<kcalendarsystemhijri.cpp> at
http://webcvs.kde.org/cgi-bin/cvsweb.cgi/kdelibs/kdecore/kcalendarsyst
+emhijri.cpp

   Copyright (c) 2002-2003 Carlos Moro <cfmoro@correo.uniovi.es>
   Copyright (c) 2002-2003 Hans Petter Bieker <bieker@kde.org>

   kcalendarsystemhijri.cpp is translated from the Lisp code
   in ``Calendrical Calculations'' by Nachum Dershowitz and
   Edward M. Reingold, Software---Practice & Experience,
   vol. 20, no. 9 (September, 1990), pp. 899--928.

   This code is in the public domain, but any use of it
   should publically acknowledge its source.

=head1 AUTHOR

Alex Pleiner, E<lt>alex@zeitform.deE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2001, 2003 zeitform Internet Dienste. All rights reserve
+d.
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 DISCLAIMER

I haven't really tested if the converted dates are right and hope
someone will point out mistakes.

Hijri calculations are very difficult. The islamic calendar is a pure
lunar calendar, the new month starts by a physical (i.e. human)
sighting of the crescent moon at a given locale. So it depends on
several factors (like weather) that make it unreliable to calculate
islamic calendars in advance. As a result the dates calculated by
Date::Hijri can be false by one or more days.

Please see http://www.rabiah.com/convert/introduction.html for further
explanation.

I'm not a muslim, but interested in Islamic culture, religion and
calendar system. I believe in the Internet as a chance to realize that
we live in a small world with multiple cultures, religions and
philosophies. We can learn from others and develop tolerance, respect
and understanding.

Salam Alaikum (peace be with you)

=cut

## -fin-