root/lang/perl/DateTime-Lite/trunk/lib/DateTime/Lite.pm @ 24134

Revision 24134, 6.4 kB (checked in by daisuke, 5 years ago)

initial import

  • Property svn:keywords set to Id
Line 
1package DateTime::Lite;
2use strict;
3use warnings;
4use DateTime::Lite::TimeZone;
5use DateTime::Lite::Locale;
6use DateTime::Lite::Util;
7use Scalar::Util qw(blessed);
8
9use constant INFINITY     =>      (9 ** 9 ** 9);
10use constant NEG_INFINITY => -1 * (9 ** 9 ** 9);
11use constant NAN          => INFINITY - INFINITY;
12use constant SECONDS_PER_DAY => 86400;
13our $DefaultLocale = 'en_US';
14
15sub year  { $_[0]->{local_c}{year} }
16sub month { $_[0]->{local_c}{month} }
17sub day   { $_[0]->{local_c}{day} }
18
19*mon = \&month;
20
21sub new {
22    my ($class, %p) = @_;
23
24    # give default values, first...
25    {
26        my %defaults = (
27            day => 1,
28            month => 1,
29            year => 1,
30            hour => 0,
31            minute => 0,
32            second => 0,
33            nanosecond => 0
34        );
35        while (my ($key, $value) = each %defaults) {
36            $p{$key} = $value unless exists $p{$key};
37        }
38    }
39    my $day        = $p{day};
40    my $month      = $p{month};
41    my $year       = $p{year};
42    my $hour       = $p{hour};
43    my $minute     = $p{minute};
44    my $second     = $p{second};
45    my $nanosecond = $p{nanosecond};
46
47    if ($day > DateTime::Lite::Util::month_length($year, $month)) {
48        Carp::croak("Invalid day of month (day = $day - month = $month - year = $year\n");
49    }
50
51    my $self = bless {}, $class;
52
53    my $locale = delete $p{language} if exists $p{language};
54    $locale = $DefaultLocale unless defined $locale;
55    my $time_zone = $p{time_zone} || 'floating';
56
57    $self->{locale} = blessed $locale ?
58        $locale : DateTime::Lite::Locale->load($locale);
59
60    $self->{tz} = blessed $time_zone ?
61        $time_zone : DateTime::Lite::TimeZone->load(name => $time_zone);
62    $self->{local_rd_days} = DateTime::Lite::Util::ymd2rd($year, $month, $day);
63    $self->{local_rd_secs} = DateTime::Lite::Util::time_as_seconds($hour, $minute, $second);
64    $self->{offfset_modifier} = 0;
65    $self->{rd_nanosecs} = $nanosecond;
66    $self->{formatter} = $p{formatter};
67
68    DateTime::Lite::Util::normalize_nanoseconds($self->{local_rd_secs}, $self->{rd_nanosecs});
69
70    $self->{utc_year} = $year + 1;
71    $self->_calc_utc_rd;
72    $self->_handle_offset_modifier($second);
73    $self->_calc_local_rd;
74
75    if ($second > 59) {
76        if ($time_zone->is_floating || $self->{utc_rd_secs} - SECONDS_PER_DAY + 1 < $second - 59) {
77            Carp::croak("Invalid second value ($second)\n");
78        }
79    }
80   
81# use Data::Dumper;
82# print STDERR Dumper($self);
83    return $self;
84}
85
86sub _calc_utc_rd {
87    my $self = shift;
88    delete $self->{utc_c};
89
90    my $time_zone = $self->{tz};
91    if ($time_zone->is_utc || $time_zone->is_floating) {
92        $self->{utc_rd_days} = $self->{local_rd_days};
93        $self->{utc_rd_secs} = $self->{local_rd_secs};
94    } else {
95        my $offset = $self->_offset_for_local_datetime;
96        $offset += $self->{offset_modifier};
97
98        $self->{utc_rd_days} = $self->{local_rd_days};
99        $self->{utc_rd_secs} = $self->{local_rd_secs} - $offset;
100    }
101
102    # We account for leap seconds in the new() method and nowhere else
103    # except date math.
104    DateTime::Lite::Util::normalize_tai_seconds( $self->{utc_rd_days}, $self->{utc_rd_secs} );
105}
106
107sub _handle_offset_modifier
108{
109    my $self = shift;
110
111    $self->{offset_modifier} = 0;
112
113    return if $self->{tz}->is_floating;
114
115    my $second = shift;
116    my $utc_is_valid = shift;
117
118    my $utc_rd_days = $self->{utc_rd_days};
119
120    my $offset = $utc_is_valid ? $self->offset : $self->_offset_for_local_datetime;
121
122    if ( $offset >= 0
123         && $self->{local_rd_secs} >= $offset
124       )
125    {
126        if ( $second < 60 && $offset > 0 )
127        {
128            $self->{offset_modifier} =
129                $self->_day_length( $utc_rd_days - 1 ) - SECONDS_PER_DAY;
130
131            $self->{local_rd_secs} += $self->{offset_modifier};
132        }
133        elsif ( $second == 60
134                &&
135                ( ( $self->{local_rd_secs} == $offset
136                    && $offset > 0 )
137                  ||
138                  ( $offset == 0
139                    && $self->{local_rd_secs} > 86399 ) )
140              )
141        {
142            my $mod = $self->_day_length( $utc_rd_days - 1 ) - SECONDS_PER_DAY;
143
144            unless ( $mod == 0 )
145            {
146                $self->{utc_rd_secs} -= $mod;
147
148                $self->_normalize_seconds;
149            }
150        }
151    }
152    elsif ( $offset < 0
153            && $self->{local_rd_secs} >= SECONDS_PER_DAY + $offset )
154    {
155        if ( $second < 60 )
156        {
157            $self->{offset_modifier} =
158                $self->_day_length( $utc_rd_days - 1 ) - SECONDS_PER_DAY;
159            $self->{local_rd_secs} += $self->{offset_modifier};
160        }
161        elsif ( $second == 60 && $self->{local_rd_secs} == SECONDS_PER_DAY + $offset )
162        {
163            my $mod = $self->_day_length( $utc_rd_days - 1 ) - SECONDS_PER_DAY;
164
165            unless ( $mod == 0 )
166            {
167                $self->{utc_rd_secs} -= $mod;
168
169                $self->_normalize_seconds;
170            }
171        }
172    }
173}
174
175# XXX FIXME
176sub _offset_for_local_datetime { $_[0]->{tz}->offset_for_local_datetime( $_[0] ) }
177
178sub _calc_local_rd
179{
180    my $self = shift;
181
182    delete $self->{local_c};
183
184    # We must short circuit for UTC times or else we could end up with
185    # loops between DateTime.pm and DateTime::TimeZone
186    if ( $self->{tz}->is_utc || $self->{tz}->is_floating )
187    {
188        $self->{local_rd_days} = $self->{utc_rd_days};
189        $self->{local_rd_secs} = $self->{utc_rd_secs};
190    }
191    else
192    {        my $offset = $self->offset;
193
194        $self->{local_rd_days} = $self->{utc_rd_days};
195        $self->{local_rd_secs} = $self->{utc_rd_secs} + $offset;
196
197        # intentionally ignore leap seconds here
198        $self->_normalize_tai_seconds( $self->{local_rd_days}, $self->{local_rd_
199secs} );
200
201        $self->{local_rd_secs} += $self->{offset_modifier};
202    }
203
204    $self->_calc_local_components;
205}
206
207sub _calc_local_components
208{
209    my $self = shift;
210
211    @{ $self->{local_c} }{ qw( year month day day_of_week
212                               day_of_year quarter day_of_quarter) } =
213        DateTime::Lite::Util::rd2ymd( $self->{local_rd_days}, 1 );
214
215    @{ $self->{local_c} }{ qw( hour minute second ) } =
216        DateTime::Lite::Util::seconds_as_components
217            ( $self->{local_rd_secs}, $self->{utc_rd_secs}, $self->{offset_modifier} );
218}
219
220
221sub last_day_of_month {
222    my ($class, %p) = @_;
223    return $class->new(%p, day => DateTime::Lite::Util::month_length($p{year}, $p{month}));
224}
225
2261;
227
228__END__
Note: See TracBrowser for help on using the browser.