File Coverage

blib/lib/Date/Gregorian.pm
Criterion Covered Total %
statement 396 403 98.2
branch 71 78 91.0
condition 93 106 87.7
subroutine 87 87 100.0
pod 33 33 100.0
total 680 707 96.1


line stmt bran cond sub pod time code
1             # Copyright (c) 1999-2007 Martin Becker. All rights reserved.
2             # This package is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4             #
5             # $Id: Gregorian.pm,v 1.15 2007/06/19 12:10:58 martin Stab $
6              
7             package Date::Gregorian;
8              
9 6     6   4879 use strict;
  6         10  
  6         294  
10 6     6   5026 use integer;
  6         51  
  6         28  
11 6     6   180 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
  6         13  
  6         984  
12             require Exporter;
13              
14             @ISA = qw(Exporter);
15             %EXPORT_TAGS = (
16             'weekdays' => [qw(
17             MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
18             )],
19             'months' => [qw(
20             JANUARY FEBRUARY MARCH APRIL MAY JUNE JULY
21             AUGUST SEPTEMBER OCTOBER NOVEMBER DECEMBER
22             )],
23             );
24             @EXPORT_OK = map @{$_}, values %EXPORT_TAGS;
25              
26             $VERSION = '0.12';
27              
28             # ----- object definition -----
29              
30             # Date::Gregorian=ARRAY(...)
31              
32             # .......... index .......... # .......... value ..........
33 6     6   30 use constant F_DAYNO => 0; # continuos day number, "March ...th, 1 BC"
  6         10  
  6         509  
34 6     6   31 use constant F_TR_DATE => 1; # first Gregorian date in dayno format
  6         8  
  6         269  
35 6     6   28 use constant F_TR_EYR => 2; # first Gregorian easter year
  6         9  
  6         248  
36 6     6   26 use constant F_YMD => 3; # [year, month, day] (on demand, memoized)
  6         18  
  6         241  
37 6     6   25 use constant F_YDYW => 4; # [yearday, year, week] (on demand, memoized)
  6         8  
  6         15504  
38 6     6   232 use constant F_SEC_NS => 5; # [seconds, nanoseconds] (optional)
  6         31  
  6         3579  
39 6     6   76 use constant NFIELDS => 6;
  6         10  
  6         1436  
40              
41             # ----- other constants -----
42              
43 6     6   32 use constant MONDAY => 0;
  6         8  
  6         271  
44 6     6   162 use constant TUESDAY => 1;
  6         9  
  6         235  
45 6     6   32 use constant WEDNESDAY => 2;
  6         16  
  6         328  
46 6     6   31 use constant THURSDAY => 3;
  6         12  
  6         254  
47 6     6   28 use constant FRIDAY => 4;
  6         20  
  6         300  
48 6     6   28 use constant SATURDAY => 5;
  6         7  
  6         262  
49 6     6   25 use constant SUNDAY => 6;
  6         9  
  6         230  
50              
51 6     6   26 use constant JANUARY => 1;
  6         8  
  6         236  
52 6     6   28 use constant FEBRUARY => 2;
  6         8  
  6         238  
53 6     6   84 use constant MARCH => 3;
  6         9  
  6         266  
54 6     6   35 use constant APRIL => 4;
  6         11  
  6         304  
55 6     6   29 use constant MAY => 5;
  6         11  
  6         243  
56 6     6   39 use constant JUNE => 6;
  6         10  
  6         268  
57 6     6   29 use constant JULY => 7;
  6         9  
  6         289  
58 6     6   28 use constant AUGUST => 8;
  6         10  
  6         259  
59 6     6   31 use constant SEPTEMBER => 9;
  6         10  
  6         246  
60 6     6   30 use constant OCTOBER => 10;
  6         9  
  6         565  
61 6     6   29 use constant NOVEMBER => 11;
  6         23  
  6         283  
62 6     6   48 use constant DECEMBER => 12;
  6         10  
  6         1206  
