The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# ============================================================================
package DateTime::Event::Zodiac;
# ============================================================================
use strict;
use warnings;
use utf8;

use Exporter;
use base qw(Exporter);

use DateTime;
use DateTime::Astro qw(solar_longitude);

our $VERSION = '1.03';
our @EXPORT = qw();
our @EXPORT_OK = qw(zodiac_date_name zodiac_date_symbol zodiac_astro_name zodiac_astro_symbol zodiac_date zodiac_astro);

our @ZODIAC = (
    {
        name    => 'aries',
        symbol  => "\x{2648}",
        start   => '21.03',
        end     => '20.04',
    },
    {
        name    => 'taurus',
        symbol  => "\x{2649}",
        start   => '21.04',
        end     => '20.05',
    },
    {
        name    => 'gemini',
        symbol  => "\x{264a}",
        start   => '21.05',
        end     => '21.06',
    },
    {
        name    => 'cancer',
        symbol  => "\x{264b}",
        start   => '22.06',
        end     => '22.07',
    },
    {
        name    => 'leo',
        symbol  => "\x{264c}",
        start   => '23.07',
        end     => '23.08',
    },
    {
        name    => 'virgo',
        symbol  => "\x{264d}",
        start   => '24.08',
        end     => '22.09',
    },
    {
        name    => 'libra',
        symbol  => "\x{264e}",
        start   => '23.09',
        end     => '22.10',
    },
    {
        name    => 'scorpius',
        symbol  => "\x{264f}",
        start   => '23.10',
        end     => '21.11',
    },
    {
        name    => 'sagittarius',
        symbol  => "\x{2650}",
        start   => '22.11',
        end     => '21.12',
    },
    {
        name    => 'capricornus',
        symbol  => "\x{2651}",
        start   => '22.12',
        end     => '19.01',
    },
    {
        name    => 'aquarius',
        symbol  => "\x{2652}",
        start   => '20.01',
        end     => '18.02',
    },
    {
        name    => 'pisces',
        symbol  => "\x{2653}",
        start   => '19.02',
        end     => '20.03',
    },
);

=encoding utf8

=pod

=head1 NAME

DateTime::Event::Zodiac - Return zodiac for a given date

=head1 SYNOPSIS

  use DateTime::Event::Zodiac qw(zodiac_date_name zodiac_date_symbol zodiac_astro_name zodiac_astro_symbol);
  
  my $dt = DateTime->new( 
    year   => 1979,
    month  => 3,
    day    => 27,
  );

  print zodiac_date_name($dt);
  print zodiac_astro_symbol($dt);
  
Returns the latin zodiac name or alternatively the unicode zodiac symbol
for the given date. The zodiac may be calculated using either fixed dates or 
using the exact longitude of the sun.

The module exports no symbols by default. All used functions must be requested
in the use statement. 

All methods return undef on failure.
  
=head1 DESCRIPTION

=head2 zodiac_date_name

 $name = zodiac_date_name($dt);
 
Latin zodiac name: aries, taurus, gemini, cancer, leo, virgo, libra, scorpius,
sagittarius, capricornus, aquarius and pisces.

Fixed dates.

=head2 zodiac_date_symbol

 $symbol = zodiac_date_symbol($dt);
 
Unicode zodiac symbol positions U+2648 to U+2653.

Fixed dates.

=head2 zodiac_astro_name

 $name = zodiac_astro_name($dt);
 
Latin zodiac name: aries, taurus, gemini, ...

Calculated from the longitude of the sun. 

=head2 zodiac_astro_symbol

 $symbol = zodiac_astro_symbol($dt);
 
Unicode zodiac symbol positions U+2648 to U+2653.

Calculated from the longitude of the sun.

=head2 zodiac_date

Simply computes the zodiac from the fixed date and returns a hash with the keys
name, symbol, start and end.

Used internally by C<zodiac_date_name> and C<zodiac_date_symbol>

=head2 zodiac_astro

Computes the zodiac from the position of the sun and returns a hash with the 
keys name, symbol, start and end. The keys start and end should be ignored
since they are only used for the C<zodiac_date> function.

May differ from the results of C<zodiac_date> depending on the solar year
(leap year ect). 

See L<DateTime::Astro> for notes on accuracy.

