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
231 Sets the date to midnight (at the beginning of the day) GMT
232 Returns the unixtime at midnight.
240 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($self->Unix);
241 $self->Unix(timegm (0,0,0,$mday,$mon,$year,$wday,$yday));
243 return ($self->Unix);
254 return($self->Set(Format => 'unix', Value => time))
262 Takes either an RT::Date object or the date in unixtime format as a string
264 Returns the differnce between $self and that time as a number of seconds
272 if (ref($other) eq 'RT::Date') {
275 return ($self->Unix - $other);
279 # {{{ sub DiffAsString
281 =head2 sub DiffAsString
283 Takes either an RT::Date object or the date in unixtime format as a string
285 Returns the differnce between $self and that time as a number of seconds as
286 as string fit for human consumption
298 if ($self->Unix < 1) {
301 my $diff = $self->Diff($other);
303 return ($self->DurationAsString($diff));
307 # {{{ sub DurationAsString
310 =head2 DurationAsString
312 Takes a number of seconds. returns a string describing that duration
316 sub DurationAsString {
319 my $duration = shift;
321 my ( $negative, $s );
323 $negative = 1 if ( $duration < 0 );
325 $duration = abs($duration);
328 if ( $duration < $MINUTE ) {
330 $time_unit = $self->loc("sec");
332 elsif ( $duration < ( 2 * $HOUR ) ) {
333 $s = int( $duration / $MINUTE );
334 $time_unit = $self->loc("min");
336 elsif ( $duration < ( 2 * $DAY ) ) {
337 $s = int( $duration / $HOUR );
338 $time_unit = $self->loc("hours");
340 elsif ( $duration < ( 2 * $WEEK ) ) {
341 $s = int( $duration / $DAY );
342 $time_unit = $self->loc("days");
344 elsif ( $duration < ( 2 * $MONTH ) ) {
345 $s = int( $duration / $WEEK );
346 $time_unit = $self->loc("weeks");
348 elsif ( $duration < $YEAR ) {
349 $s = int( $duration / $MONTH );
350 $time_unit = $self->loc("months");
353 $s = int( $duration / $YEAR );
354 $time_unit = $self->loc("years");
358 return $self->loc( "[_1] [_2] ago", $s, $time_unit );
361 return $self->loc( "[_1] [_2]", $s, $time_unit );
367 # {{{ sub AgeAsString
369 =head2 sub AgeAsString
373 Returns a string that's the differnce between the time in the object and now
379 return ($self->DiffAsString(time));
387 Returns the object\'s time as a string with the current timezone.
393 return ($self->loc("Not set")) if ($self->Unix <= 0);
395 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
397 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));
403 =head2 GetWeekday DAY
405 Takes an integer day of week and returns a localized string for that day of week
413 return $self->loc('Mon.') if ($dow == 1);
414 return $self->loc('Tue.') if ($dow == 2);
415 return $self->loc('Wed.') if ($dow == 3);
416 return $self->loc('Thu.') if ($dow == 4);
417 return $self->loc('Fri.') if ($dow == 5);
418 return $self->loc('Sat.') if ($dow == 6);
419 return $self->loc('Sun.') if ($dow == 0);
428 Takes an integer month and returns a localized string for that month
436 # We do this rather than an array so that we don't call localize 12x what we need to
437 return $self->loc('Jan.') if ($mon == 0);
438 return $self->loc('Feb.') if ($mon == 1);
439 return $self->loc('Mar.') if ($mon == 2);
440 return $self->loc('Apr.') if ($mon == 3);
441 return $self->loc('May.') if ($mon == 4);
442 return $self->loc('Jun.') if ($mon == 5);
443 return $self->loc('Jul.') if ($mon == 6);
444 return $self->loc('Aug.') if ($mon == 7);
445 return $self->loc('Sep.') if ($mon == 8);
446 return $self->loc('Oct.') if ($mon == 9);
447 return $self->loc('Nov.') if ($mon == 10);
448 return $self->loc('Dec.') if ($mon == 11);
455 =head2 sub AddSeconds
457 Takes a number of seconds as a string
467 $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
469 return ($self->Unix);
480 Adds 24 hours * $DAYS to the current time
487 $self->AddSeconds($days * $DAY);
497 Adds 24 hours to the current time
503 $self->AddSeconds($DAY);
511 =head2 sub Unix [unixtime]
513 Optionally takes a date in unix seconds since the epoch format.
514 Returns the number of seconds since the epoch
521 $self->{'time'} = shift if (@_);
523 return ($self->{'time'});
533 Returns the object's date in ISO format
539 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
541 return ('1970-01-01 00:00:00') if ($self->Unix == -1);
544 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
548 #the month needs incrementing, as gmtime returns 0-11
551 $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
564 Returns the object's date in W3C DTF format
570 my $date = $self->ISO . 'Z';
577 # {{{ sub LocalTimezone
581 Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
582 pull from a 'Timezone' attribute of the CurrentUser
589 return $self->CurrentUser->Timezone
590 if $self->CurrentUser and $self->CurrentUser->can('Timezone');
592 return ($RT::Timezone);
597 eval "require RT::Date_Vendor";
598 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
599 eval "require RT::Date_Local";
600 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});