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

Revision 1745, 6.8 kB (checked in by fumiakiy, 20 months ago)

Wrote simple pod document.

  • 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 METHODS
250
251=head2 compare( a => $a, b => $b, blog => $blog )
252
253Compares two timestamp strings and returns negative valye, 0, or positive value
254depending on whether the "a" argument is less than, equal to, or greater than
255the "b" argument.
256
257You can specify scalar value to "a" and "b".  They are treated as timestamp values
258(e.g. 20080405123456).  You can also specify either epoch string (e.g the string
259returned from time method), or MT::DateTime object.  In those cases, you must
260specify both value and type in a hash, for example:
261
262MT::DateTime->compare( blog => $blog,
263    a => '20041231123456', b => { value => time(), type => 'epoch' } );
264
265=head1 AUTHOR & COPYRIGHT
266
267Please see L<MT/AUTHOR & COPYRIGHT>.
268
269=cut
Note: See TracBrowser for help on using the browser.