1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 # CONTRIBUTION SUBMISSION POLICY:
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
45 # END BPS TAGGED BLOCK }}}
49 RT::Date - a simple Object Oriented date.
57 RT Date is a simple Date Object designed to be speedy and easy for RT to use
59 The fact that it assumes that a time of 0 means "never" is probably a bug.
63 ok (require RT::Date);
82 use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
95 my $class = ref($proto) || $proto;
97 bless ($self, $class);
98 $self->CurrentUser(@_);
109 takes a param hash with the fields 'Format' and 'Value'
111 if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
113 If $args->{'Format'} is ISO, tries to parse an ISO date.
115 If $args->{'Format'} is 'unknown', require Time::ParseDate and make it figure
116 things out. This is a heavyweight operation that should never be called from
117 within RT's core. But it's really useful for something like the textbox date
118 entry where we let the user do whatever they want.
120 If $args->{'Value'} is 0, assumes you mean never.
125 my $date = RT::Date->new($RT::SystemUser);
126 $date->Set(Format => 'unix', Value => '0');
127 ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
135 my %args = ( Format => 'unix',
139 || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
141 return ( $self->Unix() );
144 if ( $args{'Format'} =~ /^unix$/i ) {
145 $self->Unix( $args{'Value'} );
148 elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
149 $args{'Value'} =~ s!/!-!g;
151 if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
152 || ( $args{'Value'} =~
153 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
154 || ( $args{'Value'} =~
155 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
156 || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
166 #timegm expects month as 0->11
169 #now that we've parsed it, deal with the case where everything
176 #Dateamnip strings aren't in GMT.
177 if ( $args{'Format'} =~ /^datemanip$/i ) {
179 timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
182 #ISO and SQL dates are in GMT
185 timegm( $sec, $min, $hours, $mday, $mon, $year ) );
188 $self->Unix(-1) unless $self->Unix;
195 "Couldn't parse date $args{'Value'} as a $args{'Format'}");
199 elsif ( $args{'Format'} =~ /^unknown$/i ) {
200 require Time::ParseDate;
202 #Convert it to an ISO format string
204 my $date = Time::ParseDate::parsedate($args{'Value'},
205 UK => $RT::DateDayBeforeMonth,
206 PREFER_PAST => $RT::AmbiguousDayInPast,
207 PREFER_FUTURE => !($RT::AmbiguousDayInPast));
209 #This date has now been set to a date in the _local_ timezone.
210 #since ISO dates are known to be in GMT (for RT's purposes);
212 $RT::Logger->debug( "RT::Date used date::parse to make "
216 return ( $self->Set( Format => 'unix', Value => "$date" ) );
219 die "Unknown Date format: " . $args{'Format'} . "\n";
222 return ( $self->Unix() );
227 # {{{ sub SetToMidnight
229 =head2 SetToMidnight [Timezone => 'utc']
231 Sets the date to midnight (at the beginning of the day).
232 Returns the unixtime at midnight.
238 =item Timezone - Timezone context C<server> or C<UTC>
244 my %args = ( Timezone => 'UTC', @_ );
245 if ( lc $args{'Timezone'} eq 'server' ) {
246 $self->Unix( Time::Local::timelocal( 0,0,0,(localtime $self->Unix)[3..7] ) );
248 $self->Unix( Time::Local::timegm( 0,0,0,(gmtime $self->Unix)[3..7] ) );
250 return ($self->Unix);
259 return($self->Set(Format => 'unix', Value => time))
267 Takes either an RT::Date object or the date in unixtime format as a string
269 Returns the differnce between $self and that time as a number of seconds
277 if (ref($other) eq 'RT::Date') {
280 return ($self->Unix - $other);
284 # {{{ sub DiffAsString
286 =head2 sub DiffAsString
288 Takes either an RT::Date object or the date in unixtime format as a string
290 Returns the differnce between $self and that time as a number of seconds as
291 as string fit for human consumption
303 if ($self->Unix < 1) {
306 my $diff = $self->Diff($other);
308 return ($self->DurationAsString($diff));
312 # {{{ sub DurationAsString
315 =head2 DurationAsString
317 Takes a number of seconds. returns a string describing that duration
321 sub DurationAsString {
324 my $duration = shift;
326 my ( $negative, $s );
328 $negative = 1 if ( $duration < 0 );
330 $duration = abs($duration);
333 if ( $duration < $MINUTE ) {
335 $time_unit = $self->loc("sec");
337 elsif ( $duration < ( 2 * $HOUR ) ) {
338 $s = int( $duration / $MINUTE );
339 $time_unit = $self->loc("min");
341 elsif ( $duration < ( 2 * $DAY ) ) {
342 $s = int( $duration / $HOUR );
343 $time_unit = $self->loc("hours");
345 elsif ( $duration < ( 2 * $WEEK ) ) {
346 $s = int( $duration / $DAY );
347 $time_unit = $self->loc("days");
349 elsif ( $duration < ( 2 * $MONTH ) ) {
350 $s = int( $duration / $WEEK );
351 $time_unit = $self->loc("weeks");
353 elsif ( $duration < $YEAR ) {
354 $s = int( $duration / $MONTH );
355 $time_unit = $self->loc("months");
358 $s = int( $duration / $YEAR );
359 $time_unit = $self->loc("years");
363 return $self->loc( "[_1] [_2] ago", $s, $time_unit );
366 return $self->loc( "[_1] [_2]", $s, $time_unit );
372 # {{{ sub AgeAsString
374 =head2 sub AgeAsString
378 Returns a string that's the differnce between the time in the object and now
384 return ($self->DiffAsString(time));
392 Returns the object\'s time as a string with the current timezone.
398 return ($self->loc("Not set")) if ($self->Unix <= 0);
400 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
402 return $self->loc("[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]", $self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900));
408 =head2 GetWeekday DAY
410 Takes an integer day of week and returns a localized string for that day of week
418 return $self->loc('Mon.') if ($dow == 1);
419 return $self->loc('Tue.') if ($dow == 2);
420 return $self->loc('Wed.') if ($dow == 3);
421 return $self->loc('Thu.') if ($dow == 4);
422 return $self->loc('Fri.') if ($dow == 5);
423 return $self->loc('Sat.') if ($dow == 6);
424 return $self->loc('Sun.') if ($dow == 0);
433 Takes an integer month and returns a localized string for that month
441 # We do this rather than an array so that we don't call localize 12x what we need to
442 return $self->loc('Jan.') if ($mon == 0);
443 return $self->loc('Feb.') if ($mon == 1);
444 return $self->loc('Mar.') if ($mon == 2);
445 return $self->loc('Apr.') if ($mon == 3);
446 return $self->loc('May.') if ($mon == 4);
447 return $self->loc('Jun.') if ($mon == 5);
448 return $self->loc('Jul.') if ($mon == 6);
449 return $self->loc('Aug.') if ($mon == 7);
450 return $self->loc('Sep.') if ($mon == 8);
451 return $self->loc('Oct.') if ($mon == 9);
452 return $self->loc('Nov.') if ($mon == 10);
453 return $self->loc('Dec.') if ($mon == 11);
460 =head2 sub AddSeconds
462 Takes a number of seconds as a string
472 $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
474 return ($self->Unix);
485 Adds 24 hours * $DAYS to the current time
492 $self->AddSeconds($days * $DAY);
502 Adds 24 hours to the current time
508 $self->AddSeconds($DAY);
516 =head2 sub Unix [unixtime]
518 Optionally takes a date in unix seconds since the epoch format.
519 Returns the number of seconds since the epoch
526 $self->{'time'} = shift if (@_);
528 return ($self->{'time'});
538 Returns the object's date in ISO format
544 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
546 return ('1970-01-01 00:00:00') if ($self->Unix == -1);
549 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
553 #the month needs incrementing, as gmtime returns 0-11
556 $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
569 Returns the object's date in W3C DTF format
575 my $date = $self->ISO . 'Z';
582 # {{{ sub LocalTimezone
586 Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
587 pull from a 'Timezone' attribute of the CurrentUser
594 return $self->CurrentUser->Timezone
595 if $self->CurrentUser and $self->CurrentUser->can('Timezone');
597 return ($RT::Timezone);
602 eval "require RT::Date_Vendor";
603 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
604 eval "require RT::Date_Local";
605 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});