webgui/lib/WebGUI/DateTime.pm
2004-09-16 20:36:17 +00:00

780 lines
19 KiB
Perl

package WebGUI::DateTime;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2004 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use Date::Manip;
use Exporter;
use strict;
use WebGUI::International;
use WebGUI::Session;
use WebGUI::Utility;
our @ISA = qw(Exporter);
our @EXPORT = qw(&localtime &time &addToTime &addToDate &epochToHuman &epochToSet &humanToEpoch &setToEpoch &monthStartEnd);
=head1 NAME
Package WebGUI::DateTime
=head1 DESCRIPTION
This package provides easy to use date math functions, which are normally a complete pain.
=head1 SYNOPSIS
use WebGUI::DateTime;
$epoch = WebGUI::DateTime::addToDate($epoch, $years, $months, $days);
$epoch = WebGUI::DateTime::addToTime($epoch, $hours, $minutes, $seconds);
$epoch = WebGUI::DateTime::arrayToEpoch(@date);
($startEpoch, $endEpoch) = WebGUI::DateTime::dayStartEnd($epoch);
@date = WebGUI::DateTime::epochToArray($epoch);
$dateString = WebGUI::DateTime::epochToHuman($epoch, $formatString);
$setString = WebGUI::DateTime::epochToSet($epoch);
$day = WebGUI::DateTime::getDayName($dayInteger);
$integer = WebGUI::DateTime::getDaysInMonth($epoch);
$integer = WebGUI::DateTime::getDaysInInterval($start, $end);
$integer = WebGUI::DateTime::getFirstDayInMonthPosition($epoch);
$month = WebGUI::DateTime::getMonthName($monthInteger);
$seconds = WebGUI::DateTime::getSecondsFromEpoch($seconds);
$epoch = WebGUI::DateTime::humanToEpoch($dateString);
$seconds = WebGUI::DateTime::intervalToSeconds($interval, $units);
@date = WebGUI::DateTime::localtime($epoch);
($startEpoch, $endEpoch) = WebGUI::DateTime::monthStartEnd($epoch);
($interval, $units) = WebGUI::DateTime::secondsToInterval($seconds);
$timeString = WebGUI::DateTime::secondsToTime($seconds);
$epoch = WebGUI::DateTime::setToEpoch($setString);
$epoch = WebGUI::DateTime::time();
$seconds = WebGUI::DateTime::timeToSeconds($timeString);
=head1 METHODS
These functions are available from this package:
=cut
sub epochToDate {
my $secs = shift;
return &ParseDateString("epoch $secs");
}
sub dateToEpoch {
return &UnixDate(shift,"%s");
}
#-------------------------------------------------------------------
=head2 addToDate ( epoch [ , years, months, days ] )
Returns an epoch date with the amount of time added.
=head3 epoch
The number of seconds since January 1, 1970.
=head3 years
The number of years to add to the epoch.
=head3 months
The number of months to add to the epoch.
=head3 days
The number of days to add to the epoch.
=cut
sub addToDate {
my ($date,$years,$months,$days,$newDate);
$date = &epochToDate(shift);
$years = shift || 0;
$months = shift || 0;
$days = shift || 0;
$newDate = DateCalc($date,"+$years:$months:0:$days:0:0:0");
return &dateToEpoch($newDate);
}
#-------------------------------------------------------------------
=head2 addToTime ( epoch [ , hours, minutes, seconds ] )
Returns an epoch date with the amount of time added.
=head3 epoch
The number of seconds since January 1, 1970.
=head3 hours
The number of hours to add to the epoch.
=head3 minutes
The number of minutes to add to the epoch.
=head3 seconds
The number of seconds to add to the epoch.
=cut
sub addToTime {
my ($date,$hours,$mins,$secs,$newDate);
$date = &epochToDate(shift);
$hours = shift || 0;
$mins = shift || 0;
$secs = shift || 0;
$newDate = DateCalc($date,"+0:0:0:0:$hours:$mins:$secs");
return &dateToEpoch($newDate);
}
#-------------------------------------------------------------------
=head2 arrayToEpoch ( date )
Returns an epoch date.
=head3 date
An array of the format year, month, day, hour, min, sec.
=cut
sub arrayToEpoch {
my $year = shift || '0000';
my $month = shift || '00';
my $day = shift || '00';
my $hour = shift || '00';
my $min = shift || '00';
my $sec = shift || '00';
$min = "0$min" if (length($min) == 1);
$sec = "0$sec" if (length($sec) == 1);
return &dateToEpoch(&ParseDate("$year-$month-$day $hour:$min:$sec"));
}
#-------------------------------------------------------------------
=head2 dayStartEnd ( epoch )
Returns the epoch dates for the start and end of the day.
=head3 epoch
The number of seconds since January 1, 1970.
=cut
sub dayStartEnd {
my ($year,$month,$day, $hour,$min,$sec, $start, $end);
($year,$month,$day, $hour,$min,$sec) = epochToArray($_[0]);
$start = &arrayToEpoch($year,$month,$day,0,0,0);
$end = &arrayToEpoch($year,$month,$day,23,59,59);
return ($start, $end);
}
#-------------------------------------------------------------------
=head2 epochToArray ( epoch )
Returns a date array in the form of year, month, day, hour, min, sec.
=head3 epoch
An epoch date.
=cut
sub epochToArray {
my $epoch = shift;
my @date = &UnixDate(epochToDate($epoch),'%Y','%m','%d','%H','%M','%S');
$date[0] = $date[0]+0;
$date[1] = $date[1]+0;
$date[2] = $date[2]+0;
$date[3] = $date[3]+0;
$date[4] = $date[4]+0;
$date[5] = $date[5]+0;
return @date;
}
#-------------------------------------------------------------------
=head2 epochToHuman ( [ epoch, format ] )
Returns a formated date string.
=head3 epoch
The number of seconds since January 1, 1970. Defaults to NOW!
=head3 format
A string representing the output format for the date. Defaults to '%z %Z'. You can use the following to format your date string:
%% = % (percent) symbol.
%c = The calendar month name.
%C = The calendar month name abbreviated to 3 characters and represented in English.
%d = A two digit day.
%D = A variable digit day.
%h = A two digit hour (on a 12 hour clock).
%H = A variable digit hour (on a 12 hour clock).
%j = A two digit hour (on a 24 hour clock).
%J = A variable digit hour (on a 24 hour clock).
%m = A two digit month.
%M = A variable digit month.
%n = A two digit minute.
%o = Offset from GMT represented as an integer.
%O = Offset from GMT represented in four digit form with a sign. Example: -0600
%p = A lower-case am/pm.
%P = An upper-case AM/PM.
%s = A two digit second.
%w = Day of the week.
%W = Day of the week abbreviated to 3 characters and represented in English.
%y = A four digit year.
%Y = A two digit year.
%z = The current user's date format preference.
%Z = The current user's time format preference.
=cut
sub epochToHuman {
my ($offset, $temp, $hour12, $value, $output);
$offset = $session{user}{timeOffset} || 0;
$offset = $offset*3600;
$temp = int($_[0]) || WebGUI::DateTime::time();
$temp = $temp+$offset;
my $dt = epochToDate($temp);
my ($year,$month,$day,$hour,$min,$sec) = epochToArray($temp);
$output = $_[1] || "%z %Z";
#---GMT Offsets
$temp = $session{user}{timeOffset}*100;
$temp = sprintf('%+05d',$temp);
$output =~ s/\%O/$temp/g;
$temp = $session{user}{timeOffset}+0;
$output =~ s/\%o/$temp/g;
#---dealing with percent symbol
$output =~ s/\%\%/\%/g;
#---date format preference
$temp = $session{user}{dateFormat} || '%M/%D/%y';
$output =~ s/\%z/$temp/g;
#---time format preference
$temp = $session{user}{timeFormat} || '%H:%n %p';
$output =~ s/\%Z/$temp/g;
#---year stuff
$output =~ s/\%y/$year/g;
$value = substr($year,2,2);
$output =~ s/\%Y/$value/g;
#---month stuff
$value = sprintf("%02d",$month);
$output =~ s/\%m/$value/g;
$output =~ s/\%M/$month/g;
if ($output =~ /\%c/) {
$temp = getMonthName($month);
$output =~ s/\%c/$temp/g;
}
if ($output =~ /\%C/) {
$temp = &UnixDate($dt,'%b');
$output =~ s/\%C/$temp/g;
}
#---day stuff
$value = sprintf("%02d",$day);
$output =~ s/\%d/$value/g;
$output =~ s/\%D/$day/g;
if ($output =~ /\%w/) {
$temp = getDayName(&UnixDate($dt,'%w'));
$output =~ s/\%w/$temp/g;
}
if ($output =~ /%W/) {
$temp = &UnixDate($dt,'%a');
$output =~ s/\%W/$temp/g;
}
#---hour stuff
$hour12 = $hour;
if ($hour12 > 12) {
$hour12 = $hour12 - 12;
}
if ($hour12 == 0) {
$hour12 = 12;
}
$value = sprintf("%02d",$hour12);
$output =~ s/\%h/$value/g;
$output =~ s/\%H/$hour12/g;
$value = sprintf("%02d",$hour);
$output =~ s/\%j/$value/g;
$output =~ s/\%J/$hour/g;
if ($hour > 11) {
$output =~ s/\%p/pm/g;
$output =~ s/\%P/PM/g;
} else {
$output =~ s/\%p/am/g;
$output =~ s/\%P/AM/g;
}
#---minute stuff
$value = sprintf("%02d",$min);
$output =~ s/\%n/$value/g;
#---second stuff
$value = sprintf("%02d",$sec);
$output =~ s/\%s/$value/g;
return $output;
}
#-------------------------------------------------------------------
=head2 epochToSet ( epoch, withTime )
Returns a set date (used by WebGUI::HTMLForm->date) in the format of YYYY-MM-DD.
=head3 epoch
The number of seconds since January 1, 1970.
=head3 withTime
A boolean indicating that the time should be added to the output, thust turning the format into YYYY-MM-DD HH:MM:SS.
=cut
sub epochToSet {
if ($_[1]) {
return epochToHuman($_[0],"%y-%m-%d %j:%n:%s");
}
return epochToHuman($_[0],"%y-%m-%d");
}
#-------------------------------------------------------------------
=head2 getMonthName ( month )
Returns a string containing the calendar month name in the language of the current user.
=head3 month
An integer ranging from 1-12 representing the month.
=cut
sub getMonthName {
if ($_[0] == 1) {
return WebGUI::International::get(15);
} elsif ($_[0] == 2) {
return WebGUI::International::get(16);
} elsif ($_[0] == 3) {
return WebGUI::International::get(17);
} elsif ($_[0] == 4) {
return WebGUI::International::get(18);
} elsif ($_[0] == 5) {
return WebGUI::International::get(19);
} elsif ($_[0] == 6) {
return WebGUI::International::get(20);
} elsif ($_[0] == 7) {
return WebGUI::International::get(21);
} elsif ($_[0] == 8) {
return WebGUI::International::get(22);
} elsif ($_[0] == 9) {
return WebGUI::International::get(23);
} elsif ($_[0] == 10) {
return WebGUI::International::get(24);
} elsif ($_[0] == 11) {
return WebGUI::International::get(25);
} elsif ($_[0] == 12) {
return WebGUI::International::get(26);
}
}
#-------------------------------------------------------------------
=head2 getDayName ( day )
Returns a string containing the weekday name in the language of the current user.
=head3 day
An integer ranging from 1-7 representing the day of the week (Sunday is 1 and Saturday is 7).
=cut
sub getDayName {
my $day = $_[0];
if ($day == 7) {
return WebGUI::International::get(27);
} elsif ($day == 1) {
return WebGUI::International::get(28);
} elsif ($day == 2) {
return WebGUI::International::get(29);
} elsif ($day == 3) {
return WebGUI::International::get(30);
} elsif ($day == 4) {
return WebGUI::International::get(31);
} elsif ($day == 5) {
return WebGUI::International::get(32);
} elsif ($day == 6) {
return WebGUI::International::get(33);
}
}
#-------------------------------------------------------------------
=head2 getDaysInMonth ( epoch )
Returns the total number of days in the month.
=head3 epoch
An epoch date.
=cut
sub getDaysInMonth {
my $epoch = shift;
my @date = WebGUI::DateTime::epochToArray($epoch);
return &Date_DaysInMonth($date[1], $date[0]);
}
#-------------------------------------------------------------------
=head2 getDaysInInterval ( start, end )
Returns the number of days between two epoch dates.
=head3 start
An epoch date.
=head3 end
An epoch date.
=cut
sub getDaysInInterval {
my $start = &epochToDate(shift);
my $end = &epochToDate(shift);
my $err;
my $delta = &DateCalc($start,$end,\$err);
return &Delta_Format($delta,0,'%dh');
}
#-------------------------------------------------------------------
=head2 getFirstDayInMonthPosition ( epoch) {
Returns the position (1 - 7) of the first day in the month.
=head3 epoch
An epoch date.
=cut
sub getFirstDayInMonthPosition {
my $epoch = shift;
my @date = WebGUI::DateTime::epochToArray($epoch);
my $firstDayInFirstWeek = &UnixDate("$date[0]-$date[1]-01",'%w');
unless ($session{user}{firstDayOfWeek}) { #american format
$firstDayInFirstWeek++;
if ($firstDayInFirstWeek > 7) {
$firstDayInFirstWeek = 1;
}
}
return $firstDayInFirstWeek;
}
#-------------------------------------------------------------------
=head2 getSecondsFromEpoch ( epoch )
Calculates the number of seconds into the day of an epoch date the epoch datestamp is.
=head3 epoch
The number of seconds since January 1, 1970 00:00:00.
=cut
sub getSecondsFromEpoch {
return timeToSeconds(epochToHuman($_[0],"%j:%n:%s"));
}
#-------------------------------------------------------------------
=head2 humanToEpoch ( date )
Returns an epoch date derived from the human date.
=head3 date
The human date string. YYYY-MM-DD HH:MM:SS
=cut
sub humanToEpoch {
my (@temp, $dateString, $timeString, $output, @date);
($dateString,$timeString) = split(/ /,$_[0]);
@temp = split(/-/,$dateString);
$date[0] = int($temp[0]);
$date[1] = int($temp[1]);
$date[2] = int($temp[2]);
@temp = split(/:/,$timeString);
$date[3] = int($temp[0]);
$date[4] = int($temp[1]);
$date[5] = int($temp[2]);
$output = arrayToEpoch(@date);
return $output;
}
#-------------------------------------------------------------------
=head2 intervalToSeconds ( interval, units )
Returns the number of seconds derived from the interval.
=head3 interval
An integer which represents the amount of time for the interval.
=head3 units
A string which represents the units of the interval. The string must be 'years', 'months', 'weeks', 'days', 'hours', 'minutes', or 'seconds'.
=cut
sub intervalToSeconds {
if ($_[1] eq "years") {
return ($_[0]*31536000);
} elsif ($_[1] eq "months") {
return ($_[0]*2592000);
} elsif ($_[1] eq "weeks") {
return ($_[0]*604800);
} elsif ($_[1] eq "days") {
return ($_[0]*86400);
} elsif ($_[1] eq "hours") {
return ($_[0]*3600);
} elsif ($_[1] eq "minutes") {
return ($_[0]*60);
} else {
return $_[0];
}
}
#-------------------------------------------------------------------
=head2 localtime ( epoch )
Returns an array of time elements. The elements are: years, months, days, hours, minutes, seconds, day of year, day of week, daylight savings.
=head3 epoch
The number of seconds since January 1, 1970. Defaults to now.
=cut
sub localtime {
my $epoch = shift || &dateToEpoch(&ParseDate("today"));
my $date = &epochToDate($epoch);
my ($year, $month, $day, $hour, $min, $sec) = epochToArray($epoch);
if ($epoch) {
($year, $month, $day, $hour, $min, $sec) = epochToArray($epoch);
}
my $doy = &UnixDate($date,'%j');
my $dow = &UnixDate($date,'%w');
my @temp = localtime($epoch);
return ($year, $month, $day, $hour, $min, $sec, $doy, $dow, $temp[8]);
}
#-------------------------------------------------------------------
=head2 monthCount ( startEpoch, endEpoch )
Returns the number of months between the start and end dates (inclusive).
=head3 startEpoch
An epoch datestamp corresponding to the first month.
=head3 endEpoch
An epoch datestamp corresponding to the last month.
=cut
sub monthCount {
my $start = &epochToDate(shift);
my $end = &epochToDate(shift);
my $err;
my $delta = &DateCalc($start,$end,\$err,1);
my $count = 1+&Delta_Format($delta,0,'%Mv')+&Delta_Format($delta,0,'%yv')*12;
return $count;
}
#-------------------------------------------------------------------
=head2 monthStartEnd ( epoch )
Returns the epoch dates for the start and end of the month.
=head3 epoch
The number of seconds since January 1, 1970.
=cut
sub monthStartEnd {
my ($year,$month,$day, $hour,$min,$sec, $start, $end);
($year,$month,$day, $hour,$min,$sec) = epochToArray($_[0]);
$start = &arrayToEpoch($year,$month,1,0,0,0) + 0;
$end = &UnixDate(&DateCalc(&epochToDate($start), "+1 month"),'%s')-1;
return ($start, $end);
}
#-------------------------------------------------------------------
=head2 secondsToInterval ( seconds )
Returns an interval and units derived the number of seconds.
=head3 seconds
The number of seconds in the interval.
=cut
sub secondsToInterval {
my ($interval, $units);
if ($_[0] >= 31536000) {
$interval = round($_[0]/31536000);
$units = "years";
} elsif ($_[0] >= 2592000) {
$interval = round($_[0]/2592000);
$units = "months";
} elsif ($_[0] >= 604800) {
$interval = round($_[0]/604800);
$units = "weeks";
} elsif ($_[0] >= 86400) {
$interval = round($_[0]/86400);
$units = "days";
} elsif ($_[0] >= 3600) {
$interval = round($_[0]/3600);
$units = "hours";
} elsif ($_[0] >= 60) {
$interval = round($_[0]/60);
$units = "minutes";
} else {
$interval = $_[0];
$units = "seconds";
}
return ($interval, $units);
}
#-------------------------------------------------------------------
=head2 secondsToTime ( seconds )
Returns a time string of the format HH::MM::SS on a 24 hour clock. See also timeToSeconds().
=head3 seconds
A number of seconds.
=cut
sub secondsToTime {
my $seconds = $_[0];
my $timeString = sprintf("%02d",int($seconds / 3600)).":";
$seconds = $seconds % 3600;
$timeString .= sprintf("%02d",int($seconds / 60)).":";
$seconds = $seconds % 60;
$timeString .= sprintf("%02d",$seconds);
return $timeString;
}
#-------------------------------------------------------------------
=head2 setToEpoch ( set )
Returns an epoch date.
=head3 set
A string in the format of YYYY-MM-DD or YYYY-MM-DD HH:MM:SS.
=cut
sub setToEpoch {
my @now = epochToArray(WebGUI::DateTime::time());
my ($date,$time) = split(/ /,$_[0]);
my ($year, $month, $day) = split(/\-/,$date);
my ($hour, $minute, $second) = split(/\:/,$time);
if (int($year) < 3000 && int($year) > 1000) {
$year = int($year);
} else {
$year = $now[0];
}
if (int($month) < 13 && int($month) > 0) {
$month = int($month);
} else {
$month = $now[1]++;
}
if (int($day) < 32 && int($day) > 0) {
$day = int($day);
} else {
$day = $now[2];
}
return arrayToEpoch($year,$month,$day,$hour,$minute,$second);
}
#-------------------------------------------------------------------
=head2 time ( )
Returns an epoch date for now.
=cut
sub time {
return dateToEpoch(&ParseDate("now"));
}
#-------------------------------------------------------------------
=head2 timeToSeconds ( timeString )
Returns the seconds since 00:00:00 on a 24 hour clock.
=head3 timeString
A string that looks similar to this: 15:05:32
=cut
sub timeToSeconds {
my ($hour,$min,$sec) = split(/:/,$_[0]);
return ($hour*3600+$min*60+$sec);
}
1;