63              
64             # ----- predefined private variables -----
65              
66             my @m2d = map +($_ * 153 + 2) / 5, (0..11);
67             my $epoch = _ymd2dayno( 1970, 1, 1, 1, 1);
68             my @defaults = (
69             $epoch, # F_DAYNO
70             _ymd2dayno(1582, 10, 15, 1, 1), # F_TR_DATE
71             1583, # F_TR_EYR
72             undef, # F_YMD
73             undef, # F_YDYW
74             undef, # F_SEC_NS
75             );
76             my ($gmt_epoch, $gmt_correction) = _init_gmt();
77             my $datetime_epoch = 307;
78             my $default_sec_ns = [0, 0];
79             my %JG = ('J' => 0, 'G' => 1);
80             my $localtime_offset = 0;
81              
82             # ----- private functions -----
83              
84             # ($div, $mod) = _divmod($numerator, $denominator)
85             #
86             sub _divmod {
87 6     6   35 no integer; # use well defined percent operator
  6         11  
  6         80  
88 6156     6156   6988 my $mod = $_[0] % $_[1];
89 6156         12372 return (($_[0] - $mod) / $_[1], $mod);
90             }
91              
92             # $dayno = _ymd2dayno($year, $month, $day, $tr_date, $fixed)
93             # fixed == 1: tr_date == 0: force Julian, tr_date == 1: force Gregorian
94             # fixed == boolean false: normal operation
95             #
96             sub _ymd2dayno {
97 2335     2335   3154 my ($y, $m, $d, $s, $fixed) = @_;
98              
99 2335 100       6189 if (15 <= $m) { $m -= 3; $y += $m / 12; $m %= 12; }
  1 100       10  
  1 100       3  
  1         3  
100 1677         1928 elsif ( 3 <= $m) { $m -= 3; }
101 656         803 elsif (-9 <= $m) { $m += 9; $y --; }
  656         700  
102 1         2 else { $m = 14 - $m; $y -= $m / 12; $m = 11 - $m % 12; }
  1         3  
  1         2  
103              
104 2335         3917 $d += $m2d[$m] + $y * 365 + ($y >> 2) - 1;
105 2335 100 100     10870 if (!$fixed && $s <= $d || $fixed && $s) {
      100        
      66        
106 2293 100       3941 $y = 0 <= $y? $y / 100: -((99 - $y) / 100);
107 2293         3338 $d -= $y - ($y >> 2) - 2;
108             }
109 2335         3785 return $d;
110             }
111              
112             # ($year, $month, $day) = _dayno2ymd($dayno, $tr_date)
113             #
114             sub _dayno2ymd {
115 6156     6156   6799 my ($n, $s) = @_;
116 6156         5694 my ($d, $m, $y);
117 0         0 my $c;
118 6156 100       9388 if ($s <= $n) {
119 6121         14691 ($c, $n) = _divmod($n - 2, 146097);
120 6121         8286 $c *= 400;
121 6121         9621 $n += (($n << 2) + 3) / 146097;
122             }
123             else {
124 35         53 ($c, $n) = _divmod($n, 1461);
125 35         48 $c <<= 2;
126             }
127 6156         6335 $y = (($n << 2) + 3) / 1461;
128 6156         7986 $n = ($n - $y * 365 - ($y >> 2)) * 5 + 2;
129 6156         5818 $m = $n / 153 + 3;
130 6156         6256 $d = $n % 153 / 5 + 1;
131 6156 100       10459 $y ++, $m -= 12 if 12 < $m;
132 6156         22918 return ($c + $y, $m, $d);
133             }
134              
135             # ($dayno, $ymd) = _easter($year, $tr_date, $tr_eyr)
136             #
137             sub _easter {
138 34     34   55 my ($y, $s, $e) = @_;
139 34         39 my $m = 3;
140 34         37 my $d;
141 34         49 my $n = $y * 365 + ($y >> 2);
142 34 100       59 if ($e <= $y) {
143 29 100       58 my $g = 0 <= $y? $y / 100: -((99 - $y) / 100);
144 29         44 $n -= $g - ($g >> 2) - 2;
145 6     6   5367 { no integer; $g %= 3000 };
  6         79  
  6         27  
  29         33  
  29         36  
146 29         55 my $h = 15 + $g - (($g << 3) + 13) / 25 - ($g >> 2);
147 6     6   340 $g = do { no integer; $y % 19 };
  6         10  
  6         168  
  29         28  
  29         38  
148 29         54 $d = ($g * 19 + $h) % 30;
149 29 100 100     155 --$d if 28 <= $d && (28 < $d || 11 <= $g);
      66        
150             }
151             else {
152 6     6   500 $d = do { no integer; ($y % 19 * 19 + 15) % 30 };
  6         147  
  6         24  
  5         6  
  5         11  
153             }
154 6     6   376 $d += do { no integer; 28 - ($n + $d) % 7 };
  6         10  
  6         76  
  34         34  
  34         60  
155 34         39 $n += $d - 1;
156 34 100       72 $d -= 31, $m ++ if 31 < $d;
157 34 100 100     211 return ($n, ($s <= $n xor $e <= $y)? undef: [$y, $m, $d]);
158             }
159              
160             # $dayno = _dec31dayno($year, $tr_date)
161             # calculate day number of last day in year (usually December 31)
162             #
163             sub _dec31dayno {
164 10146     10146   12240 my ($y, $s) = @_;
165              
166 10146         13013 my $n = 306 + $y * 365 + ($y >> 2) - 1;
167 10146 100       16882 if ($s <= $n) {
168 10138 100       14655 $y = 0 <= $y? $y / 100: -((99 - $y) / 100);
169 10138         11054 $n -= $y - ($y >> 2) - 2;
170 10138 100       16445 if ($n < $s) {
171 4         12 return $s-1;
172             }
173             }
174 10142         13648 return $n;
175             }
176              
177             # $ydyw = _ydyw($dayno, $tr_date, $year)
178             #
179             sub _ydyw {
180 4602     4602   5469 my ($n, $s, $y) = @_;
181 4602         10118 my $base = _dec31dayno($y-1, $s);
182 4602         5193 my $yd = $n - $base;
183 4602         4223 $base += 4;
184 6     6   1753 { no integer; $base -= $base % 7 };
  6         29  
  6         24  
  4602         4187  
  4602         5618  
185 4602 100       7371 if ($n < $base) {
186 3         3 $y --;
187 3         6 $base = _dec31dayno($y-1, $s) + 4;
188 6     6   415 { no integer; $base -= $base % 7 };
  6         8  
  6         29  
  3         4  
  3         4  
189             }
190             else {
191 4599         8231 my $limit = _dec31dayno($y, $s) + 4;
192 6     6   292 { no integer; $limit -= $limit % 7 };
  6         9  
  6         22  
  4599         4595  
  4599         5191  
193 4599 100       7823 if ($limit <= $n) {
194 31         35 $base = $limit;
195 31         34 $y ++;
196             }
197             }
198 4602         5314 my $yw = ($n - $base) / 7 + 1;
199 4602         15464 return [$yd, $y, $yw];
200             }
201              
202             # ($gmt_epoch, $gmt_correction) = _init_gmt()
203             #
204             sub _init_gmt {
205 6     6   139 my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(0);
206             return (
207 6         30 _ymd2dayno(1900 + $year, 1 + $mon, $mday, 1, 1),
208             ($hour*60 + $min)*60 + $sec
209             );
210             }
211              
212             # ----- public methods -----
213              
214             sub new {
215 825     825 1 264166 my $class = $_[0];
216 825         792 my Date::Gregorian $self;
217 825 100       1616 if (ref $class) { # called as obj method: clone it
218 792         793 $self = bless [@{$class}], ref($class);
  792         3318  
219             }
220             else { # called as class method: create
221 33         167 $self = bless [@defaults], $class;
222             }
223 825         1968 return $self;
224             }
225              
226             sub configure {
227 13     13 1 83 my Date::Gregorian $self = shift;
228 13         22 my ($y, $m, $d, $e) = @_;
229 13         34 @{$self}[F_TR_DATE, F_YMD, F_YDYW] =
  13         28  
230             ( _ymd2dayno($y, $m, $d, 1, 1), undef, undef );
231 13 100       34 $self->[F_TR_EYR] = $e if defined $e;
232 13         29 return $self;
233             }
234              
235             sub is_gregorian {
236 14     14 1 36 my Date::Gregorian $self = $_[0];
237 14         44 return $self->[F_TR_DATE] <= $self->[F_DAYNO];
238             }
239              
240             sub set_date {
241 31     31 1 149 my Date::Gregorian ($self, $ref) = @_;
242 31         56 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ( $ref->[F_DAYNO], undef, undef );
  31         54  
243 31         89 return $self;
244             }
245              
246             sub set_ymd {
247 1663     1663 1 6360 my Date::Gregorian $self = shift;
248 1663         2121 my ($y, $m, $d) = @_;
249 1663         3118 @{$self}[F_DAYNO, F_YMD, F_YDYW] =
  1663         3445  
250             ( _ymd2dayno($y, $m, $d, $self->[F_TR_DATE]), undef, undef );
251 1663         4842 return $self;
252             }
253              
254             sub check_ymd {
255 214     214 1 297 my Date::Gregorian $self = shift;
256 214         325 my ($y, $m, $d) = @_;
257 214         229 my ($dayno, $yy, $mm, $dd);
258 214 100 100     3365 if (defined($d) && 1 <= $d && $d <= 31 &&
      100        
      100        
      100        
      100        
      100        
      100        
      100        
259             defined($m) && 1 <= $m && $m <= 12 &&
260             defined($y) && -1469871 <= $y && $y <= 5879489
261             ) {
262 203         403 $dayno = _ymd2dayno($y, $m, $d, $self->[F_TR_DATE]);
263 203         405 ($yy, $mm, $dd) = _dayno2ymd($dayno, $self->[F_TR_DATE]);
264 203 50 66     1086 if ($dd == $d && $mm == $m && $yy == $y) {
      66        
265 192         366 @{$self}[F_DAYNO, F_YMD, F_YDYW] =
  192         364  
266             ( $dayno, [$yy, $mm, $dd], undef );
267 192         694 return $self;
268             }
269             }
270 22         82 return undef;
271             }
272              
273             sub get_ymd {
274 6065     6065 1 8966 my Date::Gregorian $self = $_[0];
275 6065   100     18017 my $ymd = $self->[F_YMD] ||=
276             [ _dayno2ymd($self->[F_DAYNO], $self->[F_TR_DATE]) ];
277 6065         6868 return @{$ymd};
  6065         12326  
278             }
279              
280             sub get_weekday {
281 6     6   7195 no integer;
  6         11  
  6         32  
282 1472     1472 1 1565 my Date::Gregorian $self = $_[0];
283 1472         3480 return $self->[F_DAYNO] % 7;
284             }
285              
286             sub set_yd {
287 535     535 1 683 my Date::Gregorian $self = shift;
288 535         696 my ($y, $d) = @_;
289 535         1050 my $n = _dec31dayno($y-1, $self->[F_TR_DATE]) + $d;
290 535         824 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ($n, undef, undef);
  535         1053  
291 535         1462 return $self;
292             }
293              
294             sub set_ywd {
295 6     6   1124 no integer;
  6         11  
  6         21  
296 2     2 1 15 my Date::Gregorian $self = shift;
297 2         3 my ($y, $w, $d) = @_;
298 2         7 my $n = _dec31dayno($y-1, $self->[F_TR_DATE]) - 3;
299 2         5 $n += $w * 7 + $d - $n % 7;
300 2         4 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ($n, undef, undef);
  2         4  
301 2         6 return $self;
302             }
303              
304             sub check_ywd {
305 6     6   950 no integer;
  6         12  
  6         21  
306 17     17 1 66 my Date::Gregorian $self = shift;
307 17         27 my ($y, $w, $d) = @_;
308 17 100 100     208 if (defined($d) && 0 <= $d && $d <= 6 &&
      100        
      100        
      100        
      100        
      100        
      100        
      100        
309             defined($w) && 1 <= $w && $w <= 53 &&
310             defined($y) && -1469871 <= $y && $y <= 5879489
311             ) {
312 5         21 my $n = _dec31dayno($y-1, $self->[F_TR_DATE]) - 3;
313 5         12 $n += $w * 7 + $d - $n % 7;
314 5         10 my $ymd = [_dayno2ymd($n, $self->[F_TR_DATE])];
315 5         14 my $ydyw = _ydyw($n, $self->[F_TR_DATE], $ymd->[0]);
316 5 100 66     32 if ($ydyw->[1] == $y && $ydyw->[2] == $w) {
317 3         5 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ($n, $ymd, $ydyw);
  3         7  
318 3         12 return $self;
319             }
320             }
321 14         56 return undef;
322             }
323              
324             sub get_yd {
325 5519     5519 1 5770 my Date::Gregorian $self = $_[0];
326 5519         8520 my ($y, $m, $d) = $self->get_ymd;
327 5519 100       12122 return ($y, $d) if 1 == $m;
328 4667   66     9336 my $ydyw = $self->[F_YDYW] ||= _ydyw(@{$self}[F_DAYNO, F_TR_DATE], $y);
  4588         8450  
329 4667         12430 return ($y, $ydyw->[0]);
330             }
331              
332             sub get_ywd {
333 6     6   2811 no integer;
  6         9  
  6         25  
334 10     10 1 49 my Date::Gregorian $self = $_[0];
335 10         20 my $y = ($self->get_ymd)[0];
336 10   66     25 my $ydyw = $self->[F_YDYW] ||= _ydyw(@{$self}[F_DAYNO, F_TR_DATE], $y);
  9         19  
337 10         14 return (@{$ydyw}[1, 2], $self->[F_DAYNO] % 7);
  10         34  
338             }
339              
340             sub add_days {
341 5263     5263 1 19032 my Date::Gregorian $self = $_[0];
342 5263         5934 $self->[F_DAYNO] += $_[1];
343 5263         5450 @{$self}[F_YMD, F_YDYW] = (undef, undef);
  5263         8310  
344 5263         11236 return $self;
345             }
346              
347             sub get_days_until {
348 5     5 1 13 my Date::Gregorian ($self, $then) = @_;
349 5         18 return $then->[F_DAYNO] - $self->[F_DAYNO];
350             }
351              
352             sub get_days_since {
353 1800     1800 1 3374 my Date::Gregorian ($self, $then) = @_;
354 1800         4474 return $self->[F_DAYNO] - $then->[F_DAYNO];
355             }
356              
357             sub compare {
358 11     11 1 77 my Date::Gregorian ($self, $then) = @_;
359 11         41 return $self->[F_DAYNO] <=> $then->[F_DAYNO];
360             }
361              
362             sub set_easter {
363 34     34 1 95 my Date::Gregorian $self = $_[0];
364 34         66 @{$self}[F_DAYNO, F_YMD, F_YDYW] =
  34         85  
365 34         48 ( _easter($_[1], @{$self}[F_TR_DATE, F_TR_EYR]), undef );
366 34         105 return $self;
367             }
368              
369             sub set_gmtime {
370 6     6   3375 no integer;
  6         12  
  6         25  
371 2     2 1 15 my Date::Gregorian $self = $_[0];
372 2         3 my $time = $_[1] + $gmt_correction;
373 2         3 $time -= $time % 86400;
374 2         6 @{$self}[F_DAYNO, F_YMD, F_YDYW] = (
  2         4  
375             $gmt_epoch + $time / 86400,
376             undef, undef,
377             );
378 2         4 return $self;
379             }
380              
381             sub get_gmtime {
382 6     6   6562 no integer;
  6         19  
  6         24  
383 431     431 1 465 my Date::Gregorian $self = $_[0];
384 431         619 my $d = $self->[F_DAYNO] - $gmt_epoch;
385 431         817 return 86400 * $d - $gmt_correction;
386             }
387              
388             sub set_today {
389 2     2 1 16 my Date::Gregorian $self = $_[0];
390 2         7 return $self->set_localtime(time);
391             }
392              
393             sub set_localtime {
394 5     5 1 24 my Date::Gregorian $self = $_[0];
395 5         263 my ($d, $m, $y) = (localtime $_[1])[3..5];
396 5         11 $y += 1900;
397 5         6 ++ $m;
398             # presuming localtime always to return Gregorian dates,
399             # while $self might be configured to interpret Julian,
400             # we must ignore $self->[F_TR_DATE] here
401 5         10 @{$self}[F_DAYNO, F_YMD, F_YDYW] =
  5         9  
402             ( _ymd2dayno($y, $m, $d, 1, 1), undef, undef );
403 5         17 return $self;
404             }
405              
406             sub get_localtime {
407 6     6   1457 no integer;
  6         11  
  6         26  
408 428     428 1 1423 my Date::Gregorian $self = $_[0];
409              
410 428         758 my $time = $self->get_gmtime - $localtime_offset;
411 428         856 foreach my $step (0..3) {
412 428         8818 my ($S, $M, $H, $d, $m, $y) = localtime $time;
413 428         1381 my $dd = _ymd2dayno(1900+$y, 1+$m, $d, 1, 1) - $self->[F_DAYNO];
414 428 50       1016 return undef if 24855 < abs($dd);
415 428         637 my $delta = (($dd * 24 + $H) * 60 + $M) * 60 + $S;
416 428 50 33     2143 if ($delta || $dd) {
417 0 0 0     0 if ($dd < 0 && 0 <= $delta) {
418             # hours/minutes/seconds should not cancel out date increase
419 0         0 $delta = -1;
420             }
421 0         0 $time -= $delta;
422 0 0       0 $localtime_offset += $delta if !$step;
423 0         0 next;
424             }
425 428         2345 return $time;
426             }
427 0         0 return undef;
428             }
429              
430             sub set_weekday {
431 6     6   1321 no integer;
  6         11  
  6         25  
432 16     16 1 34 my Date::Gregorian $self = shift;
433 16         22 my ($wd, $rel) = @_;
434 16         31 my $delta = ($wd - $self->[F_DAYNO]) % 7;
435 16 100 100     63 if (defined($rel) && '>=' ne $rel) {
436 11 100 100     37 $delta = 7 if !$delta && '>' eq $rel;
437 11 100 100     55 $delta -= 7 if '<' eq $rel || $delta && '<=' eq $rel;
      66        
438             }
439 16 100       31 if ($delta) {
440 12         13 $self->[F_DAYNO] += $delta;
441 12         15 @{$self}[F_YMD, F_YDYW] = (undef, undef);
  12         17  
442             }
443 16         33 return $self;
444             }
445              
446             sub get_days_in_year {
447 200     200 1 251 my ($self, $year) = @_;
448             return
449 200         353 _dec31dayno($year, $self->[F_TR_DATE]) -
450             _dec31dayno($year-1, $self->[F_TR_DATE]);
451             }
452              
453             sub iterate_days_upto {
454 18     18 1 205 my ($self, $limit, $rel, $step) = @_;
455 18         29 my $dayno = $self->[F_DAYNO];
456 18         61 my $final = $limit->[F_DAYNO] - ($rel ne '<=');
457 18   100     63 $step = abs($step || 1);
458             return sub {
459 523 100   523   55277 return undef if $dayno > $final;
460 505         723 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ($dayno, undef, undef);
  505         1334  
461 505         876 $dayno += $step;
462 505         1010 return $self;
463 18         99 };
464             }
465              
466             sub iterate_days_downto {
467 6     6 1 15 my ($self, $limit, $rel, $step) = @_;
468 6         9 my $dayno = $self->[F_DAYNO];
469 6         11 my $final = $limit->[F_DAYNO] + ($rel eq '>');
470 6   100     19 $step = abs($step || 1);
471             return sub {
472 19 100   19   86 return undef if $dayno < $final;
473 13         17 @{$self}[F_DAYNO, F_YMD, F_YDYW] = ($dayno, undef, undef);
  13         23  
474 13         16 $dayno -= $step;
475 13         40 return $self;
476 6         31 };
477             }
478              
479             # --- DateTime interface ---
480              
481             sub set_datetime {
482 5     5 1 8 my ($self, $datetime) = @_;
483 5 100       17 if (!$datetime->time_zone->is_floating) {
484 3         27 $datetime = $datetime->clone->set_time_zone('floating');
485             }
486 5         576 my ($rd_days, @sec_ns) = $datetime->utc_rd_values;
487 5         37 @{$self}[F_DAYNO, F_YMD, F_YDYW, F_SEC_NS] =
  5         16  
488             ($rd_days + $datetime_epoch, undef, undef, \@sec_ns);
489 5         27 return $self;
490             }
491              
492             sub utc_rd_values {
493 8     8 1 952 my $self = $_[0];
494             return (
495 8 100       48 $self->[F_DAYNO] - $datetime_epoch,
496 8         12 @{$self->[F_SEC_NS] || $default_sec_ns}
497             );
498             }
499              
500             sub truncate_to_day {
501 1     1 1 20 my $self = $_[0];
502 1         4 undef $self->[F_SEC_NS];
503 1         3 return $self;
504             }
505              
506             sub from_object {
507 3     3 1 1852 my ($class, %param) = @_;
508 3         12 return $class->new->set_datetime($param{'object'});
509             }
510              
511             # must not define time_zone and set_time_zone methods
512              
513             # --- stringification ---
514              
515             sub get_string {
516 2     2 1 10 my $self = $_[0];
517 2 100       5 my $suffix = $self->is_gregorian? 'G': 'J';
518 2         10 return sprintf "%d-%02d-%02d$suffix", $self->get_ymd;
519             }
520              
521             sub set_string {
522 7     7 1 16 my ($self, $string) = @_;
523 7 100       32 if ($string =~ /^(-?\d+)-(\d+)-(\d+)([JG]?)\z/) {
524 5 100       21 $self->[F_DAYNO] =
525             _ymd2dayno($1, $2, $3, $4? ($JG{$4}, 1): $self->[F_TR_DATE]);
526 5         10 @{$self}[F_YMD, F_YDYW] = (undef, undef);
  5         16  
527 5         13 return $self;
528             }
529 2         4 return undef;
530             }
531              
532             # no DESTROY method, nothing to clean up
533              
534             1;
535              
536             __END__