package CalDate;
require Exporter;
@ISA = ('Exporter');
@EXPORT = qw(CalDate JulDate FormatCalDate);
##############################################################################
# $Id: CalDate.pm,v 1.5 2005/10/06 17:55:04 hiram Exp $
##############################################################################
# CalDate - pass in a pointer to a UTInstant
# which has its julian decimal date set. This routine
# will calculate and set all the other elements of the
# hash. As a convenience, it will also return the year,
# an integer.
#
sub CalDate() {
my ($date) = @_; # pointer to the UTInstant
# These locals are all integers except for $frac
my ($jd, $ka, $kb, $kc, $kd, $ke, $ialp, $frac);
# Thus, to be safer, we will use int() around all calculations
$jd = int($date->{'J_DATE'} + 0.5); # integer julian date
$frac = $date->{'J_DATE'} + 0.5 - $jd + 1.0e-10; # fraction of day
$ka = int($jd);
if ( $jd >= 2299161 ) {
$ialp = int(( $jd - 1867216.25 ) / ( 36524.25 ));
$ka = int($jd + 1 + $ialp - ( $ialp >> 2 ));
}
$kb = int($ka + 1524);
$kc = int(( $kb - 122.1 ) / 365.25);
$kd = int($kc * 365.25);
$ke = int(( $kb - $kd ) / 30.6001);
$date->{'DAY'} = $kb - $kd - int( $ke * 30.6001 );
if ( $ke > 13 ) { $date->{'MONTH'} = int($ke - 13); }
else { $date->{'MONTH'} = int($ke - 1); }
if ( ($date->{'MONTH'} == 2) && ($date->{'DAY'} > 28) ) {
$date->{'DAY'} = 29;
}
if ( ($date->{'MONTH'} == 2) && ($date->{'DAY'} == 29) && ($ke == 3) ){
$date->{'YEAR'} = int($kc - 4716);
} elsif ( $date->{'MONTH'} > 2 ) { $date->{'YEAR'} = int($kc - 4716);
} else { $date->{'YEAR'} = int($kc - 4715); }
$date->{'D_HOUR'} = $frac * 24.0; # d_hour includes min, sec as fraction
$date->{'HOUR'} = int($date->{'D_HOUR'}); # integer hour
$date->{'D_MINUTE'} = # d_minute includes sec as fraction
( $date->{'D_HOUR'} - $date->{'HOUR'} ) * 60.0;
$date->{'MINUTE'} = int($date->{'D_MINUTE'}); # integer minute
$date->{'SECOND'} = # float second, int() it if you want
( $date->{'D_MINUTE'} - $date->{'MINUTE'} ) * 60.0;
$date->{'WEEKDAY'} = int(($jd + 1) % 7); # day of week
if ( $date->{'YEAR'} == (($date->{'YEAR'} >> 2) << 2) ) {
$date->{'DAY_OF_YEAR'} =
int( ( 275 * $date->{'MONTH'} ) / 9)
- int(($date->{'MONTH'} + 9) / 12)
+ $date->{'DAY'} - 30;
} else {
$date->{'DAY_OF_YEAR'} =
int( ( 275 * $date->{'MONTH'}) / 9)
- ((($date->{'MONTH'} + 9) / 12) << 1)
+ $date->{'DAY'} - 30;
}
return( $date->{'YEAR'} );
} # end of int CalDate( date )
############################################################################
#
# JulDate - computes the julian decimal date from
# the gregorian (or Julian) calendar date.
# for astronomical purposes, The Gregorian calendar reform occurred
# on 15 Oct. 1582. This is 05 Oct 1582 by the julian calendar.
#
# Single argument input: pointer to a hash that is a UTInstant
# The elements that must be set are:
# YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
# (if they have not been set, they appear as zero here)
#
# Output result will be the setting of the UTInstant elements:
# J_DATE, D_HOUR, D_MINUTE, WEEKDAY, DAY_OF_YEAR
# and for convenience, the J_DATE value will be returned.
#
# Reference: Astronomial formulae for calculators, meeus, p 23
# from fortran program by F. Espenak - April 1982 Page 276,
# 50 Year canon of solar eclipses: 1986-2035
#
sub JulDate() {
my ($date) = @_; # pointer to the UTInstant
# These locals are all integers except for $frac and $gyr
my ($iy0, $im0, $ia, $ib, $jd, $frac, $gyr);
# Thus, to be safer, we will use int() around all calculations
# for the integers
# the fraction of a day
$frac = ($date->{'HOUR'}/ 24.0)
+ ($date->{'MINUTE'} / 1440.0)
+ ($date->{'SECOND'} / 86400.0);
# convert the cal date to format YYYY.MMDDdd
$gyr = $date->{'YEAR'}
+ (0.01 * $date->{'MONTH'})
+ (0.0001 * $date->{'DAY'})
+ (0.0001 * $frac) + 1.0e-9;
# conversion factors
if ( $date->{'MONTH'} <= 2 ) {
$iy0 = int($date->{'YEAR'} - 1);
$im0 = int($date->{'MONTH'} + 12);
} else {
$iy0 = int($date->{'YEAR'});
$im0 = int($date->{'MONTH'});
}
$ia = int($iy0 / 100);
$ib = int(2 - $ia + ($ia >> 2));
# calculate julian date
if ( $date->{'YEAR'} <= 0 ) {
$jd = int( (365.25 * $iy0) - 0.75 )
+ int( (30.6001 * ($im0 + 1) ) )
+ int( $date->{'DAY'} + 1720994 );
} else {
$jd = int(365.25 * $iy0)
+ int( 30.6001 * ($im0 + 1) )
+ int( $date->{'DAY'} + 1720994 );
}
if ( $gyr >= 1582.1015 ) { # on or after 15 October 1582
$jd += $ib;
}
$date->{'J_DATE'} = $jd + $frac + 0.5;
$jd = int( $date->{'J_DATE'} + 0.5 );
$date->{'WEEKDAY'} = int( ($jd + 1) % 7 );
return( $date->{'J_DATE'} );
} # end of JulDate(
############################################################################
# FormatCalDate - pass in a pointer to a UTInstant
# Will format and return a string in standard format:
# YYYY-MM-DD HH:MM:SS
sub FormatCalDate() {
my ($t) = @_; # pointer to the UTInstant
return( sprintf "%d-%02d-%02d %02d:%02d:%02d",
$t->{'YEAR'}, $t->{'MONTH'}, $t->{'DAY'}, $t->{'HOUR'},
$t->{'MINUTE'}, int($t->{'SECOND'} + 0.5));
}
1;
=head1 NAME
CalDate - Calculate calendar date from Julian date
JulDate - Calculate Julian date from calendar date
FormatCalDate - Return string in ISO 8601 format: YYYY/MM/DD hh:mm:ss
http://www.cl.cam.ac.uk/~mgk25/iso-time.html
=head1 SYNOPSIS
use CalDate;
my %Date;
$Date{'J_DATE'} = 2452589.105822;
my $U=\%Date; # $U is a pointer to %Date
&CalDate($U);
printf "%s\n", &FormatCalDate($U);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
$Date{'YEAR'} = $year + 1900;
$Date{'MONTH'} = $mon + 1;
$Date{'DAY'} = $mday;
$Date{'HOUR'} = $hour;
$Date{'MINUTE'} = $min;
$Date{'SECOND'} = $sec;
&JulDate($U);
printf "%s = JD: %.6f\n", &FormatCalDate($U), $Date{'J_DATE'};
=head1 DESCRIPTION
=cut
###########################################################################
#
# The fundamental structure is a UTInstant
# Its elements will be the keys in a hash and they are:
#
# J_DATE float range (0 to the limit of perl installation)
# J_DATE is the Julian decimal date
# YEAR integer range dependent upon machine implementation of perl
# -4712 lower limit to upper limit size of integer
# MONTH integer range (1..12)
# DAY integer range (1..31)
# HOUR integer range (0..23)
# MINUTE integer range (0..61)
# SECOND float range (0..60) use int() if you want to consider it an int
# D_HOUR float range (0..23.999...) includes min and second as fraction
# D_MINUTE float range (0..60.9999) includes second as fraction
# WEEKDAY integer range (0..6)
# DAY_OF_YEAR integer range (1..366)
#
syntax highlighted by Code2HTML, v. 0.9.1