New methods in WebGUI::DateTime that clearly state how they handle timezones.

The standard is everything going into the DB is in UTC.  Timezones have to be
manually handled after being fetched.
Added a test for the new methods and for object construction.
Changed Calendar and Event Assets to use the new methods.
Interim checkin to get some debug help from Doug.
This commit is contained in:
Colin Kuskie 2007-01-19 05:16:57 +00:00
parent f6a956c447
commit 17fac4a7cb
4 changed files with 394 additions and 52 deletions

View file

@ -51,7 +51,7 @@ sub definition {
my $i18n = WebGUI::International->new($session, 'Asset_Event');
my $dt = WebGUI::DateTime->new(time);
my $dt = WebGUI::DateTime->new($session, time);
### Set up list options ###
@ -215,15 +215,15 @@ sub generateRecurringEvents
# Get the distance between the event startDate and endDate
my $duration_days = 0;
my $event_start = WebGUI::DateTime->new(delete($properties->{startDate})." 00:00:00");
my $event_end = WebGUI::DateTime->new(delete($properties->{endDate})." 00:00:00");
my $event_start = WebGUI::DateTime->new($self->session, delete($properties->{startDate})." 00:00:00");
my $event_end = WebGUI::DateTime->new($self->session, delete($properties->{endDate})." 00:00:00");
$duration_days = $event_end->subtract_datetime($event_start)->days;
my @dates = $self->getRecurrenceDates($recur);
for my $date (@dates)
{
my $dt = WebGUI::DateTime->new($date." 00:00:00");
my $dt = WebGUI::DateTime->new($self->session, $date." 00:00:00");
### TODO: Only generate if the recurId does not exist on this day
$properties->{startDate} = $dt->strftime('%F');
@ -289,13 +289,13 @@ sub getDateTimeStart
if ($time)
{
my $dt = new WebGUI::DateTime($date." ".$time);
my $dt = new WebGUI::DateTime($self->session, $date." ".$time);
$dt->set_time_zone($tz);
return $dt;
}
else
{
my $dt = new WebGUI::DateTime($date." 00:00:00");
my $dt = new WebGUI::DateTime($self->session, $date." 00:00:00");
return $dt;
}
}
@ -333,13 +333,13 @@ sub getDateTimeEnd
if ($time)
{
my $dt = new WebGUI::DateTime($date." ".$time);
my $dt = new WebGUI::DateTime($self->session, $date." ".$time);
$dt->set_time_zone($tz);
return $dt;
}
else
{
my $dt = new WebGUI::DateTime($date." 23:59:59");
my $dt = new WebGUI::DateTime($self->session, $date." 23:59:59");
return $dt;
}
}
@ -719,9 +719,9 @@ sub getRecurrenceDates
);
my $dt = WebGUI::DateTime->new($recur->{startDate}." 00:00:00");
my $dt = WebGUI::DateTime->new($self->session, $recur->{startDate}." 00:00:00");
my $dt_start = $dt->clone; # Keep track of the initial start date
my $dt_end = WebGUI::DateTime->new($recur->{endDate}." 00:00:00")
my $dt_end = WebGUI::DateTime->new($self->session, $recur->{endDate}." 00:00:00")
if $recur->{endDate};
# Set an end for events with no end
#!!! TODO !!! - Get the appropriate configuration
@ -829,7 +829,7 @@ sub getRecurrenceDates
# Pick out the correct day
my $startDate = $dt->year."-".$dt->month."-".$recur->{dayNumber};
my $dt_day = WebGUI::DateTime->new($startDate." 00:00:00");
my $dt_day = WebGUI::DateTime->new($self->session, $startDate." 00:00:00");
# Only if today is not before the recurrence start
if ($dt_day->clone->truncate(to => "day") >= $dt_start->clone->truncate(to=>"day"))
@ -951,7 +951,7 @@ sub getRecurrenceDates
# Pick out the correct day
my $startDate = $dt_month->year."-".$dt_month->month."-".$recur->{dayNumber};
my $dt_day = WebGUI::DateTime->new($startDate." 00:00:00");
my $dt_day = WebGUI::DateTime->new($self->session, $startDate." 00:00:00");
# Only if today is not before the recurrence start
if ($dt_day->clone->truncate(to => "day") >= $dt_start->clone->truncate(to=>"day"))
@ -1423,10 +1423,10 @@ sub processPropertiesFromFormPost
# Convert timezone
my $tz = $self->session->user->profileField("timeZone");
my ($startDate,$startTime) = split / /, WebGUI::DateTime->new(mysql => $self->get("startDate")." ".$self->get("startTime"), time_zone => $tz)
my ($startDate,$startTime) = split / /, WebGUI::DateTime->new($self->session, mysql => $self->get("startDate")." ".$self->get("startTime"), time_zone => $tz)
->set_time_zone("UTC")->toMysql;
my ($endDate,$endTime) = split / /, WebGUI::DateTime->new(mysql => $self->get("endDate")." ".$self->get("endTime"), time_zone => $tz)
my ($endDate,$endTime) = split / /, WebGUI::DateTime->new($self->session, mysql => $self->get("endDate")." ".$self->get("endTime"), time_zone => $tz)
->set_time_zone("UTC")->toMysql;
$self->update({ startDate => $startDate,
@ -1747,39 +1747,42 @@ sub www_edit
});
# start date
my $default_start = WebGUI::DateTime->new($session->form->param("start") || time)
my $default_start = WebGUI::DateTime->new($self->session, $session->form->param("start") || time)
->set_time_zone($tz);
my ($startDate,$startTime) = split / /, $self->getDateTimeStart->toMysql
my ($startDate,$startTime) = split / /, $self->getDateTimeStart->toUserTimeZone
unless $func eq "add" || $self->get("assetId") eq "new";
$var->{"formStartDate"}= WebGUI::Form::date($session,
{
name => "startDate",
value => $form->process("startDate") || $startDate,
defaultValue => $default_start->toMysqlDate,
defaultValue => $default_start->toUserTimeZoneDate,
});
$var->{"formStartTime"} = WebGUI::Form::timeField($session,
{
name => "startTime",
value => $form->process("startTime") || $startTime,
defaultValue => $default_start->toMysqlTime,
defaultValue => $default_start->toUserTimeZoneTime,
});
# end date
$default_start->add(hours => 1);
my ($endDate,$endTime) = split / /, $self->getDateTimeEnd->toMysql
#my ($endDate,$endTime) = split / /, $self->getDateTimeEnd->toMysql
my ($endDate,$endTime) = split / /, $self->getDateTimeEnd->toUserTimeZone
unless $func eq "add" || $self->get("assetId") eq "new";
$var->{"formEndDate"} = WebGUI::Form::date($session,
{
name => "endDate",
value => $form->process("endDate") || $endDate,
defaultValue => $default_start->toMysqlDate,
#defaultValue => $default_start->toMysqlDate,
defaultValue => $default_start->toUserTimeZoneDate,
});
$var->{"formEndTime"} = WebGUI::Form::timeField($session,
{
name => "endTime",
value => $form->process("endTime") || $endTime,
defaultValue => $default_start->toMysqlTime,
#defaultValue => $default_start->toUserTimeZoneTime,
});
# time

