diff options
Diffstat (limited to 'rt/lib/RT/Date.pm')
-rw-r--r-- | rt/lib/RT/Date.pm | 250 |
1 files changed, 169 insertions, 81 deletions
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm index 4405b07..c572b75 100644 --- a/rt/lib/RT/Date.pm +++ b/rt/lib/RT/Date.pm @@ -56,7 +56,7 @@ =head1 DESCRIPTION -RT Date is a simple Date Object designed to be speedy and easy for RT to use +RT Date is a simple Date Object designed to be speedy and easy for RT to use. The fact that it assumes that a time of 0 means "never" is probably a bug. @@ -166,10 +166,21 @@ sub Set { return $self->Unix(0) unless $args{'Value'} && $args{'Value'} =~ /\S/; - if ( $args{'Format'} =~ /^unix$/i ) { + my $format = lc $args{'Format'}; + + if ( $format eq 'unix' ) { return $self->Unix( $args{'Value'} ); } - elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) { + elsif ( + ($format eq 'sql' || $format eq 'iso') + && $args{'Value'} =~ /^(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ + ) { + local $@; + my $u = eval { Time::Local::timegm($6, $5, $4, $3, $2-1, $1) } || 0; + $RT::Logger->warning("Invalid date $args{'Value'}: $@") if $@ && !$u; + return $self->Unix( $u > 0 ? $u : 0 ); + } + elsif ( $format =~ /^(sql|datemanip|iso)$/ ) { $args{'Value'} =~ s!/!-!g; if ( ( $args{'Value'} =~ /^(\d{4})?(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ ) @@ -201,7 +212,7 @@ sub Set { return $self->Unix(0); } } - elsif ( $args{'Format'} =~ /^unknown$/i ) { + elsif ( $format eq 'unknown' ) { require Time::ParseDate; # the module supports only legacy timezones like PDT or EST... # so we parse date as GMT and later apply offset, this only @@ -230,7 +241,7 @@ sub Set { "RT::Date used Time::ParseDate to make '$args{'Value'}' $date\n" ); - return $self->Set( Format => 'unix', Value => $date); + return $self->Unix($date || 0); } else { $RT::Logger->error( @@ -365,56 +376,105 @@ sub DiffAsString { Takes a number of seconds. Returns a localized string describing that duration. +Takes optional named arguments: + +=over 4 + +=item * Show + +How many elements to show, how precise it should be. Default is 1, +most vague variant. + +=item * Short + +Turn on short notation with one character units, for example +"3M 2d 1m 10s". + +=back + =cut sub DurationAsString { my $self = shift; my $duration = int shift; + my %args = ( Show => 1, Short => 0, @_ ); - my ( $negative, $s, $time_unit ); + unless ( $duration ) { + return $args{Short}? $self->loc("0s") : $self->loc("0 seconds"); + } + + my $negative; $negative = 1 if $duration < 0; $duration = abs $duration; - if ( $duration < $MINUTE ) { - $s = $duration; - $time_unit = $self->loc("sec"); - } - elsif ( $duration < ( 2 * $HOUR ) ) { - $s = int( $duration / $MINUTE + 0.5 ); - $time_unit = $self->loc("min"); - } - elsif ( $duration < ( 2 * $DAY ) ) { - $s = int( $duration / $HOUR + 0.5 ); - $time_unit = $self->loc("hours"); - } - elsif ( $duration < ( 2 * $WEEK ) ) { - $s = int( $duration / $DAY + 0.5 ); - $time_unit = $self->loc("days"); - } - elsif ( $duration < ( 2 * $MONTH ) ) { - $s = int( $duration / $WEEK + 0.5 ); - $time_unit = $self->loc("weeks"); - } - elsif ( $duration < $YEAR ) { - $s = int( $duration / $MONTH + 0.5 ); - $time_unit = $self->loc("months"); - } - else { - $s = int( $duration / $YEAR + 0.5 ); - $time_unit = $self->loc("years"); + my @res; + + my $coef = 2; + my $i = 0; + while ( $duration > 0 && ++$i <= $args{'Show'} ) { + + my ($locstr, $unit); + if ( $duration < $MINUTE ) { + $locstr = $args{Short} + ? '[_1]s' # loc + : '[quant,_1,second,seconds]'; # loc + $unit = 1; + } + elsif ( $duration < ( $coef * $HOUR ) ) { + $locstr = $args{Short} + ? '[_1]m' # loc + : '[quant,_1,minute,minutes]'; # loc + $unit = $MINUTE; + } + elsif ( $duration < ( $coef * $DAY ) ) { + $locstr = $args{Short} + ? '[_1]h' # loc + : '[quant,_1,hour,hours]'; # loc + $unit = $HOUR; + } + elsif ( $duration < ( $coef * $WEEK ) ) { + $locstr = $args{Short} + ? '[_1]d' # loc + : '[quant,_1,day,days]'; # loc + $unit = $DAY; + } + elsif ( $duration < ( $coef * $MONTH ) ) { + $locstr = $args{Short} + ? '[_1]W' # loc + : '[quant,_1,week,weeks]'; # loc + $unit = $WEEK; + } + elsif ( $duration < $YEAR ) { + $locstr = $args{Short} + ? '[_1]M' # loc + : '[quant,_1,month,months]'; # loc + $unit = $MONTH; + } + else { + $locstr = $args{Short} + ? '[_1]Y' # loc + : '[quant,_1,year,years]'; # loc + $unit = $YEAR; + } + my $value = int( $duration / $unit + ($i < $args{'Show'}? 0 : 0.5) ); + $duration -= int( $value * $unit ); + + push @res, $self->loc($locstr, $value); + + $coef = 1; } if ( $negative ) { - return $self->loc( "[_1] [_2] ago", $s, $time_unit ); + return $self->loc( "[_1] ago", join ' ', @res ); } else { - return $self->loc( "[_1] [_2]", $s, $time_unit ); + return join ' ', @res; } } =head2 AgeAsString -Takes nothing. Returns a string that's the differnce between the +Takes nothing. Returns a string that's the difference between the time in the object and now. =cut @@ -425,10 +485,10 @@ sub AgeAsString { return $_[0]->DiffAsString } =head2 AsString -Returns the object's time as a localized string with curent user's prefered +Returns the object's time as a localized string with curent user's preferred format and timezone. -If the current user didn't choose prefered format then system wide setting is +If the current user didn't choose preferred format then system wide setting is used or L</DefaultFormat> if the latter is not specified. See config option C<DateTimeFormat>. @@ -438,7 +498,7 @@ sub AsString { my $self = shift; my %args = (@_); - return $self->loc("Not set") unless $self->Unix > 0; + return $self->loc("Not set") unless $self->IsSet; my $format = RT->Config->Get( 'DateTimeFormat', $self->CurrentUser ) || 'DefaultFormat'; $format = { Format => $format } unless ref $format; @@ -555,13 +615,21 @@ Returns the number of seconds since the epoch sub Unix { my $self = shift; - $self->{'time'} = int(shift || 0) if @_; + + if (@_) { + my $time = int(shift || 0); + if ($time < 0) { + RT->Logger->notice("Passed a unix time less than 0, forcing to 0: [$time]"); + $time = 0; + } + $self->{'time'} = int $time; + } return $self->{'time'}; } =head2 DateTime -Alias for L</Get> method. Arguments C<Date> and <Time> +Alias for L</Get> method. Arguments C<Date> and C<Time> are fixed to true values, other arguments could be used as described in L</Get>. @@ -601,7 +669,7 @@ sub Time { =head2 Get -Returnsa a formatted and localized string that represets time of +Returns a formatted and localized string that represents the time of the current object. @@ -639,7 +707,7 @@ Each method takes several arguments: Formatters may also add own arguments to the list, for example in RFC2822 format day of time in output is optional so it -understand boolean argument C<DayOfTime>. +understands boolean argument C<DayOfTime>. =head3 Formatters @@ -687,7 +755,7 @@ sub DefaultFormat $self->Localtime($args{'Timezone'}); $wday = $self->GetWeekday($wday); $mon = $self->GetMonth($mon); - ($mday, $hour, $min, $sec) = map { sprintf "%02d", $_ } ($mday, $hour, $min, $sec); + $_ = sprintf "%02d", $_ foreach $mday, $hour, $min, $sec; if( $args{'Date'} && !$args{'Time'} ) { return $self->loc('[_1] [_2] [_3] [_4]', @@ -734,8 +802,8 @@ sub LocaleObj { Returns date and time as string, with user localization. Supports arguments: C<DateFormat> and C<TimeFormat> which may contains date and -time format as specified in L<DateTime::Locale> (default to full_date_format and -medium_time_format), C<AbbrDay> and C<AbbrMonth> which may be set to 0 if +time format as specified in L<DateTime::Locale> (default to C<date_format_full> and +C<time_format_medium>), C<AbbrDay> and C<AbbrMonth> which may be set to 0 if you want full Day/Month names instead of abbreviated ones. =cut @@ -796,11 +864,11 @@ sub LocalizedDateTime =head3 ISO Returns the object's date in ISO format C<YYYY-MM-DD mm:hh:ss>. -ISO format is locale independant, but adding timezone offset info +ISO format is locale-independent, but adding timezone offset info is not implemented yet. Supports arguments: C<Timezone>, C<Date>, C<Time> and C<Seconds>. -See </Output formatters> for description of arguments. +See L</Output formatters> for description of arguments. =cut @@ -822,7 +890,7 @@ sub ISO { my $res = ''; $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday) if $args{'Date'}; $res .= sprintf(' %02d:%02d', $hour, $min) if $args{'Time'}; - $res .= sprintf(':%02d', $sec, $min) if $args{'Time'} && $args{'Seconds'}; + $res .= sprintf(':%02d', $sec) if $args{'Time'} && $args{'Seconds'}; $res =~ s/^\s+//; return $res; @@ -833,12 +901,12 @@ sub ISO { Returns the object's date and time in W3C date time format (L<http://www.w3.org/TR/NOTE-datetime>). -Format is locale independand and is close enought to ISO, but +Format is locale-independent and is close enough to ISO, but note that date part is B<not optional> and output string has timezone offset mark in C<[+-]hh:mm> format. Supports arguments: C<Timezone>, C<Time> and C<Seconds>. -See </Output formatters> for description of arguments. +See L</Output formatters> for description of arguments. =cut @@ -862,7 +930,7 @@ sub W3CDTF { $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday); if ( $args{'Time'} ) { $res .= sprintf('T%02d:%02d', $hour, $min); - $res .= sprintf(':%02d', $sec, $min) if $args{'Seconds'}; + $res .= sprintf(':%02d', $sec) if $args{'Seconds'}; if ( $offset ) { $res .= sprintf "%s%02d:%02d", $self->_SplitOffset( $offset ); } else { @@ -878,11 +946,11 @@ sub W3CDTF { Returns the object's date and time in RFC2822 format, for example C<Sun, 06 Nov 1994 08:49:37 +0000>. -Format is locale independand as required by RFC. Time +Format is locale-independent as required by RFC. Time part always has timezone offset in digits with sign prefix. Supports arguments: C<Timezone>, C<Date>, C<Time>, C<DayOfWeek> -and C<Seconds>. See </Output formatters> for description of +and C<Seconds>. See L</Output formatters> for description of arguments. =cut @@ -920,8 +988,8 @@ Returns the object's date and time in RFC2616 (HTTP/1.1) format, for example C<Sun, 06 Nov 1994 08:49:37 GMT>. While the RFC describes version 1.1 of HTTP, but the same form date can be used in version 1.0. -Format is fixed length, locale independand and always represented in GMT -what makes it quite useless for users, but any date in HTTP transfers +Format is fixed-length, locale-independent and always represented in GMT +which makes it quite useless for users, but any date in HTTP transfers must be presented using this format. HTTP-date = rfc1123 | ... @@ -936,7 +1004,7 @@ must be presented using this format. Supports arguments: C<Date> and C<Time>, but you should use them only for some personal reasons, RFC2616 doesn't define any optional parts. -See </Output formatters> for description of arguments. +See L</Output formatters> for description of arguments. =cut @@ -955,10 +1023,12 @@ sub RFC2616 { =head4 iCal -Returns the object's date and time in iCalendar format, +Returns the object's date and time in iCalendar format. +If only date requested then user's timezone is used, otherwise +it's UTC. Supports arguments: C<Date> and C<Time>. -See </Output formatters> for description of arguments. +See L</Output formatters> for description of arguments. =cut @@ -1009,11 +1079,19 @@ argument unix C<$time>, default value is the current unix time. Returns object's date and time in the format provided by perl's builtin functions C<localtime> and C<gmtime> with two exceptions: -1) "Year" is a four-digit year, rather than "years since 1900" +=over + +=item 1) + +"Year" is a four-digit year, rather than "years since 1900" + +=item 2) -2) The last element of the array returned is C<offset>, which +The last element of the array returned is C<offset>, which represents timezone offset against C<UTC> in seconds. +=back + =cut sub Localtime @@ -1035,7 +1113,7 @@ sub Localtime POSIX::tzset(); @local = localtime($unix); } - POSIX::tzset(); # return back previouse value + POSIX::tzset(); # return back previous value } $local[5] += 1900; # change year to 4+ digits format my $offset = Time::Local::timegm_nocheck(@local) - $unix; @@ -1047,16 +1125,16 @@ sub Localtime Takes argument C<$context>, which determines whether we should treat C<@time> as "user local", "system" or "UTC" time. -C<@time> is array returned by L<Localtime> functions. Only first +C<@time> is array returned by L</Localtime> functions. Only first six elements are mandatory - $sec, $min, $hour, $mday, $mon and $year. You may pass $wday, $yday and $isdst, these are ignored. If you pass C<$offset> as ninth argument, it's used instead of C<$context>. It's done such way as code -C<$self->Timelocal('utc', $self->Localtime('server'))> doesn't -makes much sense and most probably would produce unexpected -result, so the method ignore 'utc' context and uses offset -returned by L<Localtime> method. +C<< $self->Timelocal('utc', $self->Localtime('server')) >> doesn't +make much sense and most probably would produce unexpected +results, so the method ignores 'utc' context and uses the offset +returned by the L</Localtime> method. =cut @@ -1087,40 +1165,36 @@ sub Timelocal { =head3 Timezone $context -Returns the timezone name. - -Takes one argument, C<$context> argument which could be C<user>, C<server> or C<utc>. +Returns the timezone name for the specified context. C<$context> +should be one of these values: =over -=item user +=item C<user> -Default value is C<user> that mean it returns current user's Timezone value. +The current user's Timezone value will be returned. -=item server +=item C<server> -If context is C<server> it returns value of the C<Timezone> RT config option. - -=item utc - -If both server's and user's timezone names are undefined returns 'UTC'. +The value of the C<Timezone> RT config option will be returned. =back +For any other value of C<$context>, or if the specified context has no +defined timezone, C<UTC> is returned. + =cut sub Timezone { my $self = shift; if (@_ == 0) { - Carp::carp "RT::Date->Timezone is a setter only"; + Carp::carp 'RT::Date->Timezone requires a context argument'; return undef; } my $context = lc(shift); - $context = 'utc' unless $context =~ /^(?:utc|server|user)$/i; - my $tz; if( $context eq 'user' ) { $tz = $self->CurrentUser->UserObj->Timezone; @@ -1134,6 +1208,20 @@ sub Timezone { return $tz; } +=head3 IsSet + +Returns true if this Date is set in the database, otherwise returns a false value. + +This avoids needing to compare to 1970-01-01 in any of your code. + +=cut + +sub IsSet { + my $self = shift; + return $self->Unix ? 1 : 0; + +} + RT::Base->_ImportOverlays(); |