File Coverage

blib/lib/Random/Day.pm
Criterion Covered Total %
statement 153 153 100.0
branch 63 64 98.4
condition 9 9 100.0
subroutine 20 20 100.0
pod 10 10 100.0
total 255 256 99.6


line stmt bran cond sub pod time code
1             package Random::Day;
2              
3 34     34   1640815 use strict;
  34         63  
  34         1325  
4 34     34   204 use warnings;
  34         68  
  34         2503  
5              
6 34     34   17574 use Class::Utils qw(set_params);
  34         204511  
  34         783  
7 34     34   37477 use DateTime;
  34         19333095  
  34         1878  
8 34     34   25887 use DateTime::Event::Random;
  34         2106234  
  34         1700  
9 34     34   24485 use DateTime::Event::Recurrence;
  34         266551  
  34         1744  
10 34     34   366 use English;
  34         106  
  34         400  
11 34     34   19295 use Error::Pure qw(err);
  34         80  
  34         2695  
12 34     34   26900 use Mo::utils 0.08 qw(check_isa);
  34         110659  
  34         1042  
13              
14             our $VERSION = 0.17;
15              
16             # Constructor.
17             sub new {
18 128     128 1 4212253 my ($class, @params) = @_;
19              
20             # Create object.
21 128         439 my $self = bless {}, $class;
22              
23             # Day.
24 128         509 $self->{'day'} = undef;
25              
26             # DateTime object from.
27 128         651 $self->{'dt_from'} = DateTime->new(
28             'year' => 1900,
29             );
30              
31             # DateTime object to.
32 128         51661 $self->{'dt_to'} = DateTime->new(
33             'year' => 2050,
34             );
35              
36             # Month.
37 128         44129 $self->{'month'} = undef;
38              
39             # Year.
40 128         327 $self->{'year'} = undef;
41              
42             # Process parameters.
43 128         731 set_params($self, @params);
44              
45 122         2716 check_isa($self, 'dt_from', 'DateTime');
46 120         3897 check_isa($self, 'dt_to', 'DateTime');
47              
48 118 100       2694 if (DateTime->compare($self->{'dt_from'}, $self->{'dt_to'}) == 1) {
49             err "Parameter 'dt_from' must have older or same date than 'dt_to'.",
50             'Date from', $self->{'dt_from'},
51 3         593 'Date to', $self->{'dt_to'},
52             ;
53             }
54              
55             # There is no sense in case that dt_from and dt_to parameters are in
56             # same day and not in 00:00:00.
57 115 100 100     21998 if ($self->{'dt_from'}->year == $self->{'dt_to'}->year
      100        
      100        
58             && $self->{'dt_from'}->month == $self->{'dt_to'}->month
59             && $self->{'dt_from'}->day == $self->{'dt_to'}->day
60             && $self->{'dt_from'}->hms ne '00:00:00') {
61              
62             err "Parameters 'dt_from' and 'dt_to' are in the same day and not on begin.",
63             'Date from', $self->{'dt_from'},
64 1         56 'Date to', $self->{'dt_to'},
65             ;
66             }
67              
68 114         2857 return $self;
69             }
70              
71             # Get DateTime object with random date.
72             sub get {
73 10     10 1 73 my ($self, $date) = @_;
74              
75 10 100       39 if ($self->{'year'}) {
76 4 100       13 if ($self->{'month'}) {
77 2 100       9 if ($self->{'day'}) {
78             $date = $self->random_day_month_year(
79             $self->{'day'},
80             $self->{'month'},
81 1         7 $self->{'year'},
82             );
83             } else {
84             $date = $self->random_month_year(
85             $self->{'month'},
86 1         7 $self->{'year'},
87             );
88             }
89             } else {
90 2 100       6 if ($self->{'day'}) {
91             $date = $self->random_day_year(
92             $self->{'day'},
93 1         35 $self->{'year'},
94             );
95             } else {
96 1         6 $date = $self->random_year($self->{'year'});
97             }
98             }
99             } else {
100 6 100       18 if ($self->{'month'}) {
101 2 100       8 if ($self->{'day'}) {
102             $date = $self->random_day_month(
103             $self->{'day'},
104 1         7 $self->{'month'},
105             );
106             } else {
107 1         6 $date = $self->random_month($self->{'month'});
108             }
109             } else {
110 4 100       62 if ($self->{'day'}) {
111 1         6 $date = $self->random_day($self->{'day'});
112             } else {
113 3         23 $date = $self->random;
114             }
115             }
116             }
117              
118 10         28199 return $date;
119             }
120              
121             # Random DateTime object for day.
122             sub random {
123 25     25 1 113 my $self = shift;
124              
125             # Random DateTime between from and to.
126 25         118 my $dt_from = $self->{'dt_from'}->clone;
127 25 100       485 if ($dt_from->hms ne '00:00:00') {
128 8         2561 $dt_from->add('days' => 1);
129 8         11363 $dt_from->set(
130             'hour' => 0,
131             'minute' => 0,
132             'second' => 0,
133             );
134             }
135             my $dt = DateTime::Event::Random->datetime(
136             'after' => $dt_from,
137 25         5643 'before' => $self->{'dt_to'},
138             );
139              
140             # Unify to begin of day.
141 25         231080 my $daily = DateTime::Event::Recurrence->daily;
142 25         14161 my $ret_dt = $daily->current($dt);
143              
144 25         125360 return $ret_dt;
145             }
146              
147             # Random DateTime object for day defined by day.
148             sub random_day {
149 19     19 1 172 my ($self, $day) = @_;
150              
151 19         107 $self->_check_day($day);
152 7         75 my $monthly_day = DateTime::Event::Recurrence->monthly(
153             'days' => $day,
154             );
155              
156 7         4387 return $monthly_day->next($self->random);
157             }
158              
159             # Random DateTime object for day defined by day and month.
160             sub random_day_month {
161 16     16 1 148 my ($self, $day, $month) = @_;
162              
163 16         91 $self->_check_day($day);
164 10         99 my $yearly_day_month = DateTime::Event::Recurrence->yearly(
165             'days' => $day,
166             'months' => $month,
167             );
168 10         5004 my $dt = $yearly_day_month->next($self->random);
169 10 100       26786 if (! defined $dt) {
170 6         55 err 'Cannot create DateTime object.';
171             }
172              
173 4         103 return $dt;
174             }
175              
176             # Random DateTime object for day defined by day and year.
177             sub random_day_year {
178 12     12 1 112 my ($self, $day, $year) = @_;
179              
180 12         43 $self->_check_day($day);
181 9 100       24 if ($day > 31) {
182 1         5 err 'Day is greater than possible day.',
183             'Day', $day,
184             ;
185             }
186 8 100       23 if ($self->{'dt_from'}->year > $year) {
187             err 'Year is lesser than minimal year.',
188             'Expected year', $year,
189 1         8 'Minimal year', $self->{'dt_from'}->year,
190             ;
191             }
192 7 100       53 if ($self->{'dt_to'}->year < $year) {
193             err 'Year is greater than maximal year.',
194             'Expected year', $year,
195 1         8 'Maximal year', $self->{'dt_to'}->year,
196             ;
197             }
198 6         35 my ($from_month, $to_month) = (1, 12);
199 6 100       17 if ($self->{'dt_from'}->year == $year) {
200 3         17 $from_month = $self->{'dt_from'}->month;
201 3 100       17 if ($self->{'dt_from'}->day > $day) {
202 2         9 $from_month++;
203             }
204 3 100       12 if ($from_month > 12) {
205 1         5 err 'Day is lesser than minimal possible date.';
206             }
207             }
208 5 100       26 if ($self->{'dt_to'}->year == $year) {
209 3         18 $to_month = $self->{'dt_to'}->month;
210 3 100       14 if ($self->{'dt_to'}->day < $day) {
211 1         5 $to_month--;
212             }
213 3 100       13 if ($to_month < 1) {
214 1         4 err 'Day is greater than maximal possible date.';
215             }
216             }
217 4 100       19 if ($to_month < $from_month) {
218 1         4 err 'Day not fit between start and end dates.';
219             }
220 3         11 my @possible_months = ($from_month .. $to_month);
221 3         5 my $dt;
222 3         9 while (! $dt) {
223 3         81 my $random_month = $possible_months[int(rand(scalar @possible_months))];
224 3         6 $dt = eval {
225 3         12 DateTime->new(
226             'day' => $day,
227             'month' => $random_month,
228             'year' => $year,
229             );
230             };
231             }
232              
233 3         946 return $dt;
234             }
235              
236             # DateTime object for day defined by day, month and year.
237             sub random_day_month_year {
238 22     22 1 332 my ($self, $day, $month, $year) = @_;
239              
240 22         108 $self->_check_day($day);
241 13         25 my $dt = eval {
242 13         51 DateTime->new(
243             'day' => $day,
244             'month' => $month,
245             'year' => $year,
246             );
247             };
248 13 100       6465 if ($EVAL_ERROR) {
249 3         6433 err 'Cannot create DateTime object.',
250             'Error', $EVAL_ERROR;
251             }
252              
253 10 100       61 if (DateTime->compare($self->{'dt_from'}, $dt) == 1) {
254             err "Begin of expected month is lesser than minimal date.",
255             'Expected year', $year,
256             'Expected month', $month,
257             'Expected day', $day,
258             'Minimal year', $self->{'dt_from'}->year,
259             'Minimal month', $self->{'dt_from'}->month,
260 3         423 'Minimal day', $self->{'dt_from'}->day,
261             ;
262             }
263              
264 7 100       887 if (DateTime->compare($dt, $self->{'dt_to'}) == 1) {
265             err "End of expected month is greater than maximal date.",
266             'Expected year', $year,
267             'Expected month', $month,
268             'Expected day', $day,
269             'Maximal year', $self->{'dt_to'}->year,
270             'Maximal month', $self->{'dt_to'}->month,
271 3         376 'Maximal day', $self->{'dt_to'}->day,
272             ;
273             }
274              
275 4         452 return $dt;
276             }
277              
278             # Random DateTime object for day defined by month.
279             sub random_month {
280 8     8 1 71 my ($self, $month) = @_;
281              
282 8         33 my @possible_years = ($self->{'dt_from'}->year .. $self->{'dt_to'}->year);
283 8 50       167 if ($month > $self->{'dt_to'}->month) {
284 8         79 pop @possible_years;
285             }
286 8         154 my $random_year_index = int(rand(scalar @possible_years));
287 8         21 my $random_year = $possible_years[$random_year_index];
288              
289 8         43 return $self->random_month_year($month, $random_year);
290             }
291              
292             # Random DateTime object for day defined by month and year.
293             sub random_month_year {
294 21     21 1 225 my ($self, $month, $year) = @_;
295              
296 21         56 my $after = eval {
297 21         88 DateTime->new(
298             'day' => 1,
299             'month' => $month,
300             'year' => $year,
301             );
302             };
303 21 100       12743 if ($EVAL_ERROR) {
304 6         14813 err 'Cannot create DateTime object.',
305             'Error', $EVAL_ERROR;
306             }
307              
308 15 100       74 if (DateTime->compare($self->{'dt_from'}, $after) == 1) {
309             err "Begin of expected month is lesser than minimal date.",
310             'Expected year', $year,
311             'Expected month', $month,
312             'Minimal year', $self->{'dt_from'}->year,
313 3         423 'Minimal month', $self->{'dt_from'}->month,
314             ;
315             }
316              
317 12         1513 my $before = $after->clone;
318 12         211 $before->add(months => 1)->subtract(days => 1);
319              
320 12 100       35879 if (DateTime->compare($before, $self->{'dt_to'}) == 1) {
321             err "End of expected month is greater than maximal date.",
322             'Expected year', $year,
323             'Expected month', $month,
324             'Maximal year', $self->{'dt_to'}->year,
325 3         414 'Maximal month', $self->{'dt_to'}->month,
326             ;
327             }
328              
329 9         1174 my $daily = DateTime::Event::Recurrence->daily;
330 9         6297 return $daily->next(DateTime::Event::Random->datetime(
331             'after' => $after,
332             'before' => $before,
333             ));
334             }
335              
336             # Random DateTime object for day defined by year.
337             sub random_year {
338 10     10 1 97 my ($self, $year) = @_;
339              
340 10 100       51 if ($self->{'dt_from'}->year > $year) {
341             err "Year is lesser than minimal year.",
342             'Expected year', $year,
343 3         30 'Minimal year', $self->{'dt_from'}->year,
344             ;
345             }
346 7 100       68 if ($self->{'dt_to'}->year < $year) {
347             err "Year is greater than maximal year.",
348             'Expected year', $year,
349 3         31 'Maximal year', $self->{'dt_to'}->year,
350             ;
351             }
352              
353 4         92 my $daily = DateTime::Event::Recurrence->daily;
354              
355 4         3030 return $daily->next(DateTime::Event::Random->datetime(
356             'after' => DateTime->new(
357             'day' => 1,
358             'month' => 1,
359             'year' => $year,
360             ),
361             'before' => DateTime->new(
362             'day' => 31,
363             'month' => 12,
364             'year' => $year,
365             ),
366             ));
367             }
368              
369             # Check day.
370             sub _check_day {
371 69     69   166 my ($self, $day) = @_;
372              
373 69 100       524 if ($day !~ m/^\d+$/ms) {
374 20         110 err "Day isn't positive number.";
375             }
376 49 100       173 if ($day == 0) {
377 10         57 err 'Day cannot be a zero.';
378             }
379 39         100 return;
380             }
381              
382             1;
383              
384             __END__
385              
386             =pod
387              
388             =encoding utf8
389              
390             =head1 NAME
391              
392             Random::Day - Class for random day generation.
393              
394             =head1 SYNOPSIS
395              
396             use Random::Day;
397              
398             my $obj = Random::Day->new(%params);
399             my $dt = $obj->get;
400             my $dt = $obj->random;
401             my $dt = $obj->random_day($day);
402             my $dt = $obj->random_day_month($day, $month);
403             my $dt = $obj->random_day_month_year($day, $month, $year);
404             my $dt = $obj->random_day_year($day, $year);
405             my $dt = $obj->random_month($month);
406             my $dt = $obj->random_month_year($month, $year);
407             my $dt = $obj->random_year($year);
408              
409             =head1 METHODS
410              
411             =head2 C<new>
412              
413             my $obj = Random::Day->new(%params);
414              
415             Constructor.
416              
417             =over 8
418              
419             =item * C<day>
420              
421             Day.
422              
423             Default value is undef.
424              
425             =item * C<dt_from>
426              
427             DateTime object from.
428              
429             Default value is DateTime object for 1900 year.
430              
431             =item * C<dt_to>
432              
433             DateTime object to.
434              
435             Default value is DateTime object for 2050 year.
436              
437             =item * C<month>
438              
439             Month.
440              
441             Default value is undef.
442              
443             =item * C<year>
444              
445             Year.
446              
447             Default value is undef.
448              
449             =back
450              
451             Returns instance of object.
452              
453             =head2 C<get>
454              
455             my $dt = $obj->get;
456              
457             Get random date defined by constructor parameters.
458              
459             Returns DateTime object for date.
460              
461             =head2 C<random>
462              
463             my $dt = $obj->random;
464              
465             Get random date.
466              
467             Returns DateTime object for date.
468              
469             =head2 C<random_day>
470              
471             my $dt = $obj->random_day($day);
472              
473             Get random date defined by day.
474              
475             Returns DateTime object for date.
476              
477             =head2 C<random_day_month>
478              
479             my $dt = $obj->random_day_month($day, $month);
480              
481             Get random date defined by day and month.
482              
483             Returns DateTime object for date.
484              
485             =head2 C<random_day_year>
486              
487             my $dt = $obj->random_day_year($day, $year);
488              
489             Get random date defined by day and year.
490              
491             Returns DateTime object for date.
492              
493             =head2 C<random_day_month_year>
494              
495             my $dt = $obj->random_day_month_year($day, $month, $year);
496              
497             Get random date defined by day, month and year.
498              
499             Returns DateTime object for date.
500              
501             =head2 C<random_month>
502              
503             my $dt = $obj->random_month($month);
504              
505             Get random date defined by month.
506              
507             Returns DateTime object for date.
508              
509             =head2 C<random_month_year>
510              
511             my $dt = $obj->random_month_year($month, $year);
512              
513             Get random date defined by month and year.
514              
515             Returns DateTime object for date.
516              
517             =head2 C<random_year>
518              
519             my $dt = $obj->random_year($year);
520              
521             Get random date defined by year.
522              
523             Returns DateTime object for date.
524              
525             =head1 ERRORS
526              
527             new():
528             From Class::Utils::set_params():
529             Unknown parameter '%s'.
530             From Mo::utils::check_isa():
531             Parameter 'dt_from' must be a 'DateTime' object.
532             Value: %s
533             Reference: %s
534             Parameter 'dt_to' must be a 'DateTime' object.
535             Value: %s
536             Reference: %s
537             Parameter 'dt_from' must have older or same date than 'dt_to'.
538             Date from: %s
539             Date to: %s
540             Parameters 'dt_from' and 'dt_to' are in the same day and not on begin.
541             Date from: %s
542             Date to: %s
543              
544             random_day():
545             Day cannot be a zero.
546             Day isn't positive number.
547              
548             random_day_month():
549             Cannot create DateTime object.
550             Day cannot be a zero.
551             Day isn't positive number.
552              
553             random_day_month_year():
554             Begin of expected month is lesser than minimal date.
555             Expected year: %s
556             Expected month: %s
557             Expected day: %s
558             Minimal year: %s
559             Minimal month: %s
560             Minimal day: %s
561             Cannot create DateTime object.
562             Error: %s
563             Day cannot be a zero.
564             Day isn't positive number.
565             End of expected month is greater than maximal date.
566             Expected year: %s
567             Expected month: %s
568             Expected day: %s
569             Maximal year: %s
570             Maximal month: %s
571             Maximal day: %s
572              
573             random_day_year():
574             Day cannot be a zero.
575             Day is greater than maximal possible date.
576             Day is greater than possible day.
577             Day: %s
578             Day is lesser than minimal possible date.
579             Day isn't positive number.
580             Day not fit between start and end dates.
581             Year is lesser than minimal year.
582             Expected year: %s
583             Minimal year: %s
584             Year is greater than maximal year.
585             Expected year: %s
586             Maximal year: %s
587              
588             random_month():
589             Cannot create DateTime object.
590             Error: %s
591              
592             random_month_year():
593             Begin of expected month is lesser than minimal date.
594             Expected year: %s
595             Expected month: %s
596             Minimal year: %s
597             Minimal month: %s
598             Cannot create DateTime object.
599             Error: %s
600             End of expected month is greater than maximal date.
601             Expected year: %s
602             Expected month: %s
603             Maximal year: %s
604             Maximal month: %s
605              
606             random_year():
607             Year is greater than maximal year.
608             Expected year: %s
609             Maximal year: %s
610             Year is lesser than minimal year.
611             Expected year: %s
612             Minimal year: %s
613              
614             =head1 EXAMPLE
615              
616             =for comment filename=get_random_day.pl
617              
618             use strict;
619             use warnings;
620              
621             use Random::Day;
622              
623             # Object.
624             my $obj = Random::Day->new;
625              
626             # Get date.
627             my $dt = $obj->get;
628              
629             # Print out.
630             print $dt->ymd."\n";
631              
632             # Output like:
633             # \d\d\d\d-\d\d-\d\d
634              
635             =head1 DEPENDENCIES
636              
637             L<Class::Utils>,
638             L<DateTime>,
639             L<DateTime::Event::Random>,
640             L<DateTime::Event::Recurrence>,
641             L<English>,
642             L<Error::Pure>,
643             L<Mo::utils>.
644              
645             =head1 SEE ALSO
646              
647             =over
648              
649             =item L<Data::Random>
650              
651             Perl module to generate random data
652              
653             =item L<DateTime::Event::Random>
654              
655             DateTime extension for creating random datetimes.
656              
657             =item L<Random::Day::InTheFuture>
658              
659             Class for random day generation in the future.
660              
661             =item L<Random::Day::InThePast>
662              
663             Class for random day generation in the past.
664              
665             =back
666              
667             =head1 REPOSITORY
668              
669             L<https://github.com/michal-josef-spacek/Random-Day>
670              
671             =head1 AUTHOR
672              
673             Michal Josef Špaček L<mailto:skim@cpan.org>
674              
675             L<http://skim.cz>
676              
677             =head1 LICENSE AND COPYRIGHT
678              
679             © 2013-2025 Michal Josef Špaček
680              
681             BSD 2-Clause License
682              
683             =head1 VERSION
684              
685             0.17
686              
687             =cut