698 lines
18 KiB
Perl
698 lines
18 KiB
Perl
package WebGUI::DateTime;
|
|
|
|
=head1 LEGAL
|
|
|
|
-------------------------------------------------------------------
|
|
WebGUI is Copyright 2001-2005 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 DateTime;
|
|
use DateTime::Format::Strptime;
|
|
use DateTime::TimeZone;
|
|
use Tie::IxHash;
|
|
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);
|
|
($startEpoch, $endEpoch) = WebGUI::DateTime::dayStartEnd($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);
|
|
$zones = WebGUI::DateTime::getTimeZones();
|
|
$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
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 = DateTime->from_epoch( epoch =>shift);
|
|
my $years = shift || 0;
|
|
my $months = shift || 0;
|
|
my $days = shift || 0;
|
|
my $currentTimeZone = $date->time_zone->name;
|
|
$date->set_time_zone('UTC'); # do this to prevent date math errors due to daylight savings time shifts
|
|
$date->add(years=>$years, months=>$months, days=>$days);
|
|
$date->set_time_zone($currentTimeZone);
|
|
return $date->epoch;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 $epoch = shift;
|
|
return undef unless $epoch;
|
|
my $date = DateTime->from_epoch( epoch =>$epoch);
|
|
my $hours = shift || 0;
|
|
my $mins = shift || 0;
|
|
my $secs = shift || 0;
|
|
$date->add(hours=>$hours, minutes=>$mins, seconds=>$secs);
|
|
return $date->epoch;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 $dt = DateTime->from_epoch( epoch => shift);
|
|
my $end = $dt->clone;
|
|
$dt->set_hour(0);
|
|
$dt->set_minute(0);
|
|
$dt->set_second(0);
|
|
$end->set_hour(23);
|
|
$end->set_minute(59);
|
|
$end->set_second(59);
|
|
return ($dt->epoch, $end->epoch);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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.
|
|
%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/UTC 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.
|
|
%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 $language = WebGUI::International::getLanguage($session{user}{language});
|
|
my $locale = $language->{languageAbbreviation} || "en";
|
|
$locale .= "_".$language->{locale} if ($language->{locale});
|
|
my $timeZone = $session{user}{timeZone} || "America/Chicago";
|
|
my $dt = DateTime->from_epoch( epoch=>shift||time(), time_zone=>$timeZone, locale=>$locale );
|
|
my $output = shift || "%z %Z";
|
|
my $temp;
|
|
#---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;
|
|
#--- convert WebGUI date formats to DateTime formats
|
|
my %conversion = (
|
|
"c" => "B",
|
|
"C" => "b",
|
|
"d" => "d",
|
|
"D" => "e",
|
|
"h" => "I",
|
|
"H" => "l",
|
|
"j" => "H",
|
|
"J" => "k",
|
|
"m" => "m",
|
|
"M" => "_varmonth_",
|
|
"n" => "M",
|
|
"O" => "z",
|
|
"p" => "P",
|
|
"P" => "p",
|
|
"s" => "S",
|
|
"w" => "A",
|
|
"W" => "a",
|
|
"y" => "Y",
|
|
"Y" => "y"
|
|
);
|
|
$output =~ s/\%(\w)/\~$1/g;
|
|
foreach my $key (keys %conversion) {
|
|
my $replacement = $conversion{$key};
|
|
$output =~ s/\~$key/\%$replacement/g;
|
|
}
|
|
#--- %M
|
|
$output = $dt->strftime($output);
|
|
$temp = int($dt->month);
|
|
$output =~ s/\%_varmonth_/$temp/g;
|
|
#--- return
|
|
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 {
|
|
my $timeZone = $session{user}{timeZone} || "America/Chicago";
|
|
my $dt = DateTime->from_epoch( epoch =>shift, time_zone=>$timeZone);
|
|
my $withTime = shift;
|
|
if ($withTime) {
|
|
return $dt->strftime("%Y-%m-%d %H:%M:%S");
|
|
}
|
|
return $dt->strftime("%Y-%m-%d");
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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('sunday','DateTime');
|
|
} elsif ($day == 1) {
|
|
return WebGUI::International::get('monday','DateTime');
|
|
} elsif ($day == 2) {
|
|
return WebGUI::International::get('tuesday','DateTime');
|
|
} elsif ($day == 3) {
|
|
return WebGUI::International::get('wednesday','DateTime');
|
|
} elsif ($day == 4) {
|
|
return WebGUI::International::get('thursday','DateTime');
|
|
} elsif ($day == 5) {
|
|
return WebGUI::International::get('friday','DateTime');
|
|
} elsif ($day == 6) {
|
|
return WebGUI::International::get('saturday','DateTime');
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getDaysInMonth ( epoch )
|
|
|
|
Returns the total number of days in the month.
|
|
|
|
=head3 epoch
|
|
|
|
An epoch date.
|
|
|
|
=cut
|
|
|
|
sub getDaysInMonth {
|
|
my $dt = DateTime->from_epoch( epoch =>shift);
|
|
my $last = DateTime->last_day_of_month(year=>$dt->year, month=>$dt->month);
|
|
return $last->day;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 = DateTime->from_epoch( epoch =>shift);
|
|
my $end = DateTime->from_epoch( epoch =>shift);
|
|
my $duration = $end - $start;
|
|
return $duration->delta_days;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getFirstDayInMonthPosition ( epoch) {
|
|
|
|
Returns the position (1 - 7) of the first day in the month. 1 is Monday.
|
|
|
|
=head3 epoch
|
|
|
|
An epoch date.
|
|
|
|
=cut
|
|
|
|
sub getFirstDayInMonthPosition {
|
|
my $dt = DateTime->from_epoch( epoch => shift );
|
|
$dt->set_day(1);
|
|
return $dt->day_of_week;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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('january','DateTime');
|
|
} elsif ($_[0] == 2) {
|
|
return WebGUI::International::get('february','DateTime');
|
|
} elsif ($_[0] == 3) {
|
|
return WebGUI::International::get('march','DateTime');
|
|
} elsif ($_[0] == 4) {
|
|
return WebGUI::International::get('april','DateTime');
|
|
} elsif ($_[0] == 5) {
|
|
return WebGUI::International::get('may','DateTime');
|
|
} elsif ($_[0] == 6) {
|
|
return WebGUI::International::get('june','DateTime');
|
|
} elsif ($_[0] == 7) {
|
|
return WebGUI::International::get('july','DateTime');
|
|
} elsif ($_[0] == 8) {
|
|
return WebGUI::International::get('august','DateTime');
|
|
} elsif ($_[0] == 9) {
|
|
return WebGUI::International::get('september','DateTime');
|
|
} elsif ($_[0] == 10) {
|
|
return WebGUI::International::get('october','DateTime');
|
|
} elsif ($_[0] == 11) {
|
|
return WebGUI::International::get('november','DateTime');
|
|
} elsif ($_[0] == 12) {
|
|
return WebGUI::International::get('december','DateTime');
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 {
|
|
my $dt = DateTime->from_epoch( epoch => shift );
|
|
my $start = $dt->clone;
|
|
$start->set_hour(0);
|
|
$start->set_minute(0);
|
|
$start->set_second(0);
|
|
my $duration = $dt - $start;
|
|
return $duration->delta_seconds;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getTimeZones ( )
|
|
|
|
Returns a hash reference containing name/value pairs both with the list of time zones.
|
|
|
|
=cut
|
|
|
|
sub getTimeZones {
|
|
my %zones;
|
|
tie %zones, 'Tie::IxHash';
|
|
foreach my $zone (@{DateTime::TimeZone::all_names()}) {
|
|
my $zoneLabel = $zone;
|
|
$zoneLabel =~ s/\_/ /g;
|
|
$zones{$zone} = $zoneLabel;
|
|
}
|
|
return \%zones;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 ($dateString,$timeString) = split(/ /,shift);
|
|
my @date = split(/-/,$dateString);
|
|
my @time = split(/:/,$timeString);
|
|
$time[0] = 0 if $time[0] == 24;
|
|
my $dt = DateTime->new(year => $date[0], month=> $date[1], day=> $date[2], hour=> $time[0], minute => $time[1], second => $time[2]);
|
|
return $dt->epoch;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 {
|
|
my $interval = shift;
|
|
my $units = shift;
|
|
if ($units eq "years") {
|
|
return ($interval*31536000);
|
|
} elsif ($units eq "months") {
|
|
return ($interval*2592000);
|
|
} elsif ($units eq "weeks") {
|
|
return ($interval*604800);
|
|
} elsif ($units eq "days") {
|
|
return ($interval*86400);
|
|
} elsif ($units eq "hours") {
|
|
return ($interval*3600);
|
|
} elsif ($units eq "minutes") {
|
|
return ($interval*60);
|
|
} else {
|
|
return $interval;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 $dt = DateTime->from_epoch( epoch => shift || time() );
|
|
return ( $dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second, $dt->day_of_year, $dt->day_of_week, $dt->is_dst );
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
=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 = DateTime->from_epoch( epoch => shift );
|
|
my $end = DateTime->from_epoch( epoch => shift );
|
|
my $duration = $end - $start;
|
|
return $duration->delta_months;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 $dt = DateTime->from_epoch( epoch => shift);
|
|
my $end = DateTime->last_day_of_month(year=>$dt->year, month=>$dt->month);
|
|
$dt->set_hour(0);
|
|
$dt->set_minute(0);
|
|
$dt->set_second(0);
|
|
$end->set_hour(23);
|
|
$end->set_minute(59);
|
|
$end->set_second(59);
|
|
return ($dt->epoch, $end->epoch);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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 $seconds = shift;
|
|
my ($interval, $units);
|
|
if ($seconds >= 31536000) {
|
|
$interval = round($seconds/31536000);
|
|
$units = "years";
|
|
} elsif ($seconds >= 2592000) {
|
|
$interval = round($seconds/2592000);
|
|
$units = "months";
|
|
} elsif ($seconds >= 604800) {
|
|
$interval = round($seconds/604800);
|
|
$units = "weeks";
|
|
} elsif ($seconds >= 86400) {
|
|
$interval = round($seconds/86400);
|
|
$units = "days";
|
|
} elsif ($seconds >= 3600) {
|
|
$interval = round($seconds/3600);
|
|
$units = "hours";
|
|
} elsif ($seconds >= 60) {
|
|
$interval = round($seconds/60);
|
|
$units = "minutes";
|
|
} else {
|
|
$interval = $seconds;
|
|
$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 = shift;
|
|
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 $set = shift;
|
|
return undef unless $set;
|
|
my $parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S' );
|
|
my $dt = $parser->parse_datetime($set);
|
|
unless ($dt) {
|
|
$parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d' );
|
|
$dt = $parser->parse_datetime($set);
|
|
}
|
|
# in epochToSet we apply the user's time zone, so now we have to remove it.
|
|
$dt->set_time_zone($session{user}{timeZone}|| "America/Chicago"); # assign the user's timezone
|
|
return $dt->epoch;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 time ( )
|
|
|
|
Returns an epoch date for now.
|
|
|
|
=cut
|
|
|
|
sub time {
|
|
return time();
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=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*60*60+$min*60+$sec);
|
|
}
|
|
|
|
|
|
1;
|