Used internally by C<zodiac_astro_name> and C<zodiac_astro_symbol>

=cut

# ----------------------------------------------------------------------------
sub zodiac_date_name
# ----------------------------------------------------------------------------
{
    my ($datetime) = @_;
    my $zodiac = zodiac_date($datetime);
    return defined $zodiac ? $zodiac->{name}:undef;
}

# ----------------------------------------------------------------------------
sub zodiac_date_symbol
# ----------------------------------------------------------------------------
{
    my ($datetime) = @_;
    my $zodiac = zodiac_date($datetime);
    return defined $zodiac ? $zodiac->{symbol}:undef;
}

# ----------------------------------------------------------------------------
sub zodiac_astro_name
# ----------------------------------------------------------------------------
{
    my ($datetime) = @_;
    my $zodiac = zodiac_astro($datetime);
    return defined $zodiac ? $zodiac->{name}:undef;
}

# ----------------------------------------------------------------------------
sub zodiac_astro_symbol
# ----------------------------------------------------------------------------
{
    my ($datetime) = @_;
    my $zodiac = zodiac_astro($datetime);
    return defined $zodiac ? $zodiac->{symbol}:undef;
}

# ----------------------------------------------------------------------------
sub zodiac_date
# ----------------------------------------------------------------------------
{
    my ($date) = @_;

    die('Must specify a DateTime object') unless (defined $date 
        && ref($date)
        && $date->isa('DateTime'));

    # Loop all zodiacs
    foreach my $zodiac (@ZODIAC) {
        my $start = _convertdate($zodiac->{start},$date->year,0,0);
        my $end = _convertdate($zodiac->{end},$date->year,23,59);
        
        next unless defined $start && defined $end;
        
        # Special case: Zodiac spans new year
        if ($start->month > $end->month) {
            # Check zodiac for past year
            $start->set(year => $start->year -1);
            return $zodiac if ($date >= $start && $date <= $end);
            # Reset and search current year too
            $start->set(year => $start->year +1);
            $end->set(year => $end->year + 1);
        }
        
        # Check zodiac
        return $zodiac if ($date >= $start && $date <= $end);    

    }
    return undef;
}

# ----------------------------------------------------------------------------
sub zodiac_astro
# ----------------------------------------------------------------------------
{
    my ($date) = @_;

    die('Must specify a DateTime object') unless (defined $date 
        && ref($date)
        && $date->isa('DateTime'));

    # Get longitude (0-360) for given DateTime object
    my $longitude = solar_longitude($date);
    
    return undef unless defined $longitude;
    
    # Return the zodiac
    return $ZODIAC[int($longitude / 30)];
}

# ----------------------------------------------------------------------------
sub _convertdate
# ----------------------------------------------------------------------------
{
    my ($date,$year,$hour,$minute) = @_;
    if ($date =~ m/^(\d\d)\.(\d\d)$/) {
        my $dt = DateTime->new(
            month  => $2,
            day    => $1,
            year   => $year,
            hour   => $hour,
            minute => $minute,
        );
        return $dt;
    }
    return undef;
}

=head1 DISCLAIMER  

The author of this module regads astrology as being a pseudoscience and 
superstition. I wrote this module for my job. I was young, foolish and 
I needed the money.

=head1 TODO

The C<zodiac_astro_horoscope> and C<zodiac_date_horoscope> functions have not 
yet been implemented and probably never will be ;-)

=head1 SUPPORT

Please report any bugs or feature requests to 
C<datetime-event-zodiac@rt.cpan.org>, or through the web interface at 
L<http://rt.cpan.org/Public/Bug/Report.html?Queue=DateTime::Event::Zodiac>. 
I will be notified, and then you'll automatically be notified of progress on 
your report as I make changes.

=head1 AUTHOR

    Maroš Kollár
    CPAN ID: MAROS
    maros [at] k-1.com
    
    L<http://www.k-1.com>

=head1 COPYRIGHT

DateTime::Event::Zodiac is Copyright (c) 2008-13 Maroš Kollár
- L<http://www.k-1.com>

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
LICENSE file included with this module.

=cut

q[I do not believe in this rubbish
I do not believe in this rubbish
I do not believe in this rubbish
I do not believe in this rubbish
I do not bel....];