View file

@ -635,9 +635,9 @@ sub getEventsIn
my ($startDate,$startTime) = split / /, $start;
my ($endDate,$endTime) = split / /, $end;
my $startTz = WebGUI::DateTime->new(mysql => $start, time_zone => $tz)
my $startTz = WebGUI::DateTime->new($self->session, mysql => $start, time_zone => $tz)
->set_time_zone("UTC")->toMysql;
my $endTz = WebGUI::DateTime->new(mysql => $end, time_zone => $tz)
my $endTz = WebGUI::DateTime->new($self->session, mysql => $end, time_zone => $tz)
->set_time_zone("UTC")->toMysql;
my $where = qq{(Event.startTime IS NULL && Event.endTime IS NULL && Event.startDate >= '$startDate' && Event.startDate < '$endDate')}
@ -886,7 +886,7 @@ sub view
}
else
{
$params->{start} = WebGUI::DateTime->from_epoch(epoch => time(), time_zone => $session->user->profileField("timeZone"))->toMysql;
$params->{start} = WebGUI::DateTime->new($self->session, time)->toUserTimeZone;
}
}
$params->{type} ||= $self->get("defaultView") || "Month";
@ -979,7 +979,7 @@ sub viewDay
### Get all the events in this time period
# Get the range of the epoch of this day
my $dt = WebGUI::DateTime->new($params->{start});
my $dt = WebGUI::DateTime->new($self->session, $params->{start});
$dt->set_locale($i18n->get("locale"));
$dt->truncate( to => "day");
@ -1073,12 +1073,12 @@ sub viewMonth
my $i18n = WebGUI::International->new($session,"Asset_Calendar");
my $var = {};
my $tz = $session->user->profileField("timeZone");
my $today = WebGUI::DateTime->new(time)->set_time_zone($tz)
my $today = WebGUI::DateTime->new($self->session, time)->set_time_zone($tz)
->toMysqlDate;
#### Get all the events in this time period
# Get the range of the epoch of this month
my $dt = WebGUI::DateTime->new($params->{start});
my $dt = WebGUI::DateTime->new($self->session, $params->{start});
$dt->set_locale($i18n->get("locale"));
$dt->truncate( to => "month");
@ -1220,13 +1220,13 @@ sub viewWeek
my $i18n = WebGUI::International->new($session,"Asset_Calendar");
my $var = {};
my $tz = $session->user->profileField("timeZone");
my $today = WebGUI::DateTime->new(time)->set_time_zone($tz)
my $today = WebGUI::DateTime->new($self->session, time)->set_time_zone($tz)
->toMysqlDate;
#### Get all the events in this time period
# Get the range of the epoch of this week
my $dt = WebGUI::DateTime->new($params->{start});
my $dt = WebGUI::DateTime->new($self->session, $params->{start});
$dt->truncate( to => "day");
# Apply First Day of Week settings
@ -1459,12 +1459,12 @@ sub www_ical
#}
#else
#{
$dt_start = WebGUI::DateTime->from_epoch(epoch => time(), time_zone => $session->user->profileField("timeZone"));
$dt_start = WebGUI::DateTime->new($self->session, time)->toUserTimeZone;
#}
}
else
{
$dt_start = WebGUI::DateTime->new($start)->set_time_zone($session->user->profileField("timeZone"));
$dt_start = WebGUI::DateTime->new($self->session, $start)->set_time_zone($session->user->profileField("timeZone"));
}
@ -1487,7 +1487,7 @@ sub www_ical
}
else
{
$dt_end = WebGUI::DateTime->new($end)->set_time_zone($session->user->profileField("timeZone"));
$dt_end = WebGUI::DateTime->new($self->session, $end)->set_time_zone($session->user->profileField("timeZone"));
}
@ -1519,12 +1519,12 @@ sub www_ical
# LAST-MODIFIED (revisionDate)
$ical .= qq{LAST-MODIFIED:}
. WebGUI::DateTime->new($event->get("revisionDate"))->toIcal
. WebGUI::DateTime->new($self->session, $event->get("revisionDate"))->toIcal
. "\r\n";
# CREATED (creationDate)
$ical .= qq{CREATED:}
. WebGUI::DateTime->new($event->get("creationDate"))->toIcal
. WebGUI::DateTime->new($self->session, $event->get("creationDate"))->toIcal
. "\r\n";
# DTSTART
@ -1640,7 +1640,7 @@ sub www_search
if ($self->session->user->userId eq $data->{ownerUserId} || $self->session->user->isInGroup($data->{groupIdView}) || $self->session->user->isInGroup($data->{groupIdEdit}))
{
# Format the date
my $dt = WebGUI::DateTime->new($data->{startDate}." ".($data->{startTime}?$data->{startTime}:"00:00:00"));
my $dt = WebGUI::DateTime->new($self->session, $data->{startDate}." ".($data->{startTime}?$data->{startTime}:"00:00:00"));
$dt->set_time_zone($self->session->user->profileField("timeZone"))
if ($data->{startTime});
@ -1660,7 +1660,7 @@ sub www_search
}
# Prepare the form
my $default_dt = WebGUI::DateTime->new(time);
my $default_dt = WebGUI::DateTime->new($self->session, time);
my $default_start = $default_dt->toMysqlDate;
my $default_end = $default_dt->add(years => 1)->toMysqlDate;

