3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
5 # (Except where explictly superceded by other copyright notices)
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
26 RT::Date - a simple Object Oriented date.
34 RT Date is a simple Date Object designed to be speedy and easy for RT to use
36 The fact that it assumes that a time of 0 means "never" is probably a bug.
40 ok (require RT::Date);
59 use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
72 my $class = ref($proto) || $proto;
74 bless ($self, $class);
75 $self->CurrentUser(@_);
86 takes a param hash with the fields 'Format' and 'Value'
88 if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
90 If $args->{'Format'} is ISO, tries to parse an ISO date.
92 If $args->{'Format'} is 'unknown', require Time::ParseDate and make it figure
93 things out. This is a heavyweight operation that should never be called from
94 within RT's core. But it's really useful for something like the textbox date
95 entry where we let the user do whatever they want.
97 If $args->{'Value'} is 0, assumes you mean never.
102 my $date = RT::Date->new($RT::SystemUser);
103 $date->Set(Format => 'unix', Value => '0');
104 ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
112 my %args = ( Format => 'unix',
116 || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
118 return ( $self->Unix() );
121 if ( $args{'Format'} =~ /^unix$/i ) {
122 $self->Unix( $args{'Value'} );
125 elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
126 $args{'Value'} =~ s!/!-!g;
128 if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
129 || ( $args{'Value'} =~
130 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
131 || ( $args{'Value'} =~
132 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
133 || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
143 #timegm expects month as 0->11
146 #now that we've parsed it, deal with the case where everything
153 #Dateamnip strings aren't in GMT.
154 if ( $args{'Format'} =~ /^datemanip$/i ) {
156 timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
159 #ISO and SQL dates are in GMT
162 timegm( $sec, $min, $hours, $mday, $mon, $year ) );
165 $self->Unix(-1) unless $self->Unix;
172 "Couldn't parse date $args{'Value'} as a $args{'Format'}");
176 elsif ( $args{'Format'} =~ /^unknown$/i ) {
177 require Time::ParseDate;
179 #Convert it to an ISO format string
181 my $date = Time::ParseDate::parsedate($args{'Value'},
182 UK => $RT::DateDayBeforeMonth,
183 PREFER_PAST => $RT::AmbiguousDayInPast,
184 PREFER_FUTURE => !($RT::AmbiguousDayInPast));
186 #This date has now been set to a date in the _local_ timezone.
187 #since ISO dates are known to be in GMT (for RT's purposes);
189 $RT::Logger->debug( "RT::Date used date::parse to make "
193 return ( $self->Set( Format => 'unix', Value => "$date" ) );
196 die "Unknown Date format: " . $args{'Format'} . "\n";
199 return ( $self->Unix() );
204 # {{{ sub SetToMidnight
208 Sets the date to midnight (at the beginning of the day) GMT
209 Returns the unixtime at midnight.
217 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($self->Unix);
218 $self->Unix(timegm (0,0,0,$mday,$mon,$year,$wday,$yday));
220 return ($self->Unix);
231 return($self->Set(Format => 'unix', Value => time))
239 Takes either an RT::Date object or the date in unixtime format as a string
241 Returns the differnce between $self and that time as a number of seconds
249 if (ref($other) eq 'RT::Date') {
252 return ($self->Unix - $other);
256 # {{{ sub DiffAsString
258 =head2 sub DiffAsString
260 Takes either an RT::Date object or the date in unixtime format as a string
262 Returns the differnce between $self and that time as a number of seconds as
263 as string fit for human consumption
275 if ($self->Unix < 1) {
278 my $diff = $self->Diff($other);
280 return ($self->DurationAsString($diff));
284 # {{{ sub DurationAsString
287 =head2 DurationAsString
289 Takes a number of seconds. returns a string describing that duration
293 sub DurationAsString {
296 my $duration = shift;
298 my ( $negative, $s );
300 $negative = 1 if ( $duration < 0 );
302 $duration = abs($duration);
305 if ( $duration < $MINUTE ) {
307 $time_unit = $self->loc("sec");
309 elsif ( $duration < ( 2 * $HOUR ) ) {
310 $s = int( $duration / $MINUTE );
311 $time_unit = $self->loc("min");
313 elsif ( $duration < ( 2 * $DAY ) ) {
314 $s = int( $duration / $HOUR );
315 $time_unit = $self->loc("hours");
317 elsif ( $duration < ( 2 * $WEEK ) ) {
318 $s = int( $duration / $DAY );
319 $time_unit = $self->loc("days");
321 elsif ( $duration < ( 2 * $MONTH ) ) {
322 $s = int( $duration / $WEEK );
323 $time_unit = $self->loc("weeks");
325 elsif ( $duration < $YEAR ) {
326 $s = int( $duration / $MONTH );
327 $time_unit = $self->loc("months");
330 $s = int( $duration / $YEAR );
331 $time_unit = $self->loc("years");
333 if (0) { # For now, never display the "AGO" # $negative) {
334 return $self->loc( "[_1] [_2] ago", $s, $time_unit );
337 return $self->loc( "[_1] [_2]", $s, $time_unit );
343 # {{{ sub AgeAsString
345 =head2 sub AgeAsString
349 Returns a string that's the differnce between the time in the object and now
355 return ($self->DiffAsString(time));
363 Returns the object\'s time as a string with the current timezone.
369 return ($self->loc("Not set")) if ($self->Unix <= 0);
371 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
373 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));
378 =head2 GetWeekday DAY
380 Takes an integer day of week and returns a localized string for that day of week
388 return $self->loc('Mon.') if ($dow == 1);
389 return $self->loc('Tue.') if ($dow == 2);
390 return $self->loc('Wed.') if ($dow == 3);
391 return $self->loc('Thu.') if ($dow == 4);
392 return $self->loc('Fri.') if ($dow == 5);
393 return $self->loc('Sat.') if ($dow == 6);
394 return $self->loc('Sun.') if ($dow == 0);
402 Takes an integer month and returns a localized string for that month
410 # We do this rather than an array so that we don't call localize 12x what we need to
411 return $self->loc('Jan.') if ($mon == 0);
412 return $self->loc('Feb.') if ($mon == 1);
413 return $self->loc('Mar.') if ($mon == 2);
414 return $self->loc('Apr.') if ($mon == 3);
415 return $self->loc('May.') if ($mon == 4);
416 return $self->loc('Jun.') if ($mon == 5);
417 return $self->loc('Jul.') if ($mon == 6);
418 return $self->loc('Aug.') if ($mon == 7);
419 return $self->loc('Sep.') if ($mon == 8);
420 return $self->loc('Oct.') if ($mon == 9);
421 return $self->loc('Nov.') if ($mon == 10);
422 return $self->loc('Dec.') if ($mon == 11);
429 =head2 sub AddSeconds
431 Takes a number of seconds as a string
441 $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
443 return ($self->Unix);
454 Adds 24 hours * $DAYS to the current time
461 $self->AddSeconds($days * $DAY);
471 Adds 24 hours to the current time
477 $self->AddSeconds($DAY);
485 =head2 sub Unix [unixtime]
487 Optionally takes a date in unix seconds since the epoch format.
488 Returns the number of seconds since the epoch
495 $self->{'time'} = shift if (@_);
497 return ($self->{'time'});
507 Returns the object's date in ISO format
513 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
515 return ('1970-01-01 00:00:00') if ($self->Unix == -1);
518 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
522 #the month needs incrementing, as gmtime returns 0-11
525 $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
533 # {{{ sub LocalTimezone
536 Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
537 pull from a 'Timezone' attribute of the CurrentUser
544 return $self->CurrentUser->Timezone
545 if $self->CurrentUser and $self->CurrentUser->can('Timezone');
547 return ($RT::Timezone);
552 eval "require RT::Date_Vendor";
553 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
554 eval "require RT::Date_Local";
555 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});