package DateConversions; # $Id: DateConversions.pm, V1.0 djl Exp $ # # Copyright (c) 2005 Douglas Larson, All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # 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. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, # MA 02111-1307 USA # # ===================================================================== # dbg = 0, no messages. # dbg = 1, print debug messages # dbg = 2, print copious debug messages # ************************************************************************ =head1 NAME DateConversions - Space science specific date conversions. =head1 SYNOPSIS use lib "$ENV{HOME}/perl"; or wherever this file is located. use DateConversions; my $variable = &DateConversions::NeededFunction; =head1 DESCRIPTION ACE, GEOTAIL, GOES12, POLAR, WIND, and other spacecraft use differing timecode schemes. These conversion routines should help to convert the different formats to more uniform formats. =head1 SUBROUTINES f_isleap - Return 0 if a year is NOT leap year and a 1 otherwise. datetodoy - Convert a date to the Day Of Year. doytomonth - Day Of Year to month number. SecsSinceMidnight - Total number of seconds since midnight, including milliseconds. HMSToFraction - Convert a (hh, mm, ss, ms) into fraction of a day. FractionToHMS - Convert fractional part of DOY into (hh, mm, ss, ms). validateDate - Check a string in the form "DD/MO/YEAR HH:MM:SS" =cut use strict; BEGIN { use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); # Set the version for version checking $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(&GetMonthNames &GetMonthLengths &GetDayNames &SecsSinceMidnight &FractionToHMS &f_isleap &GetDaySum &HMSToFraction &datetodoy &doytomonth &doytodom); # eg: TAG => [ qw!name1 name2! ], %EXPORT_TAGS = ( ); @EXPORT_OK = qw(&GetMonthNames &GetMonthLengths &GetDayNames &SecsSinceMidnight &FractionToHMS &f_isleap &GetDaySum &HMSToFraction &datetodoy &doytomonth &doytodom); } use vars @EXPORT_OK; use vars qw($DebugLevel); # This global variable needs setting in main to be useful. # Example: $main::DateConversions::DebugLevel = 2; $DebugLevel=0; # ************************************************************************ sub f_isleap { # # Purpose :: Return 0 if a year is NOT leap year and a 1 otherwise. # # Description: Every fourth year is a leap year. But NOT when divisible # by 100, except if the year is divisible by 400. my $year = $_[0]; my $retval = 0; $retval = 1 if ($year%400==0) || (($year%4==0) && ($year%100!=0)); return ($retval); } # ************************************************************************ sub GetMonthNames { # # Purpose :: Return an array with the month names. # my @MonthNames = ("", "January", "February", "March", "April", "May", "June","July","August","September","October","November","December"); return(@MonthNames); } # ************************************************************************ sub GetMonthLengths { # # Purpose : Return an array with the number of days in a month. # my $Year = shift; my @MonthLengths=(); if( $Year <= 0 ) { # Just in case there is no year, we set default as non-leap. @MonthLengths = (0,31,28,31,30,31,30,31,31,30,31,30,31); } else { if(&f_isleap($Year)) { @MonthLengths = (0,31,29,31,30,31,30,31,31,30,31,30,31); } else { @MonthLengths = (0,31,28,31,30,31,30,31,31,30,31,30,31); } } return(@MonthLengths); } # ************************************************************************ sub GetDayNames { # # Purpose : Return an array with the names of the days of the week.. # my @DayNames = ("", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"); return(@DayNames); } # ************************************************************************ sub GetDaySum { # # Purpose :: Return an array of the cumulative number of days per month. # my $Year = shift; my @DaySum = (); # 0 - NOT leap year, 1 - it is a leap year my $isleap = 0; if( $Year <= 0 ) { # Just in case there is no year, we set default as non-leap. $isleap = 0; } else { $isleap = &f_isleap($Year); } if( $isleap ) { @DaySum = (0,31,60,91,121,152,182,213,244,274,305,335,366); } else { @DaySum = (0,31,59,90,120,151,181,212,243,273,304,334,365); } return(@DaySum); } # ************************************************************************ sub SecsSinceMidnight { # # Purpose :: Convert a time (hh:mm:ss:ms) to seconds since midnight. # my ($hh, $mm, $ss, $ms) = @_; defined($ms) or $ms = 0; # Number of seconds elapsed during this day: $ss += 60 * ($hh * 60 + $mm); $ss = $ss + ($ms/1.E3); return($ss); } # ************************************************************************ sub HMSToFraction { # # Purpose :: Convert a (hh, mm, ss, ms) into fraction of a day. # my ($hh, $mm, $ss, $ms) = @_; defined($ss) or $ss = 0; defined($ms) or $ms = 0; # Remove leading zeroes so we can do math. if( $DebugLevel > 0 ) { print STDOUT ("HMSToFraction: hour= $hh, minute=$mm, sec=$ss\n"); } # Number of seconds elapsed during this day: my $seconds = &SecsSinceMidnight($hh, $mm, $ss, $ms); $seconds /= 86400.E0; return($seconds); } # ************************************************************************ sub FractionToHMS { # # Purpose :: Convert fractional part of DOY into (hh, mm, ss, ms). # my $number = shift; # Remove the integer part of the number. if( $DebugLevel > 0 ) { print STDOUT ("\nFractionToHMS: number=".$number."\n"); } my $fraction = $number - int($number); $fraction *= 24.E0; my $hh = int($fraction); $fraction = $fraction - $hh; $fraction *= 60.E0; my $mm = int($fraction); $fraction = $fraction - $mm; $fraction *= 60.E0; my $ss = int($fraction); $fraction = $fraction - $ss; if( $DebugLevel > 0 ) { print STDOUT ("FractionToHMS: $hh:$mm:$ss:$fraction\n"); } return($hh, $mm, $ss, $fraction); } # ************************************************************************ sub datetodoy { # # Purpose :: Convert a date to the Day Of Year. # # You MUST pass the actual year, not just the last 2 digits. my ($year, $month, $day, $hh, $mm, $ss, $ms) = @_; defined($day) or $day = 0; defined($hh) or $hh = 0; defined($mm) or $mm = 0; defined($ss) or $ss = 0; defined($ms) or $ms = 0; my $DayOfYearNumber = 0; # [1-365] my @DaySum = &GetDaySum($year); $DayOfYearNumber = $day + $DaySum[$month - 1]; if( $DebugLevel > 0 ) { print STDOUT ("datetodoy: $month/$day/$year, DOY=$DayOfYearNumber\n"); } return ($DayOfYearNumber); } # ************************************************************************ sub doytomonth { # # Purpose :: Convert "Day Of The Year" into Month number. # # You MUST pass the actual year, not just the last 2 digits. This is # because we will eventually need to know if it is a leap year to get # the correct number of days. my $Year = shift; my $DayOfYearNumber = shift; # [1-365/6] $DayOfYearNumber = int($DayOfYearNumber); if( $DebugLevel >= 2 ) { print STDOUT ("doytomonth: YR=".$Year." DayOfYearNumber=".$DayOfYearNumber."\n"); } if( $DayOfYearNumber > 366 ) { return (-1); } my @DaySum = &GetDaySum($Year); my $month=0; while( $DayOfYearNumber > $DaySum[$month] ) { $month++; } #f( $DebugLevel >= 2 ) { # print STDOUT ("Year= ".$Year.", month=".$month. # " DayOfYearNumber=".$DayOfYearNumber." DaySum[$month]=".$DaySum[$month]."\n"); # return ($month); } # ************************************************************************ sub doytodom { # # Purpose :: Convert Day Of The Year into Day Of Month. # # You MUST pass the actual year, not just the last 2 digits. This is # because we will eventually need to know if it is a leap year to get # the correct number of days. my $Year = shift; my $DayOfYearNumber = shift; # [1-365/6] $DayOfYearNumber = int($DayOfYearNumber); if( $DebugLevel >= 2 ) { print STDOUT ("Year=".$Year." DayOfYearNumber=".$DayOfYearNumber."\n"); } if( $DayOfYearNumber > 366 ) { return (-1); } my @DaySum = &GetDaySum($Year); my $month = &doytomonth($Year,$DayOfYearNumber); my $DayOfMonth = $DayOfYearNumber - $DaySum[$month-1]; if( $DebugLevel >= 2 ) { print STDOUT ("Year= ".$Year.", DayOfMonth=".$DayOfMonth."\n"); } return($DayOfMonth); } # ************************************************************************ sub validateDate { # # Purpose : Given "DD/MO/YEAR HH:MM:SS", make sure it is a legal date. # my $str_date = shift; my $retval = 0; # 0-Bad, 1-OK my $re= qr/^(?=\d)(?:(?:31(?!.(?:0?[2469]|11))|(?:30|29)(?!.0?2)|29(?=.0?2.(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(?:\x20|$))|(?:2[0-8]|1\d|0?[1-9]))([-.\/])(?:1[012]|0?[1-9])\1(?:1[6-9]|[2-9]\d)?\d\d(?:(?=\x20\d)\x20|$))?(((0?[1-9]|1[012])(:[0-5]\d){0,2}(\x20[AP]M))|([01]\d|2[0-3])(:[0-5]\d){1,2})?$/; if( $DebugLevel > 0 ) { print STDOUT ("\nstr_date=$str_date\n$re\n"); } if( length($str_date) == 19 ) { if( $str_date =~ m%$re% ) { $retval=1; if( $DebugLevel > 0 ) { print STDOUT ("\nDate looks good.\n"); } } else { print STDOUT ("\nDate is bad, use (DD/MO/YEAR HH:MM:SS)\n"); } } else { print STDOUT ("\nThe date you gave, \"$str_date\" is not even the correct length.\n"); } return($retval); } END { } # module clean-up code here (global destructor) 1;