View file

@ -15,9 +15,21 @@ package WebGUI::DateTime;
=cut
use strict;
use WebGUI::International;
use base 'DateTime';
### Some formats for strftime()
my $MYSQL_TIME = '%H:%M:%S';
my $MYSQL_DATE = '%Y-%m-%d';
my $MYSQL_DATETIME = $MYSQL_DATE . q{ } . $MYSQL_TIME;
my $ICAL_DATETIME = '%Y%m%dT%H%M%SZ';
my $ICAL_DATE = '%Y%m%d';
=head1 NAME
WebGUI::DateTime - DateTime subclass with additional WebGUI methods
@ -46,6 +58,8 @@ WebGUI::DateTime - DateTime subclass with additional WebGUI methods
my $icalDate = $dt->toIcalDate; # Make an iCal date string
my $icalTime = $dt->toIcalTime; # Make an iCal time string
my $webguiDate = $dt->webguiDate($webguiFormat) #return the date based on WebGUI's date format string
### See perldoc DateTime for additional methods ###
@ -63,9 +77,10 @@ dealing with time zones.
=cut
#######################################################################
=head2 new ( string )
=head2 new (session, string )
Creates a new object from a MySQL Date/Time string in the format
"2006-11-06 21:12:45".
@ -73,16 +88,16 @@ Creates a new object from a MySQL Date/Time string in the format
This string is assumed to be in the UTC time zone. If it is not, use the
"mysql" => string constructor, below.
=head2 new ( integer )
=head2 new (session, integer )
Creates a new object from an epoch time.
=head2 new ( "mysql" => string, "time_zone" => string )
=head2 new (session, "mysql" => string, "time_zone" => string )
Creates a new object from a MySQL Date/Time string that is in the specified
time zone
=head2 new ( hash )
=head2 new (session, hash )
Creates a new object from a hash of data passed directly to DateTime. See
perldoc DateTime for the proper keys to be used.
@ -97,7 +112,18 @@ sub new
{
# Drop-in replacement for Perl's DateTime.pm
my $class = shift;
my $self;
my $param0 = $_[0];
my $self;
my $locale = 'en_US';
my $session;
if(ref $param0 eq "WebGUI::Session") {
$session = shift;
my $i18n = WebGUI::International->new($session);
my $language = $i18n->getLanguage($session->user->profileField('language'));
$locale = $language->{languageAbbreviation} || 'en';
$locale .= "_".$language->{locale} if ($language->{locale});
}
#use Data::Dumper;
#warn "Args to DateTime->new: ".Dumper \@_;
@ -119,7 +145,7 @@ sub new
}
elsif ($_[0] =~ /^\d+$/)
{
$self = DateTime->from_epoch(epoch=>$_[0], time_zone=>"UTC");
$self = DateTime->from_epoch(epoch=>$_[0], time_zone=>"UTC", locale=>$locale);
}
else
{
@ -129,11 +155,15 @@ sub new
);
}
#Set the session object
$self->{_session} = $session;
# If no DateTime object created yet, I don't know how
unless ($self)
{
return;
}
return bless $self, $class;
}
@ -141,6 +171,82 @@ sub new
#######################################################################
=head2 cloneToUTC
Returns a clone of the current object with the time zone changed to UTC
=cut
sub cloneToUTC {
my $self = shift;
my $copy = $self->clone;
$copy->set_time_zone("UTC");
return $copy;
}
#######################################################################
=head2 cloneToUserTimeZone
Returns a clone of the current object with the time zone changed to the
current users's time zone.
=cut
sub cloneToUserTimeZone {
my $self = shift;
my $copy = $self->clone;
my $timezone = $self->session->user->profileField("timeZone");
$copy->set_time_zone($timezone);
return $copy;
}
#######################################################################
=head2 toDatabase
Returns a MySQL Date/Time string in the UTC time zone
=cut
sub toDatabase {
my $self = shift;
my $copy = $self->cloneToUTC;
return $copy->strftime($MYSQL_DATETIME);
}
#######################################################################
=head2 toDatabaseDate
Returns a MySQL Date string. Any time data stored by this object will be
ignored. Is not adjusted for time zone.
=cut
sub toDatabaseDate {
my $self = shift;
my $copy = $self->cloneToUTC;
return $copy->strftime($MYSQL_DATE);
}
#######################################################################
=head2 toDatabaseTime
Returns a MySQL Time string adjusted to UTC. Any date data stored by this object will be
ignored.
=cut
sub toDatabaseTime {
my $self = shift;
my $copy = $self->cloneToUTC;
return $copy->strftime($MYSQL_TIME);
}
#######################################################################
=head2 toIcal
@ -157,11 +263,11 @@ sub toIcal
if ($self->time_zone->is_utc)
{
return $self->strftime('%Y%m%dT%H%M%SZ');
return $self->strftime($ICAL_DATETIME);
}
else
{
return $self->clone->set_time_zone("UTC")->strftime('%Y%m%dT%H%M%SZ');
return $self->clone->set_time_zone("UTC")->strftime($ICAL_DATETIME);
}
}
@ -179,7 +285,7 @@ time zone.
sub toIcalDate
{
return $_[0]->strftime('%Y%m%d');
return $_[0]->strftime($ICAL_DATE);
}
@ -189,8 +295,9 @@ sub toIcalDate
=head2 toMysql
Returns a MySQL Date/Time string in the UTC time zone. This method is deprecated
and will be removed at some point in the future.
This method is deprecated and will be removed in the future.
Returns a MySQL Date/Time string in the UTC time zone.
=cut
@ -206,10 +313,10 @@ sub toMysql
=head2 toMysqlDate
Returns a MySQL Date string. Any time data stored by this object will be
ignored. Is not adjusted for time zone. This method is deprecated
and will be removed at some point in the future.
This method is deprecated and will be removed in the future.
Returns a MySQL Date string. Any time data stored by this object will be
ignored. Is not adjusted for time zone.
=cut
@ -225,10 +332,10 @@ sub toMysqlDate
=head2 toMysqlTime
Returns a MySQL Time string. Any date data stored by this object will be
ignored. Is not adjusted for time zone. This method is deprecated
and will be removed at some point in the future.
This method is deprecated and will be removed in the future.
Returns a MySQL Time string. Any date data stored by this object will be
ignored. Is not adjusted for time zone.
=cut
@ -237,11 +344,164 @@ sub toMysqlTime
return $_[0]->strftime("%H:%M:%S");
}
#######################################################################
=head2 toUserTimeZone
Returns a MySQL Date/Time string in the user's time zone.
=cut
sub toUserTimeZone {
my $self = shift;
my $copy = $self->cloneToUserTimeZone;
return $copy->strftime($MYSQL_DATETIME);
}
#######################################################################
=head2 toUserTimeZoneDate
Returns a MySQL Date string adjusted to the current user's time
zone. Any time data stored by this object will be ignored.
=cut
sub toUserTimeZoneDate {
my $self = shift;
my $copy = $self->cloneToUserTimeZone;
return $copy->strftime($MYSQL_DATE);
}
#######################################################################
=head2 toUserTimeZoneTime
Returns a MySQL Time string adjusted to the current user's time
zone. Any date data stored by this object will be ignored.
=cut
sub toUserTimeZoneTime {
my $self = shift;
my $copy = $self->cloneToUserTimeZone;
return $copy->strftime($MYSQL_TIME);
}
#######################################################################
=head2 session
gets/sets the session variable in the object.
This is going to have to be changed eventually so you don't have to set the session
=cut
sub session {
my $self = shift;
my $session = shift;
if($session) {
$self->{_session} = $session;
}
return $self->{_session};
}
#######################################################################
=head2 webguiDate ( format )
Parses WebGUI dateFormat string and converts to DateTime format
=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.
%t = Time zone name.
%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 webguiDate {
my $self = shift;
my $session = $self->session;
return undef unless ($session);
my $format = shift || "%z %Z";
my $temp;
#---date format preference
$temp = $session->user->profileField('dateFormat') || '%y-%M-%D';
$format =~ s/\%z/$temp/g;
#---time format preference
$temp = $session->user->profileField('timeFormat') || '%H:%n %p';
$format =~ 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",
"t" => "Z",
"O" => "z",
"p" => "P",
"P" => "p",
"s" => "S",
"w" => "A",
"W" => "a",
"y" => "Y",
"Y" => "y"
);
$format =~ s/\%(\w)/\~$1/g;
foreach my $key (keys %conversion) {
my $replacement = $conversion{$key};
$format =~ s/\~$key/\%$replacement/g;
}
#--- %M
my $datestr = $self->strftime($format);
$temp = int($self->month);
$datestr =~ s/\%_varmonth_/$temp/g;
#--- return
return $datestr;
}
#######################################################################
=head2 _splitMysql ( string )
Class method that splits a MySQL Date/Time string into a hash to be passed into