Date::Korean - Conversion between Korean solar / lunisolar date.


Date-Korean documentation Contained in the Date-Korean distribution.

Index


Code Index:

NAME

Top

Date::Korean - Conversion between Korean solar / lunisolar date.

SYNOPSIS

Top

    use Date::Korean;

    # convert solar to lunisolar date
    my ($ly, $lm, $ld, $leap) = sol2lun(2008, 10, 10);

    # convert lunisolar to solar date
    my ($sy, $sm, $sd) = lun2sol(2008, 9, 12, 0);

    # get ganzi in chinese
    my ($saecha, $wolgun, $iljin) = get_ganzi(2008, 9, 12, 0);

    # get ganzi in korean
    my ($saecha, $wolgun, $iljin) = get_ganzi_ko(2008, 9, 12, 0);

DESCRIPTION

Top

The traditional korean lunisolar calendar is based on the chinese calendar. This module handles conversion between Korean solar and lunisolar date.

FUNCTIONS

Top

sol2lun
  my ($ly, $lm, $ld, $leap) = sol2lun(2008, 10, 10);

Convert solar to lunisolar date. This function takes solar year, month, day arguements and returns lunisolar year, month, day and leap flag( 1 if month is leap month, or 0 if not )

lun2sol
 my ($sy, $sm, $sd) = lun2sol(2008, 9, 12, 0);

Convert lunisolar to solar date. This function takes lunisolar year, month, day, leap flag and returns solar year, month, day.

get_ganzi
 my ($saecha, $wolgun, $iljin) = get_ganzi(2008, 9, 12, 0);
 binmode STDOUT, ':encoding(UTF-8)';
 print "$saecha $wolgun $iljin\n";

output

 戊子 壬戌 癸未

Get ganzi (sexagenary cycle - 干支) of year(歲次), month(月建), day(日辰) in chinese. This function takes lunisolar year, month, day, leap flag.

get_ganzi_ko
 my ($saecha, $wolgun, $iljin) = get_ganzi_ko(2008, 9, 12, 0);
 binmode STDOUT, ':encoding(UTF-8)';
 print "$saecha $wolgun $iljin\n";

output

 무자 임술 계미

Get ganzi (sexagenary cycle - 간지) of year(세차), month(월건), day(일진) in korean. This function takes lunisolar year, month, day, leap flag.

CAVEATS

Top

AUTHOR

Top

C.H. Kang <chahkang_AT_gmail.com>

COPYRIGHT AND LICENSE

Top

SEE ALSO

Top

DateTime, DateTime::Calendar::Julian, Date::ISO8601


Date-Korean documentation Contained in the Date-Korean distribution.

package Date::Korean;

use strict;
use warnings;
use version; our $VERSION = qv('0.0.2');
use base 'Exporter';
use DateTime;
use DateTime::Calendar::Julian;
use Date::ISO8601 qw/cjdn_to_ymd/;
use Date::Korean::Table;
use Carp;

our @EXPORT = qw/get_ganzi get_ganzi_ko sol2lun lun2sol/;

sub _calculate_ganzi {
    my($year,$month,$day,$leap) = @_;

    my @ganzis = ( ($year+56)%60, ($year*12+$month+13)%60 );
    ($year,$month,$day) = lun2sol($year,$month,$day,$leap);
    # mjd(Modified Julian Date)
    my $mjd = DateTime->new(year=>$year,month=>$month,day=>$day)->mjd;
    # solar 1582-10-15 -> mjd:-100840
    if ($mjd < -100840) { 
        $mjd += DateTime::Calendar::Julian
                ->new(year=>$year,month=>$month,day=>$day)
                ->gregorian_deviation;
    }
    push @ganzis, ($mjd+50)%60;
    return @ganzis;
}

sub get_ganzi {
    return map { $CELESTIAL_STEMS[$_%10].$TERRESTRIAL_BRANCHES[$_%12] }
           _calculate_ganzi(@_);
}

