1 # {{{ BEGIN BPS TAGGED BLOCK
5 # This software is Copyright (c) 1996-2004 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
48 RT::Date - a simple Object Oriented date.
56 RT Date is a simple Date Object designed to be speedy and easy for RT to use
58 The fact that it assumes that a time of 0 means "never" is probably a bug.
62 ok (require RT::Date);
81 use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
94 my $class = ref($proto) || $proto;
96 bless ($self, $class);
97 $self->CurrentUser(@_);
108 takes a param hash with the fields 'Format' and 'Value'
110 if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
112 If $args->{'Format'} is ISO, tries to parse an ISO date.
114 If $args->{'Format'} is 'unknown', require Time::ParseDate and make it figure
115 things out. This is a heavyweight operation that should never be called from
116 within RT's core. But it's really useful for something like the textbox date
117 entry where we let the user do whatever they want.
119 If $args->{'Value'} is 0, assumes you mean never.
124 my $date = RT::Date->new($RT::SystemUser);
125 $date->Set(Format => 'unix', Value => '0');
126 ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
134 my %args = ( Format => 'unix',
138 || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
140 return ( $self->Unix() );
143 if ( $args{'Format'} =~ /^unix$/i ) {
144 $self->Unix( $args{'Value'} );
147 elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
148 $args{'Value'} =~ s!/!-!g;
150 if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
151 || ( $args{'Value'} =~
152 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
153 || ( $args{'Value'} =~
154 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
155 || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
165 #timegm expects month as 0->11
168 #now that we've parsed it, deal with the case where everything
175 #Dateamnip strings aren't in GMT.
176 if ( $args{'Format'} =~ /^datemanip$/i ) {
178 timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
181 #ISO and SQL dates are in GMT
184 timegm( $sec, $min, $hours, $mday, $mon, $year ) );
187 $self->Unix(-1) unless $self->Unix;
194 "Couldn't parse date $args{'Value'} as a $args{'Format'}");
198 elsif ( $args{'Format'} =~ /^unknown$/i ) {
199 require Time::ParseDate;
201 #Convert it to an ISO format string
203 my $date = Time::ParseDate::parsedate($args{'Value'},
204 UK => $RT::DateDayBeforeMonth,
205 PREFER_PAST => $RT::AmbiguousDayInPast,
206 PREFER_FUTURE => !($RT::AmbiguousDayInPast));
208 #This date has now been set to a date in the _local_ timezone.
209 #since ISO dates are known to be in GMT (for RT's purposes);
211 $RT::Logger->debug( "RT::Date used date::parse to make "
215 return ( $self->Set( Format => 'unix', Value => "$date" ) );
218 die "Unknown Date format: " . $args{'Format'} . "\n";
221 return ( $self->Unix() );
226 # {{{ sub SetToMidnight
230 Sets the date to midnight (at the beginning of the day) GMT
231 Returns the unixtime at midnight.
239 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($self->Unix);
240 $self->Unix(timegm (0,0,0,$mday,$mon,$year,$wday,$yday));
242 return ($self->Unix);
253 return($self->Set(Format => 'unix', Value => time))
261 Takes either an RT::Date object or the date in unixtime format as a string
263 Returns the differnce between $self and that time as a number of seconds
271 if (ref($other) eq 'RT::Date') {
274 return ($self->Unix - $other);
278 # {{{ sub DiffAsString
280 =head2 sub DiffAsString
282 Takes either an RT::Date object or the date in unixtime format as a string
284 Returns the differnce between $self and that time as a number of seconds as
285 as string fit for human consumption
297 if ($self->Unix < 1) {
300 my $diff = $self->Diff($other);
302 return ($self->DurationAsString($diff));
306 # {{{ sub DurationAsString
309 =head2 DurationAsString
311 Takes a number of seconds. returns a string describing that duration
315 sub DurationAsString {
318 my $duration = shift;
320 my ( $negative, $s );
322 $negative = 1 if ( $duration < 0 );
324 $duration = abs($duration);
327 if ( $duration < $MINUTE ) {
329 $time_unit = $self->loc("sec");
331 elsif ( $duration < ( 2 * $HOUR ) ) {
332 $s = int( $duration / $MINUTE );
333 $time_unit = $self->loc("min");
335 elsif ( $duration < ( 2 * $DAY ) ) {
336 $s = int( $duration / $HOUR );
337 $time_unit = $self->loc("hours");
339 elsif ( $duration < ( 2 * $WEEK ) ) {
340 $s = int( $duration / $DAY );
341 $time_unit = $self->loc("days");
343 elsif ( $duration < ( 2 * $MONTH ) ) {
344 $s = int( $duration / $WEEK );
345 $time_unit = $self->loc("weeks");
347 elsif ( $duration < $YEAR ) {
348 $s = int( $duration / $MONTH );
349 $time_unit = $self->loc("months");
352 $s = int( $duration / $YEAR );
353 $time_unit = $self->loc("years");
357 return $self->loc( "[_1] [_2] ago", $s, $time_unit );
360 return $self->loc( "[_1] [_2]", $s, $time_unit );
366 # {{{ sub AgeAsString
368 =head2 sub AgeAsString
372 Returns a string that's the differnce between the time in the object and now
378 return ($self->DiffAsString(time));
386 Returns the object\'s time as a string with the current timezone.
392 return ($self->loc("Not set")) if ($self->Unix <= 0);
394 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
396 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));
401 =head2 GetWeekday DAY
403 Takes an integer day of week and returns a localized string for that day of week
411 return $self->loc('Mon.') if ($dow == 1);
412 return $self->loc('Tue.') if ($dow == 2);
413 return $self->loc('Wed.') if ($dow == 3);
414 return $self->loc('Thu.') if ($dow == 4);
415 return $self->loc('Fri.') if ($dow == 5);
416 return $self->loc('Sat.') if ($dow == 6);
417 return $self->loc('Sun.') if ($dow == 0);
425 Takes an integer month and returns a localized string for that month
433 # We do this rather than an array so that we don't call localize 12x what we need to
434 return $self->loc('Jan.') if ($mon == 0);
435 return $self->loc('Feb.') if ($mon == 1);
436 return $self->loc('Mar.') if ($mon == 2);
437 return $self->loc('Apr.') if ($mon == 3);
438 return $self->loc('May.') if ($mon == 4);
439 return $self->loc('Jun.') if ($mon == 5);
440 return $self->loc('Jul.') if ($mon == 6);
441 return $self->loc('Aug.') if ($mon == 7);
442 return $self->loc('Sep.') if ($mon == 8);
443 return $self->loc('Oct.') if ($mon == 9);
444 return $self->loc('Nov.') if ($mon == 10);
445 return $self->loc('Dec.') if ($mon == 11);
452 =head2 sub AddSeconds
454 Takes a number of seconds as a string
464 $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
466 return ($self->Unix);
477 Adds 24 hours * $DAYS to the current time
484 $self->AddSeconds($days * $DAY);
494 Adds 24 hours to the current time
500 $self->AddSeconds($DAY);
508 =head2 sub Unix [unixtime]
510 Optionally takes a date in unix seconds since the epoch format.
511 Returns the number of seconds since the epoch
518 $self->{'time'} = shift if (@_);
520 return ($self->{'time'});
530 Returns the object's date in ISO format
536 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
538 return ('1970-01-01 00:00:00') if ($self->Unix == -1);
541 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
545 #the month needs incrementing, as gmtime returns 0-11
548 $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
561 Returns the object's date in W3C DTF format
567 my $date = $self->ISO . 'Z';
574 # {{{ sub LocalTimezone
577 Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
578 pull from a 'Timezone' attribute of the CurrentUser
585 return $self->CurrentUser->Timezone
586 if $self->CurrentUser and $self->CurrentUser->can('Timezone');
588 return ($RT::Timezone);
593 eval "require RT::Date_Vendor";
594 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
595 eval "require RT::Date_Local";
596 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});