############################################################################ # TimeConv.pm # # Time conversion routines in Perl # # version 2007.178 # # Chad Trabant, IRIS Data Management Center ############################################################################ use strict; ############################################################################ # time2epoch: # # Compute a double precision Unix/POSIX epoch time from the specified # year, day-of-year, hours, minutes, seconds and microseconds. # # Returns a double precision epoch time ############################################################################ sub time2epoch { # time2epoch (year,yday,hour,min,sec,usec) my $year = shift; my $yday = shift; my $hour = shift; my $min = shift; my $sec = shift; my $usec = shift; if ( ! defined $year ) { $year = 0; } if ( ! defined $yday ) { $yday = 1; } if ( ! defined $hour ) { $hour = 0; } if ( ! defined $min ) { $min = 0; } if ( ! defined $sec ) { $sec = 0; } if ( ! defined $usec ) { $usec = 0; } # Calculate epoch my $shortyear = $year - 1900; my $a4 = int (($shortyear >> 2) + 475 - ! ($shortyear & 3)); my $a100 = int ($a4 / 25 - (($a4 % 25) < 0)); my $a400 = int ($a100 >> 2); my $intervening_leap_days = ($a4 - 492) - ($a100 - 19) + ($a400 - 4); my $days = (365 * ($shortyear - 70) + $intervening_leap_days + ($yday - 1)); my $epoch = (60 * (60 * (24 * $days + $hour) + $min) + $sec) + $usec / 1000000; return $epoch; } # End of time2epoch() ############################################################################ # timestr2epoch: # # Compute a double precision Unix/POSIX epoch time by parsing a string # containing the year, day-of-year, hours, minutes, seconds and # microseconds. # # Example input string: "2006,141,10:11:33.12" # # Value : Range # Year : 1900 - 2100 # Yday : 1 - 366 # Hour : 0 - 23 # Min : 0 - 59 # Sec : 0 - 59 # Fract : No limit on the fraction seconds # # Returns a double precision epoch time ############################################################################ sub timestr2epoch { # timestr2epoch ("year,yday,hour:min:sec.fsec") my $timestr = shift; my ($year,$yday,$hour,$min,$sec,$fract) = split (/[-:,.\s\/]/,$timestr); # Sanity check all input values and set defaults if ( ! defined $year || $year < 1900 || $year > 3000 ) { print "time2epoch: year ($year) is out of range\n"; return; } # Test for leap year my $leap = ( (($year%4 == 0) && ($year%100 != 0)) || ($year%400 == 0) ) ? 1 : 0; if ( ! defined $yday ) { $yday = 1; } elsif ( $yday > 365+$leap || $yday < 0 ) { print "time2epoch: day-of-year ($yday) is out of range\n"; return; } if ( ! defined $hour ) { $hour = 0; } elsif ( $hour > 23 || $hour < 0 ) { print "time2epoch: hour ($hour) is out of range\n"; return; } if ( ! defined $min ) { $min = 0; } elsif ( $min > 59 || $min < 0 ) { print "time2epoch: minute ($min) is out of range\n"; return; } if ( ! defined $sec ) { $sec = 0; } elsif ( $sec > 59 || $sec < 0 ) { print "time2epoch: second ($sec) is out of range\n"; return; } my $usec = 0; # Convert the fractional seconds to microseconds if ( defined $fract ) { my $flen = length $fract; $usec = $fract * (10 ** (6-$flen)); } return time2epoch ($year,$yday,$hour,$min,$sec,$usec); } # End of timestr2epoch() ############################################################################ # mdtimestr2epoch: # # Compute a double precision Unix/POSIX epoch time by parsing a string # containing the year, month, day-of-month, hours, minutes, seconds and # microseconds. # # Example input string: "2006/5/21 10:11:33.12" # # Value : Range # Year : 1900 - 2100 # Month : 1 - 12 # Mday : 1 - 31 # Hour : 0 - 23 # Min : 0 - 59 # Sec : 0 - 59 # Fract : No limit on the fraction seconds # # Returns a double precision epoch time ############################################################################ sub mdtimestr2epoch { # mdtimestr2epoch ("2006/5/21 10:11:33.12") my $mdtimestr = shift; my ($year,$month,$mday,$hour,$min,$sec,$fract) = split (/[-:,.\s\/]/,$mdtimestr); my $yday = undef; if ( defined $year && defined $month && defined $mday ) { $yday = &md2doy ($year, $month, $mday); } # Sanity check all input values and set defaults if ( ! defined $year || $year < 1900 || $year > 3000 ) { print "time2epoch: year ($year) is out of range\n"; return; } # Test for leap year my $leap = ( (($year%4 == 0) && ($year%100 != 0)) || ($year%400 == 0) ) ? 1 : 0; if ( ! defined $yday ) { $yday = 1; } elsif ( $yday > 365+$leap || $yday < 0 ) { print "time2epoch: day-of-year ($yday) is out of range\n"; return; } if ( ! defined $hour ) { $hour = 0; } elsif ( $hour > 23 || $hour < 0 ) { print "time2epoch: hour ($hour) is out of range\n"; return; } if ( ! defined $min ) { $min = 0; } elsif ( $min > 59 || $min < 0 ) { print "time2epoch: minute ($min) is out of range\n"; return; } if ( ! defined $sec ) { $sec = 0; } elsif ( $sec > 59 || $sec < 0 ) { print "time2epoch: second ($sec) is out of range\n"; return; } my $usec = 0; # Convert the fractional seconds to microseconds if ( defined $fract ) { my $flen = length $fract; $usec = $fract * (10 ** (6-$flen)); } return time2epoch ($year,$yday,$hour,$min,$sec,$usec); } # End of mdtimestr2epoch() ############################################################################ # epoch2time: # # Convert an epoch time to year, day-of-year, hour, minute, second and # microseconds. # # Returns (year,yday,hour,min,sec,usec) ############################################################################ sub epoch2time { # epoch2time (epoch) my $epoch = shift; return if ( ! defined $epoch ); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime ($epoch); $year += 1900; $mon++; $yday++; my $usec = ($epoch - int $epoch) * 1000000; return ($year,$yday,$hour,$min,$sec,$usec); } # End of epoch2time() ############################################################################ # epoch2timestr: # # Create a formatted time string from an epoch. # # Format: 'YYYY,DDD,HH:MM:SS.FFFFFF' # # Returns a formatted time string. ############################################################################ sub epoch2timestr { # epoch2timestr (epoch) my $epoch = shift; return if ( ! defined $epoch ); my ($year,$yday,$hour,$min,$sec,$usec) = epoch2time ($epoch); return sprintf ("%4d,%03d,%02d:%02d:%02d.%06d", $year,$yday,$hour,$min,$sec,$usec); } # End of epoch2timestr() ############################################################################ # epoch2mdtimestr: # # Create a month-day formatted time string from an epoch. # # Format: 'YYYY-MM-DD HH:MM:SS.FFFFFF' # # Returns a formatted time string. ############################################################################ sub epoch2mdtimestr { # epoch2mdtimestr (epoch) my $epoch = shift; return if ( ! defined $epoch ); my ($year,$yday,$hour,$min,$sec,$usec) = epoch2time ($epoch); my ($mon, $mday) = doy2md ($year, $yday); return sprintf ("%4d-%02d-%02d %02d:%02d:%02d.%06d", $year,$mon,$mday,$hour,$min,$sec,$usec); } # End of epoch2mdtimestr() ############################################################################ # doy2md: # # Compute the month and day-of-month (mday) from a year and # day-of-year (yday). # # Year is expected to be in the range 1900-2100, jday is expected to # be in the range 1-366, month will be in the range 1-12 and mday # will be in the range 1-31 # # Returns the month and day-of-month (mday) ############################################################################ sub doy2md { # doy2md (year, yday) my $year = shift; my $yday = shift; my @days = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); my $month = -1; my $mday = -1; # Sanity check for the supplied year if ( $year < 1900 || $year > 2100 ) { print "doy2md(): year ($year) is out of range\n"; return; } # Test for leap year my $leap = ( (($year%4 == 0) && ($year%100 != 0)) || ($year%400 == 0) ) ? 1 : 0; # Add a day to February if leap year if ( $leap ) { $days[1]++; } if ( $yday > 365+$leap || $yday <= 0 ) { print "doy2md(): day-of-year ($yday) is out of range\n"; return; } for ( my $idx=0; $idx < 12; $idx++ ) { $yday -= $days[$idx]; if ( $yday <= 0 ) { $month = $idx + 1; $mday = $days[$idx] + $yday; last; } } return ($month, $mday); } # End of doy2md() ############################################################################ # md2doy: # # Compute the day-of-year from a year, month and day-of-month. # # Year is expected to be in the range 1900-2100, month is expected to # be in the range 1-12, mday is expected to be in the range 1-31 and # jday will be in the range 1-366 # # Returns 0 on success and -1 on error. ############################################################################ sub md2doy { # md2doy (year, month, mday) my $year = shift; my $month = shift; my $mday = shift; my @days = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); # Sanity check for the supplied parameters */ if ( $year < 1900 || $year > 2100 ) { print "md2doy(): year ($year) is out of range\n"; return; } if ( $month < 1 || $month > 12 ) { print "md2doy(): month ($month) is out of range\n"; return; } if ( $mday < 1 || $mday > 31 ) { print "md2doy(): day-of-month ($mday) is out of range\n"; return; } # Test for leap year my $leap = ( (($year%4 == 0) && ($year%100 != 0)) || ($year%400 == 0) ) ? 1 : 0; # Add a day to February if leap year if ( $leap ) { $days[1]++; } # Check that the day-of-month jives with specified month if ( $mday > $days[$month-1] ) { print "md2doy(): day-of-month ($mday) is out of range for month $month\n"; return; } $month--; my $yday = 0; for ( my $idx=0; $idx < 12; $idx++ ) { if ( $idx == $month ) { $yday += $mday; last; } $yday += $days[$idx]; } return $yday; } # End of md2doy() return 1;