1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/copyleft/gpl.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
50 RT::Date - a simple Object Oriented date.
58 RT Date is a simple Date Object designed to be speedy and easy for RT to use
60 The fact that it assumes that a time of 0 means "never" is probably a bug.
64 ok (require RT::Date);
83 use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
96 my $class = ref($proto) || $proto;
98 bless ($self, $class);
99 $self->CurrentUser(@_);
110 takes a param hash with the fields 'Format' and 'Value'
112 if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
114 If $args->{'Format'} is ISO, tries to parse an ISO date.
116 If $args->{'Format'} is 'unknown', require Time::ParseDate and make it figure
117 things out. This is a heavyweight operation that should never be called from
118 within RT's core. But it's really useful for something like the textbox date
119 entry where we let the user do whatever they want.
121 If $args->{'Value'} is 0, assumes you mean never.
126 my $date = RT::Date->new($RT::SystemUser);
127 $date->Set(Format => 'unix', Value => '0');
128 ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
136 my %args = ( Format => 'unix',
140 || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
142 return ( $self->Unix() );
145 if ( $args{'Format'} =~ /^unix$/i ) {
146 $self->Unix( $args{'Value'} );
149 elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
150 $args{'Value'} =~ s!/!-!g;
152 if (( $args{'Value'} =~ /^(\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)$/ )
155 || ( $args{'Value'} =~
156 /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
157 || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
167 #timegm expects month as 0->11
170 #now that we've parsed it, deal with the case where everything
177 #Dateamnip strings aren't in GMT.
178 if ( $args{'Format'} =~ /^datemanip$/i ) {
180 timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
183 #ISO and SQL dates are in GMT
186 timegm( $sec, $min, $hours, $mday, $mon, $year ) );
189 $self->Unix(-1) unless $self->Unix;
196 "Couldn't parse date $args{'Value'} as a $args{'Format'}");
200 elsif ( $args{'Format'} =~ /^unknown$/i ) {
201 require Time::ParseDate;
203 #Convert it to an ISO format string
205 my $date = Time::ParseDate::parsedate($args{'Value'},
206 UK => $RT::DateDayBeforeMonth,
207 PREFER_PAST => $RT::AmbiguousDayInPast,
208 PREFER_FUTURE => !($RT::AmbiguousDayInPast));
210 #This date has now been set to a date in the _local_ timezone.
211 #since ISO dates are known to be in GMT (for RT's purposes);
213 $RT::Logger->debug( "RT::Date used date::parse to make "
217 return ( $self->Set( Format => 'unix', Value => "$date" ) );
220 die "Unknown Date format: " . $args{'Format'} . "\n";
223 return ( $self->Unix() );
228 # {{{ sub SetToMidnight
230 =head2 SetToMidnight [Timezone => 'utc']
232 Sets the date to midnight (at the beginning of the day).
233 Returns the unixtime at midnight.
239 =item Timezone - Timezone context C<server> or C<UTC>
245 my %args = ( Timezone => 'UTC', @_ );
246 if ( lc $args{'Timezone'} eq 'server' ) {
247 $self->Unix( Time::Local::timelocal( 0,0,0,(localtime $self->Unix)[3..7] ) );
249 $self->Unix( Time::Local::timegm( 0,0,0,(gmtime $self->Unix)[3..7] ) );
251 return ($self->Unix);
260 return($self->Set(Format => 'unix', Value => time))
268 Takes either an RT::Date object or the date in unixtime format as a string
270 Returns the differnce between $self and that time as a number of seconds
278 if (ref($other) eq 'RT::Date') {
281 return ($self->Unix - $other);
285 # {{{ sub DiffAsString
287 =head2 sub DiffAsString
289 Takes either an RT::Date object or the date in unixtime format as a string
291 Returns the differnce between $self and that time as a number of seconds as
292 as string fit for human consumption
304 if ($self->Unix < 1) {
307 my $diff = $self->Diff($other);
309 return ($self->DurationAsString($diff));
313 # {{{ sub DurationAsString
316 =head2 DurationAsString
318 Takes a number of seconds. returns a string describing that duration
322 sub DurationAsString {
325 my $duration = shift;
327 my ( $negative, $s );
329 $negative = 1 if ( $duration < 0 );
331 $duration = abs($duration);
334 if ( $duration < $MINUTE ) {
336 $time_unit = $self->loc("sec");
338 elsif ( $duration < ( 2 * $HOUR ) ) {
339 $s = int( $duration / $MINUTE );
340 $time_unit = $self->loc("min");
342 elsif ( $duration < ( 2 * $DAY ) ) {
343 $s = int( $duration / $HOUR );
344 $time_unit = $self->loc("hours");
346 elsif ( $duration < ( 2 * $WEEK ) ) {
347 $s = int( $duration / $DAY );
348 $time_unit = $self->loc("days");
350 elsif ( $duration < ( 2 * $MONTH ) ) {
351 $s = int( $duration / $WEEK );
352 $time_unit = $self->loc("weeks");
354 elsif ( $duration < $YEAR ) {
355 $s = int( $duration / $MONTH );
356 $time_unit = $self->loc("months");
359 $s = int( $duration / $YEAR );
360 $time_unit = $self->loc("years");
364 return $self->loc( "[_1] [_2] ago", $s, $time_unit );
367 return $self->loc( "[_1] [_2]", $s, $time_unit );
373 # {{{ sub AgeAsString
375 =head2 sub AgeAsString
379 Returns a string that's the differnce between the time in the object and now
385 return ($self->DiffAsString(time));
393 Returns the object\'s time as a string with the current timezone.
399 return ($self->loc("Not set")) if ($self->Unix <= 0);
401 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
403 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));
409 =head2 GetWeekday DAY
411 Takes an integer day of week and returns a localized string for that day of week
419 return $self->loc('Mon.') if ($dow == 1);
420 return $self->loc('Tue.') if ($dow == 2);
421 return $self->loc('Wed.') if ($dow == 3);
422 return $self->loc('Thu.') if ($dow == 4);
423 return $self->loc('Fri.') if ($dow == 5);
424 return $self->loc('Sat.') if ($dow == 6);
425 return $self->loc('Sun.') if ($dow == 0);
434 Takes an integer month and returns a localized string for that month
442 # We do this rather than an array so that we don't call localize 12x what we need to
443 return $self->loc('Jan.') if ($mon == 0);
444 return $self->loc('Feb.') if ($mon == 1);
445 return $self->loc('Mar.') if ($mon == 2);
446 return $self->loc('Apr.') if ($mon == 3);
447 return $self->loc('May.') if ($mon == 4);
448 return $self->loc('Jun.') if ($mon == 5);
449 return $self->loc('Jul.') if ($mon == 6);
450 return $self->loc('Aug.') if ($mon == 7);
451 return $self->loc('Sep.') if ($mon == 8);
452 return $self->loc('Oct.') if ($mon == 9);
453 return $self->loc('Nov.') if ($mon == 10);
454 return $self->loc('Dec.') if ($mon == 11);
461 =head2 sub AddSeconds
463 Takes a number of seconds as a string
473 $self->Set(Format => 'unix', Value => ($self->Unix + $delta));
475 return ($self->Unix);
486 Adds 24 hours * $DAYS to the current time
493 $self->AddSeconds($days * $DAY);
503 Adds 24 hours to the current time
509 $self->AddSeconds($DAY);
517 =head2 sub Unix [unixtime]
519 Optionally takes a date in unix seconds since the epoch format.
520 Returns the number of seconds since the epoch
527 $self->{'time'} = shift if (@_);
529 return ($self->{'time'});
539 Returns the object's date in ISO format
545 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
547 return ('1970-01-01 00:00:00') if ($self->Unix == -1);
550 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
554 #the month needs incrementing, as gmtime returns 0-11
557 $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
570 Returns the object's date in yyyy-mm-dd format; this is the same as
571 the ISO format without the time
577 my ($date, $time) = split ' ', $self->ISO;
589 Returns the object's time in hh:mm:ss format; this is the same as
590 the ISO format without the date
596 my ($date, $time) = split ' ', $self->ISO;
608 Returns the object's date in W3C DTF format
614 my $date = $self->ISO . 'Z';
621 # {{{ sub LocalTimezone
625 Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
626 pull from a 'Timezone' attribute of the CurrentUser
633 return $self->CurrentUser->Timezone
634 if $self->CurrentUser and $self->CurrentUser->can('Timezone');
636 return ($RT::Timezone);
641 eval "require RT::Date_Vendor";
642 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
643 eval "require RT::Date_Local";
644 die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Local.pm});