root/branches/release-33/lib/MT/DateTime.pm @ 1744

Revision 1744, 6.2 kB (checked in by fumiakiy, 20 months ago)

Implemented MT::DateTime::compare. BugId:67917

  • Property svn:keywords set to Author Date Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7# Adapted from DateTime package to avoid requirement of DateTime package.
8
9package MT::DateTime;
10
11use Exporter;
12@MT::DateTime::ISA = qw( Exporter );
13use vars qw( @EXPORT_OK );
14@EXPORT_OK = qw( ymd2rd tz_offset_as_seconds );
15
16use MT::Util qw( epoch2ts );
17
18sub new {
19    my $class = shift;
20    my (%param) = @_;
21    my $self = \%param;
22    bless $self, $class || __PACKAGE__;
23}
24
25sub week_year { (shift->week)[0] }
26sub week_number { (shift->week)[1] }
27
28sub year { shift->{year} }
29sub month { shift->{month} }
30sub day { shift->{day} }
31sub hour { shift->{hour} }
32sub minute { shift->{minute} }
33sub second { shift->{second} }
34sub time_zone { shift->{time_zone} }
35
36sub day_of_year {
37    my $self = shift;
38    return $self->{local_c}{day_of_year} if $self->{local_c}{day_of_year};
39
40    my $year = $self->year;
41    my $days = 0;
42
43    require MT::Util;
44    for (my $i = 1; $i < $self->month; $i++) {
45        $days += MT::Util::days_in($i, $year);
46    }
47    $days += $self->day;
48    $self->{local_c}{day_of_year} = $days;
49}
50
51sub week {
52    my $self = shift;
53
54    unless ( defined $self->{local_c}{week_year} ) {
55        my $jan_one_dow_m1 =
56            ( ( $self->ymd2rd( $self->year, 1, 1 ) + 6 ) % 7 );
57
58        $self->{local_c}{week_number} =
59            int( ( ( $self->day_of_year) + $jan_one_dow_m1 ) / 7 );
60        $self->{local_c}{week_number}++ if $jan_one_dow_m1 < 4;
61
62        if ( $self->{local_c}{week_number} == 0 ) {
63            $self->{local_c}{week_year} = $self->year - 1;
64            $self->{local_c}{week_number} =
65                $self->weeks_in_year( $self->{local_c}{week_year} );
66        }
67        elsif ( $self->{local_c}{week_number} == 53 &&
68                $self->weeks_in_year( $self->year ) == 52 )
69        {
70            $self->{local_c}{week_number} = 1;
71            $self->{local_c}{week_year} = $self->year + 1;
72        }
73        else
74        {
75            $self->{local_c}{week_year} = $self->year;
76        }
77    }
78
79    return @{ $self->{local_c} }{ 'week_year', 'week_number' }
80}
81
82sub weeks_in_year {
83    my $self = shift;
84    my $year = shift;
85   
86    my $jan_one_dow =
87        ( ( $self->ymd2rd( $year, 1, 1 ) + 6 ) % 7 ) + 1;
88    my $dec_31_dow =
89        ( ( $self->ymd2rd( $year, 12, 31 ) + 6 ) % 7 ) + 1;
90
91    return $jan_one_dow == 4 || $dec_31_dow == 4 ? 53 : 52;
92}
93
94sub ymd2rd {
95    my $self = shift;
96
97    use integer;
98    my ( $y, $m, $d );
99    if (@_) {
100        ( $y, $m, $d ) = @_;
101    } elsif (ref $self) {
102        ( $y, $m, $d ) = ( $self->{year}, $self->{month}, $self->{day} );
103    }
104
105    my $adj;
106
107    # make month in range 3..14 (treat Jan & Feb as months 13..14 of
108    # prev year)
109    if ( $m <= 2 )
110    {
111        $y -= ( $adj = ( 14 - $m ) / 12 );
112        $m += 12 * $adj;
113    }
114    elsif ( $m > 14 )
115    {
116        $y += ( $adj = ( $m - 3 ) / 12 );
117        $m -= 12 * $adj;
118    }
119
120    # make year positive (oh, for a use integer 'sane_div'!)
121    if ( $y < 0 )
122    {
123        $d -= 146097 * ( $adj = ( 399 - $y ) / 400 );
124        $y += 400 * $adj;
125    }
126
127    # add: day of month, days of previous 0-11 month period that began
128    # w/March, days of previous 0-399 year period that began w/March
129    # of a 400-multiple year), days of any 400-year periods before
130    # that, and 306 days to adjust from Mar 1, year 0-relative to Jan
131    # 1, year 1-relative (whew)
132
133    $d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 +
134          ( $y / 100 * 36524 + $y / 400 ) - 306;
135}
136
137sub tz_offset_as_seconds {   
138    my $self = shift;
139    my $offset = shift; 
140    if (ref $self) {
141        $offset = $self->{time_zone};
142    }
143
144    return undef unless defined $offset;
145
146    return 0 if $offset eq '0';
147       
148    my ( $sign, $hours, $minutes, $seconds );
149    if ( $offset =~ /^([\+\-])?(\d\d?):(\d\d)(?::(\d\d))?$/ )
150    {
151        ( $sign, $hours, $minutes, $seconds ) = ( $1, $2, $3, $4 );
152    }
153    elsif ( $offset =~ /^([\+\-])?(\d\d)(\d\d)(\d\d)?$/ )
154    {
155        ( $sign, $hours, $minutes, $seconds ) = ( $1, $2, $3, $4 );
156    }   
157    else
158    {       
159        return undef;
160    }
161       
162    $sign = '+' unless defined $sign;
163    return undef unless $hours >= 0 && $hours <= 99;
164    return undef unless $minutes >= 0 && $minutes <= 59;
165    return undef unless ! defined( $seconds ) || ( $seconds >= 0 && $seconds <= 59 );   
166       
167    my $total =  $hours * 3600 + $minutes * 60;
168    $total += $seconds if $seconds;
169    $total *= -1 if $sign eq '-';
170
171    return $total;
172}
173
174sub _param2ts {
175    my ( $param, $blog ) = @_;
176
177    my ( $type, $value );
178    if ( 'HASH' eq ref($param) ) {
179        $type = $param->{type};
180        $value = $param->{value};
181    }
182    else {
183        $type = 'ts';
184        $value = $param;
185    }
186    if ( 'CODE' eq ref($value) ) {
187        $value = $value->();
188    }
189
190    my $ts;
191    if ( 'epoch' eq $type ) {
192        $ts = epoch2ts( $blog, $value );
193    }
194    elsif ( 'datetime' eq $type ) {
195        $ts = sprintf "%04d%02d%02d%02d%02d%02d",
196            $value->year,
197            $value->month,
198            $value->day,
199            $value->hour,
200            $value->minute,
201            $value->second;
202    }
203    else {
204        $ts = $value;
205    }
206    $ts;
207}
208
209sub compare {
210    my $self = shift;
211    my %param = @_;
212    # a => $ts | CODE | { value => CODE|$v, type => ts|epoch|datetime }
213    # b => $ts | CODE | { value => CODE|$v, type => ts|epoch|datetime }
214    # blog => ref|N|undef
215    # comparer => CODE|undef
216
217    my $blog = $param{blog};
218    if ( defined($blog) && !ref($blog) ) {
219        $blog = MT->model('blog')->load( $blog );
220        $blog = undef unless ref($blog);
221    }
222
223    if ( !exists($param{a}) && ref($self) ) {
224        $param{a} = { value => $self, type => 'datetime' };
225    }
226    my $ts_a = _param2ts( $param{a}, $blog );
227
228    if ( !exists($param{b}) && ref($self) ) {
229        $param{b} = { value => $self, type => 'datetime' };
230    }
231    my $ts_b = _param2ts( $param{b}, $blog );
232
233    my $comparer = $param{code};
234    if ( 'CODE' eq ref($comparer) ) {
235        return $comparer->( $ts_a, $ts_b );
236    }
237    else {
238        return $ts_a - $ts_b;
239    }
240}
241
2421;
243__END__
244
245=head1 NAME
246
247MT::DateTime
248
249=head1 AUTHOR & COPYRIGHT
250
251Please see L<MT/AUTHOR & COPYRIGHT>.
252
253=cut
Note: See TracBrowser for help on using the browser.