sub get_ganzi_ko {
    return map { $CELESTIAL_STEMS_KO[$_%10].$TERRESTRIAL_BRANCHES_KO[$_%12] }
           _calculate_ganzi(@_);
}

sub sol2lun {

    my($year,$month,$day) = @_;

    my $days;
    eval {
        # Chronological Julian Day(cjd)
        $days = DateTime->new(year=>$year,month=>$month,day=>$day)->jd+0.5;
    };
    if ($@) { # Maybe valid Julian date.
        if ( $year<=1582 ) {
            eval { 
                $days = DateTime::Calendar::Julian
                        ->new(year=>$year,month=>$month,day=>$day)->jd+0.5;
            };
            if ($@) {
                croak "Invalid date.";
            }
        }
        else {
            croak "Invalid date.";
        }
    }
    # solar 1582-10-15 -> cjd:2299161 ,After this are gregorian calendar range.
    if ( $days < 2299161 ) {  # julian calendar range
        # gregorian 1582-10~05 ~ 1582-10-14 dates do not exist.
        if ( $year==1582 && $month==10 && $day>=5 && $day<=14) {
            croak "The gregorian date does not exist\n";
        }
        $days = DateTime::Calendar::Julian
                ->new(year=>$year,month=>$month,day=>$day)->jd+0.5;
    }

    if ( $days<$MINDATE || $days>$MAXDATE ) {
        croak "The date is out of range."
    }

    $days -= $MINDATE;
    $month = _bisect(\@MONTHTABLE,$days);
    $year = _bisect(\@YEARTABLE,$month);
    ($month,$day) = ( $month-$YEARTABLE[$year]+1, $days-$MONTHTABLE[$month]+1);
    my $leap;
    if ( $LEAPTABLE[$year]!=0 && $LEAPTABLE[$year]<=$month ) {
        if ( $LEAPTABLE[$year] == $month ) {
            $leap = 1;
        }
        else {
            $leap = 0;
        }
        $month -= 1;
    }
    else {
        $leap = 0;
    }

    return ( $year+$BASEYEAR, $month, $day, $leap );
}

sub lun2sol {

    my($year,$month,$day,$leap) = @_;

    $year -= $BASEYEAR;

    unless ( $year>=0 && $year< $#YEARTABLE ) {
        croak "Year is out of range.";
    }

    unless ( $month>=1 && $month <=12 ) {
        croak "Month is out of range.";
    }

    if ( $leap!=0 && ($LEAPTABLE[$year]-1)!=$month ) {
        croak "Wrong leap month.";
    }

    my $months = $YEARTABLE[$year] + $month - 1;

    if ( $leap==1 && ($month+1)==$LEAPTABLE[$year] ) {
        $months += 1;
    }
    elsif ( $LEAPTABLE[$year]!=0 && $LEAPTABLE[$year]<=$month ) {
        $months += 1;
    }

    my $days = $MONTHTABLE[$months] + $day -1;

    if ( $day<1 || $days>=$MONTHTABLE[$months+1]) {
        croak "Wrong day.";
    }

    # 1582-10-15 -> cjd(chronical julian date):2299161
    if ( ($days+$MINDATE) < 2299161 ) {
        my ($y,$m,$d) = cjdn_to_ymd($days+$MINDATE);
        my $deviation = DateTime::Calendar::Julian
                        ->new(year=>$y,month=>$m,day=>$d)
                        ->gregorian_deviation;
        return cjdn_to_ymd($days+$MINDATE-$deviation);
    }
    else {
        return cjdn_to_ymd($days+$MINDATE);
    }
}

sub _bisect {
    my ($a,$x) = @_;
    my $lo = 0;
    my $hi = $#{$a};
    while ( $lo < $hi ) {
        my $mid = int( ($lo+$hi)/2 );
        if ( $x < $a->[$mid] ) {
            $hi = $mid;
        }
        else {
            $lo = $mid + 1;
        }
    }

    return $lo-1;
}

1;
__END__