From f21070544f411020875b478cda8b2ecf3d4c75dc Mon Sep 17 00:00:00 2001 From: Don Armstrong Date: Sat, 4 Feb 2006 00:23:04 +0000 Subject: [PATCH] [svn-inject] Tagging upstream source version of libhtml-calendarmonth-perl --- 1.16/Changes | 99 ++ 1.16/MANIFEST | 22 + 1.16/Makefile.PL | 53 + 1.16/README | 72 + 1.16/lib/HTML/CalendarMonth.pm | 1309 +++++++++++++++++ 1.16/lib/HTML/CalendarMonth/DateTool.pm | 318 ++++ 1.16/lib/HTML/CalendarMonth/DateTool/Cal.pm | 39 + .../HTML/CalendarMonth/DateTool/DateCalc.pm | 65 + .../HTML/CalendarMonth/DateTool/DateManip.pm | 71 + .../HTML/CalendarMonth/DateTool/DateTime.pm | 84 ++ .../HTML/CalendarMonth/DateTool/TimeLocal.pm | 39 + 1.16/lib/HTML/CalendarMonth/Locale.pm | 302 ++++ 1.16/t/00_basic.t | 6 + 1.16/t/01_autodetect.t | 15 + 1.16/t/02_timelocal.t | 20 + 1.16/t/03_datetime.t | 21 + 1.16/t/04_datemanip.t | 21 + 1.16/t/05_datecalc.t | 21 + 1.16/t/06_cal.t | 21 + 1.16/t/20_i8n.t | 29 + 1.16/t/test.dat | 101 ++ 1.16/t/testload.pm | 131 ++ 22 files changed, 2859 insertions(+) create mode 100644 1.16/Changes create mode 100644 1.16/MANIFEST create mode 100644 1.16/Makefile.PL create mode 100644 1.16/README create mode 100644 1.16/lib/HTML/CalendarMonth.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool/Cal.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool/DateCalc.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool/DateManip.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool/DateTime.pm create mode 100644 1.16/lib/HTML/CalendarMonth/DateTool/TimeLocal.pm create mode 100644 1.16/lib/HTML/CalendarMonth/Locale.pm create mode 100755 1.16/t/00_basic.t create mode 100755 1.16/t/01_autodetect.t create mode 100755 1.16/t/02_timelocal.t create mode 100755 1.16/t/03_datetime.t create mode 100755 1.16/t/04_datemanip.t create mode 100755 1.16/t/05_datecalc.t create mode 100755 1.16/t/06_cal.t create mode 100755 1.16/t/20_i8n.t create mode 100644 1.16/t/test.dat create mode 100644 1.16/t/testload.pm diff --git a/1.16/Changes b/1.16/Changes new file mode 100644 index 0000000..8729a7e --- /dev/null +++ b/1.16/Changes @@ -0,0 +1,99 @@ +Revision history for Perl extension HTML-CalendarMonth. + +1.16 Fri Oct 21 16:23:48 EDT 2005 + - Polished tests + - Fixed a Date::Calc testing procedure + +1.15 Fri Apr 1 12:43:09 EST 2005 + - Split out tests + - Version roll hopefully ironed out some cpan tester + dependency issues + +1.14 Mon Mar 28 15:32:54 EST 2005 + - forced dependency on HTML::ElementTable 1.13 or greater + - minor brush ups + +1.13 Mon Feb 28 16:02:31 EST 2005 + - Streamlined accessor/mutator logic. Uses Class::Accessor now. + - Fixed a week of year bug introduced in the recent changes. + - General cleanup + - Added more tests (including one i8n test) + +1.12 Mon Feb 28 00:31:16 EST 2005 + - Fixed auto-select bug that slipped through tests. :( + - Fixed tests. + +1.11 Sun Feb 27 23:58:04 EST 2005 + - No reason to reinvent the wheel. Language support has now been + entirely replaced with full locale support as provided by + DateTime::Locale (does not require installation of entire + DateTime suite, if that's an issue) + - Calendrical calculations have now been fully abstracted out to + a back end interface. Calendars can be generated given the + presence of any one of the following and subject to the + circumstances of the request: + + * native Time::Local (but limited to dates between 1970 and + 2038) + * Date::Calc + * DateTime + * Date::Manip + * unix 'cal' command + +1.10 Sat Feb 26 00:47:37 EST 2005 + - Added more robust language support. Currently supports en, + de, and fr. + +1.09 Tue Mar 26 05:21:59 CST 2002 + - Fixed obscure bug that caused March 31 2002 + to be dropped; related to a localtime/gmtime + issue with DST effects. + - Added tests for calendars over 1-year span + plus special cases such as the aforementioned + March 31 2002 instance. + +1.08 Mon Jan 8 19:15:16 CST 2001 + - Added Date::Manip fallback from Date::Calc, + so a C compiler is not strictly necessary + for folks wanting week-of-year numbering + or exotic dates. + +1.07 Wed Nov 8 01:42:40 CST 2000 + - Day-of-week bug REALLY fixed. Should now work + properly with all perumutations of calendar + pecularities and concepts of what the first + day of the week should be. + +1.06 Wed Oct 4 13:37:31 CDT 2000 + - Day-of-week bug fixed for cases where Sunday is + the first day of the month (such as Oct, 2000), + or last day of the month (such as Dec, 2000), + over various configurations of what is considered + the first day of the week. + +1.04 Sun Sep 17 12:13:40 CDT 2000 + - Documentation tweaks and corrections. + +1.03 Wed Apr 26 12:06:23 CDT 2000 + - Added HTML::ElementTable dependency check in Makefile.PL + - Various bug fixes, under less common circumstances. + - Code syntax tweaks. + +1.02 Tue Jan 25 20:05:05 CST 2000 + - Cleaned up -w noise + - Added mailing list information + +1.01 Thu Sep 16 15:33:41 CDT 1999 + - Distribution patch + - Purged erroneus Date::Manip references + +1.00 Tue Jul 27 21:55:43 CDT 1999 + - abstracted item/coord translations to cell refs + rather than redundant coord lookups + - initial release + +0.09 Thu Jul 8 19:02:39 CDT 1999 + - added week counts and arbitrary 1st DOW + +0.08 Fri Jul 10 19:58:28 1998 + - first public version diff --git a/1.16/MANIFEST b/1.16/MANIFEST new file mode 100644 index 0000000..aa93d09 --- /dev/null +++ b/1.16/MANIFEST @@ -0,0 +1,22 @@ +README +Changes +MANIFEST +Makefile.PL +lib/HTML/CalendarMonth.pm +lib/HTML/CalendarMonth/Locale.pm +lib/HTML/CalendarMonth/DateTool.pm +lib/HTML/CalendarMonth/DateTool/TimeLocal.pm +lib/HTML/CalendarMonth/DateTool/Cal.pm +lib/HTML/CalendarMonth/DateTool/DateCalc.pm +lib/HTML/CalendarMonth/DateTool/DateManip.pm +lib/HTML/CalendarMonth/DateTool/DateTime.pm +t/00_basic.t +t/01_autodetect.t +t/02_timelocal.t +t/03_datetime.t +t/04_datemanip.t +t/05_datecalc.t +t/06_cal.t +t/20_i8n.t +t/test.dat +t/testload.pm diff --git a/1.16/Makefile.PL b/1.16/Makefile.PL new file mode 100644 index 0000000..0d0efd3 --- /dev/null +++ b/1.16/Makefile.PL @@ -0,0 +1,53 @@ +# Check for non-standard modules used by this module. +use ExtUtils::MakeMaker; + +$DISTNAME = "HTML-CalendarMonth"; +$NAME = "HTML::CalendarMonth"; + +$| = 1; + +print "Checking for DateTime, Date::Calc or Date::Manip ... "; +unless (eval "require DateTime" || + eval "require Date::Calc" || + eval "require Date::Manip") { + print " failed\n"; + print < $DISTNAME, + NAME => $NAME, + VERSION_FROM => 'lib/HTML/CalendarMonth.pm', + PREREQ_PM => { + Time::Local => 0, + HTML::ElementTable => 1.13, + DateTime::Locale => 0, + }, + dist => { + COMPRESS => 'gzip -9f', + SUFFIX => 'gz', + }, + ); diff --git a/1.16/README b/1.16/README new file mode 100644 index 0000000..42df8e0 --- /dev/null +++ b/1.16/README @@ -0,0 +1,72 @@ +HTML-CalendarMonth +------------------- + +HTML::CalendarMonth is a module that simplifies the rendering of a +calendar month in HTML. It is NOT a scheduling system. + +Calendars are represented as HTML::Element based structures, derived +from the HTML::ElementTable class. + +The module includes support for 'week of the year' numbering, arbitrary +1st day of the week definitions, and aliasing so that you can express +any element in any language HTML can handle. + +If you wish to use 'week of the year' numbering, or want to explore +dates beyond the capability of the internal perl time functions, then +you will need Date::Calc or Date::Manip. + +INSTALLATION + +You install HTML-Calendar, as you would install any perl module library, +by running these commands: + + perl Makefile.PL + make + make test + make install + +DOCUMENTATION + +See HTML/CalendarMonth.pm for the code. See Changes for recent changes. +POD style documentation is included in the module. This is normally +converted to a manual page and installed as part of the "make install" +process. You should also be able to use the 'perldoc' utility to extract +and read documentation from the module directly. + +Some examples can be found here: + + http://www.mojotoad.com/sisk/projects/HTML-CalendarMonth/examples.html + +SUPPORT + +There is a mailing list for HTML::Calendar. To subscribe or view past +messages, please visit the following URL: + + http://lists.sourceforge.net/mailman/listinfo/html-calmonth-general + +Questions and comments may also be directed to Matt Sisk + + +AVAILABILITY + +The package is available from CPAN: + + http://www.cpan.org/authors/id/M/MS/MSISK/ + +The package is also available at the Toadstool: + + http://www.mojotoad.com/sisk/projects/HTML-CalendarMonth/ + +ACKNOWLEDGMENTS + +Thanks to William R. Ward for some conceptual nudging. Thanks to Fabian +Aichele, Jarkko Hietaniemi, Wolfgang Jürgensen, and David 'Sniper' +Rigaudiere for some suggestions on global calendar customs. Thanks to +Gael Marziou, Raul Rivero, T. Bugra Uytun, and Philipp W. for some +helpful bug spotting. + +COPYRIGHT + +Copyright (c) 1999-2005 Matthew P. Sisk. All rights reserved. All wrongs +revenged. This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. diff --git a/1.16/lib/HTML/CalendarMonth.pm b/1.16/lib/HTML/CalendarMonth.pm new file mode 100644 index 0000000..b378c2a --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth.pm @@ -0,0 +1,1309 @@ +package HTML::CalendarMonth; + +use strict; +use vars qw($VERSION @ISA); + +$VERSION = '1.16'; + +use Carp; + +use HTML::ElementTable 1.13; +use HTML::CalendarMonth::Locale; +use HTML::CalendarMonth::DateTool; + +@ISA = qw(HTML::CalendarMonth::Accessor HTML::ElementTable); + +# Calmonth attribute method overrides + +sub row_offset { + # Displace calendar how many rows into table? + my $self = shift; + if (@_) { + $_[0] >= 0 or croak "Offset must be zero or more"; + } + $self->SUPER::row_offset(@_); +} + +sub col_offset { + # Displace calendar how many columns into table? + my $self = shift; + if (@_) { + $_[0] >= 0 or croak "Offset must be zero or more"; + } + $self->SUPER::col_offset(@_); +} + +sub item_alias { + my($self, $item) = splice(@_, 0, 2); + defined $item or croak "Item name required"; + $self->alias->{$item} = shift if @_; + $self->alias->{$item} || $item; +} + +sub item_aliased { + my($self, $item) = splice(@_, 0, 2); + defined $item or croak "Item name required.\n"; + defined $self->alias->{$item}; +} + +# Header Toggles + +sub _head { + # Set/test entire heading (month,year,and dow headers) (does not + # affect week number column). Return true if either heading active. + my $self = shift; + $self->head_m(@_) && $self->head_dow(@_) if @_; + $self->_head_my || $self->head_dow; +} + +sub _head_my { + # Set/test month and year header mode + my($self, $mode) = splice(@_, 0, 2); + $self->head_m($mode) && $self->head_y($mode) if defined $mode; + $self->head_m || $self->head_y; +} + +sub _initialized { + my $self = shift; + @_ ? $self->{_initialized} = shift : $self->{_initialized}; +} + +# Circa Interface + +sub _date { + # Set target month, year + my $self = shift; + if (@_) { + my ($month, $year) = @_; + $month && defined $year || croak "date method requires month and year"; + croak "Date already set" if $self->_initialized(); + + # get rid of possible leading 0's + $month += 0; + $year += 0; + + $month <= 12 && $month >= 1 or croak "Month $month out of range (1-12)\n"; + $year > 0 or croak "Negative years are unacceptable\n"; + + $self->month($self->monthname($month)); + $self->year($year); + $month = $self->monthnum($month); + + # Trigger _gencal...this should be the only place where this occurs + $self->_gencal; + } + return($self->month, $self->year); +} + +# locale accessors +sub locales { shift->loc->locales } +sub locale_days { shift->loc->days } +sub locale_daynum { shift->loc->daynum(@_) } +sub locale_months { shift->loc->months } +sub locale_daynums { shift->loc->daynums } +sub locale_minmatch { shift->loc->minmatch } +sub locale_monthnum { shift->loc->monthnum(@_) } +sub locale_monthnums { shift->loc->monthnums } +sub locale_minmatch_pattern { shift->loc->minmatch_pattern } + +# class factory access + +sub class_element_table { 'HTML::ElementTable' } +sub class_datetool { __PACKAGE__ . '::DateTool' } +sub class_locale { __PACKAGE__ . '::Locale' } + +sub _gencal { + # Generate internal calendar representation + my $self = shift; + + # New calendar...clobber day-specific settings + my $itoc = $self->_itoc({}); + + # Figure out dow of 1st day of the month as well as last day of the + # month (uses date calculator backends) + $self->_anchor_month(); + + # row count for weeks in grid + my ($wcnt) = 0; + + my ($dowc) = $self->dow1st; + my $skips = $self->_caltool->skips; + + # For each day + foreach (1 .. $self->lastday) { + next if $skips->{$_}; + my $r = $wcnt + 2 + $self->row_offset; + my $c = $dowc + $self->col_offset; + # This is a bootstrap until we know the number of rows in the month. + $itoc->{$_} = [$r, $c]; + $dowc = ++$dowc % 7; + ++$wcnt unless $dowc || $_ == $self->lastday; + } + + $self->{_week_rows} = $wcnt; + + my $row_extent = $wcnt + 2; + my $col_extent = 6; + $col_extent += 1 if $self->head_week; + + $self->extent($row_extent + $self->row_offset, + $col_extent + $self->col_offset); + + # Table can contain the days now, so replace our bootstrap coordinates + # with references to the actual elements. + foreach (keys %$itoc) { + my $cellref = $self->cell(@{$itoc->{$_}}); + $self->itoc($_, $cellref); + $self->ctoi($cellref, $_); + } + + # week num affects month/year spans + my $width = $self->head_week ? 8 : 7; + + # month/year headers + my $cellref = $self->cell($self->row_offset, $self->col_offset); + $self->itoc($self->month, $cellref); + $self->ctoi($cellref, $self->month); + $cellref = $self->cell($self->row_offset, + $width - $self->year_span + $self->col_offset); + $self->itoc($self->year, $cellref); + $self->ctoi($cellref, $self->year); + + $self->item($self->month)->replace_content($self->item_alias($self->month)); + $self->item($self->year)->replace_content($self->item_alias($self->year)); + + if ($self->_head_my) { + if ($self->head_m) { + $self->item($self->month)->attr('colspan',$width - $self->year_span); + } + else { + $self->item($self->month)->mask(1); + $self->item($self->year)->attr('colspan', $width); + } + if ($self->head_y) { + $self->item($self->year)->attr('colspan',$self->year_span); + } + else { + $self->item($self->year)->mask(1); + $self->item($self->month)->attr('colspan', $width); + } + } + else { + $self->row($self->first_row)->mask(1); + } + + # DOW headers + my $trans; + my $days = $self->locale_days; + foreach (0..$#$days) { + # Transform for week_begin 1..7 + $trans = ($_ + $self->week_begin - 1) % 7; + my $cellref = $self->cell(1 + $self->row_offset, $_ + $self->col_offset); + $self->itoc($days->[$trans], $cellref); + $self->ctoi($cellref, $days->[$trans]); + } + if ($self->head_dow) { + grep($self->item($_)->replace_content($self->item_alias($_)), @$days); + } + else { + $self->row($self->first_row + 1)->mask(1); + } + + # Week number column + if ($self->head_week) { + # Week nums can collide with days. Use "w" in front of the number + # for uniqueness, and automatically alias to just the number (unless + # already aliased, of course). + $self->_gen_week_nums(); + my $ws; + my $row_count = $self->first_week_row; + foreach ($self->_numeric_week_nums) { + $ws = "w$_"; + $self->item_alias($ws, $_) unless $self->item_aliased($ws); + my $cellref = $self->cell($row_count, $self->last_col); + $self->itoc($ws, $cellref); + $self->ctoi($cellref, $ws); + $self->item($ws)->replace_content($self->item_alias($ws)); + ++$row_count; + } + } + + # Fill in days of the month + my $i; + foreach my $r ($self->first_week_row .. $self->last_row) { + foreach my $c ($self->first_col .. $self->last_week_col) { + $self->cell($r,$c)->replace_content($self->item_alias($i)) + if ($i = $self->item_at($r,$c)); + } + } + + # Defaults + $self->table->attr(align => 'center'); + $self->item($self->month)->attr(align => 'left') if $self->head_m; + $self->attr(bgcolor => 'white') unless defined $self->attr('bgcolor'); + $self->attr(border => 1) unless defined $self->attr('border'); + $self->attr(cellspacing => 0) unless defined $self->attr('cellspacing'); + $self->attr(cellpadding => 0) unless defined $self->attr('cellpadding'); + + $self; +} + +sub _anchor_month { + # Figure out what our month grid looks like. + # Let HTML::CalendarMonth::DateTool determine which method is + # appropriate. + my $self = shift; + + my $month = $self->monthnum($self->month); + my $year = $self->year; + + my $tool = $self->_caltool; + if (!$tool) { + $tool = $self->class_datetool->new( + year => $year, + month => $month, + weeknum => $self->head_week, + historic => $self->historic, + datetool => $self->datetool, + ); + $self->_caltool($tool); + } + my $dow1st = $tool->dow1st; + my $lastday = $tool->lastday; + + # If the first day of the week is not Sunday... + $dow1st = ($dow1st - ($self->week_begin - 1)) % 7; + + $self->dow1st($dow1st); + $self->lastday($lastday); + + $self; +} + +sub _gen_week_nums { + # Generate week-of-the-year numbers. The first week is generally + # agreed upon to be the week that contains the 4th of January. + # + # For purposes of shenanigans with 'week_begin', we anchor the week + # number off of Thursday in each row. + + my $self = shift; + + my($year, $month, $lastday) = ($self->year, $self->monthnum, $self->lastday); + + my $tool = $self->_caltool; + $tool->can('week_of_year') + or croak "Oops. $tool not set up for week of year calculations.\n"; + + my $fdow = $self->dow1st; + my $delta = 4 - $fdow; + if ($delta < 0) { + $delta += 7; + } + my @ft = $tool->add_days($delta, 1); + + my $ldow = $tool->dow($lastday); + $delta = 4 - $ldow; + if ($delta > 0) { + $delta -= 7; + } + my @lt = $tool->add_days($delta, $lastday); + + my $fweek = $tool->week_of_year(@ft); + my $lweek = $tool->week_of_year(@lt); + my @wnums = $fweek .. $lweek; + + # Do we have days above our first Thursday? + if ($self->row_of($ft[0]) != $self->first_week_row) { + unshift(@wnums, $wnums[0] -1); + } + + # Do we have days below our last Thursday? + if ($self->row_of($lt[0]) != $self->last_row) { + push(@wnums, $wnums[-1] + 1); + } + + # First visible week is from last year + if ($wnums[0] == 0) { + $wnums[0] = $tool->week_of_year($tool->add_days(-7, $ft[0])); + } + + # Last visible week is from subsequent year + if ($wnums[-1] > $lweek) { + $wnums[-1] = $tool->week_of_year($tool->add_days(7, $lt[0])); + } + + $self->_weeknums(@wnums); +} + +# Month hooks + +sub row_items { + # Given a list of items, return all items in rows shared by the + # provided items. + my $self = shift; + my($item,$row,$col,$i,@i,%i); + foreach $item (@_) { + $row = ($self->coords_of($item))[0]; + foreach $col ($self->first_col .. $self->last_col) { + $i = $self->item_at($row,$col) || next; + ++$i{$i}; + } + } + @i = keys %i; + @i ? @i : $i[0]; +} + +sub col_items { + # Return all item cells in the columns occupied by the provided list + # of items. + my $self = shift; + $self->_col_items($self->first_row,$self->last_row,@_); +} + +sub daycol_items { + # Same as col_items(), but excludes header cells. + my $self = shift; + $self->_col_items($self->first_week_row,$self->last_row,@_); +} + +sub _col_items { + # Given row bounds and a list of items, return all item elements + # in the columns occupied by the provided items. Does not return + # empty cells. + my($self, $rfirst, $rlast) = splice(@_, 0, 3); + my($item, $row, $col, %i); + foreach $item (@_) { + $col = ($self->coords_of($item))[1]; + foreach $row ($rfirst .. $rlast) { + my $i = $self->item_at($row,$col) || next; + ++$i{$i}; + } + } + my @i = keys %i; + $#i ? @i : $i[0]; +} + +sub daytime { + # Return seconds since epoch for a given day + my($self, $day) = splice(@_, 0, 2); + $day or croak "Must specify day of month"; + croak "Day does not exist" unless $self->_daycheck($day); + $self->_caltool->day_epoch($day); +} + +sub week_nums { + # Return list of all week numbers + map("w$_", shift->_numeric_week_nums); +} + +sub _numeric_week_nums { + # Return list of all week numbers as numbers + my $self = shift; + $self->head_week ? @{$self->_weeknums} : (); +} + +sub days { + # Return list of all days of the month (1..$c->lastday). + my $self = shift; + my $skips = $self->_caltool->skips; + grep(!$skips->{$_}, (1 .. $self->lastday)); +} + +sub dayheaders { + # Return list of all day headers (Su..Sa). + shift->locale_days; +} + +sub headers { + # Return list of all headers (month,year,dayheaders) + my $self = shift; + ($self->year, $self->month, $self->dayheaders); +} + +sub items { + # Return list of all items (days, headers) + my $self = shift; + ($self->headers, $self->days); +} + +sub first_col { + # Where is the first column of the calendar within the table? + shift->col_offset(); +} + +sub first_week_col { first_col(@_) } + +sub last_col { + # What's the max col of the calendar? + my $self = shift; + $self->head_week ? $self->last_week_col + 1 : $self->last_week_col; +} + +sub last_week_col { + # What column does the last DOW fall in? Should be the same as + # last_col unless head_week is activated + shift->first_col + 6; +} + +sub first_row { + # Where is the first row of the calendar? + shift->row_offset(); +} + +sub first_week_row { + # Returns the first row containing days of the month. This used to + # take into account whether the header rows were active or not, + # but since masking was implemented this should always be offset 2 + # from the first row (thereby taking into account the month/year + # and DOW rows). + my $w = 2; + shift->first_row + $w; +} + +sub last_row { + # Last row of the calendar + my $self = shift; + return ($self->coords_of($self->lastday))[0]; +} + +sub last_week_row { last_row(@_) } + +# Custom glob interfaces + +sub item { + # Return TD elements containing items + my $self = shift; + @_ || croak "Item(s) must be provided"; + $self->cell(grep(defined $_, map($self->coords_of($_), @_))); +} + +sub item_row { + # Return a glob of the rows of a list of items, including empty cells. + my $self = shift; + $self->_item_row($self->first_col, $self->last_col, @_); +} + +sub item_day_row { + # Same as item_row, but excludes possible week number cells + my $self = shift; + $self->_item_row($self->first_col, $self->last_week_col, @_); +} + +sub _item_row { + # Given column bounds and a list of items, return a glob representing + # the cells in the rows occupied by the provided items, including + # empty cells. + my($self, $cfirst, $clast) = splice(@_, 0, 3); + defined $cfirst && defined $clast or croak "No items provided"; + my($row, $col, @coords); + foreach $row (map($self->row_of($_), @_)) { + foreach $col ($cfirst .. $clast) { + push(@coords, $row, $col); + } + } + $self->cell(@coords); +} + +sub item_week_nums { + # Glob of all week numbers + my $self = shift; + $self->item($self->week_nums); +} + +sub item_col { + # Return a glob of the cols of a list of items, including empty cells. + my $self = shift; + $self->_item_col($self->first_row, $self->last_row, @_); +} + +sub item_daycol { + # Same as item_col(), but excludes header cells. + my $self = shift; + $self->_item_col($self->first_week_row, $self->last_row, @_); +} + +sub _item_col { + # Given row bounds and a list of items, return a glob representing + # the cells in the columns occupied by the provided items, including + # empty cells. + my($self, $rfirst, $rlast) = splice(@_, 0, 3); + defined $rfirst && defined $rlast or croak "No items provided"; + my($row, $col, @coords); + foreach $col (map($self->col_of($_), @_)) { + foreach $row ($rfirst .. $rlast) { + push(@coords, $row, $col); + } + } + $self->cell(@coords); +} + +sub item_box { + # Return a glob of the box defined by two items + my($self, $item1, $item2) = splice(@_, 0, 3); + defined $item1 && defined $item2 or croak "Two items required"; + $self->box($self->coords_of($item1), $self->coords_of($item2)); +} + +sub all { + # Return a glob of all calendar cells, including empty cells. + my $self = shift; + $self->box( $self->first_row => $self->first_col, + $self->last_row => $self->last_col ); +} + +sub alldays { + # Return a glob of all cells other than header cells + my $self = shift; + $self->box( $self->first_week_row => $self->first_col, + $self->last_row => $self->last_week_col ); +} + +sub allheaders { + # Return a glob of all header cells + my $self = shift; + $self->item($self->headers); +} + +# Transformation Methods + +sub coords_of { + # Convert an item into grid coordinates + my $self = shift; + my $ref = $self->itoc(@_); + my @pos = ref $ref ? $ref->position : (); + @pos ? (@pos[$#pos - 1, $#pos]) : (); +} + +sub item_at { + # Convert grid coords into item + my $self = shift; + $self->ctoi($self->cell(@_)); +} + +sub itoc { + # Item to grid + my($self, $item, $ref) = splice(@_, 0, 3); + defined $item or croak "item required"; + my $itoc = $self->_itoc; + if ($ref) { + croak "Reference required" unless ref $ref; + $itoc->{$item} = $ref; + } + $itoc->{$item}; +} + +sub ctoi { + # Cell reference to item + my($self, $refstring, $item) = splice(@_, 0, 3); + defined $refstring or croak "cell id required"; + my $ctoi = $self->_ctoi; + if (defined $item) { + $ctoi->{$refstring} = $item; + } + $ctoi->{$refstring}; +} + +sub row_of { + my $self = shift; + ($self->coords_of(@_))[0]; +} + +sub col_of { + my $self = shift; + ($self->coords_of(@_))[1]; +} + +sub monthname { + # Check/return month...returns name. Accepts 1-12, or Jan..Dec + my $self = shift; + return $self->month unless @_; + my(@mn, $month); + my $months = $self->locale_months; + my $monthnum = $self->locale_monthnums; + my $minmatch = $self->locale_minmatch; + my $mmpat = $self->locale_minmatch_pattern; + + foreach $month (@_) { + if ($month =~ /^\d+$/) { + $month >= 1 && $month <= 12 || return 0; + push(@mn, $months->[$month-1]); + } + else { + if (exists $monthnum->{$month}) { + push(@mn, $month); + } + else { + # Make one last attempt + if ($month =~ /^($mmpat)/) { + push(@mn, $minmatch->{$1}); + } + else { + return undef; + } + } + } + } + $#mn > 0 ? @mn : $mn[0]; +} + +sub monthnum { + # Check/return month, returns number. Accepts 1-12, or Jan..Dec + my $self = shift; + my $monthnum = $self->locale_monthnums; + my @mn; + push(@mn, map(exists $monthnum->{$_} ? + $monthnum->{$_}+1 : undef, $self->monthname(@_))); + $#mn > 0 ? @mn : $mn[0]; +} + +sub dayname { + # Check/return day...returns name. Accepts 1..7, or Su..Sa + my $self = shift; + @_ || croak "Day must be provided"; + my(@dn, $day); + my $days = $self->locale_days; + my $daynum = $self->locale_daynums; + foreach $day (@_) { + if ($day =~ /^\d+$/) { + $day >= 1 && $day <= 7 || return undef; + # week_begin is at least 1, so skew is automatic + push(@dn, $days->[($day - 1 + $self->week_begin - 1) % 8]); + } + else { + $day = ucfirst(lc($day)); + if (exists $daynum->{$day}) { + push(@dn, $day); + } + else { + return undef; + } + } + } + $#dn > 0 ? @dn : $dn[0]; +} + +sub daynum { + # Check/return day number 1..7, returns number. Accepts 1..7, + # or Su..Sa + my $self = shift; + my $daynum = $self->locale_daynums; + my @dn; + push(@dn, map(exists $daynum->{$_} ? + $daynum->{$_}+1 : undef,$self->dayname(@_))); + $#dn > 0 ? @dn : $dn[0]; +} + +# Tests-n-checks + +sub _dayheadcheck { + # Test day head names + my($self, $name) = splice(@_, 0, 2); + $name or croak "Name missing"; + return undef if $name =~ /^\d+$/; + $self->daynum($name); +} + +sub _daycheck { + # Check if an item is a day of the month (1..31) + my($self, $item) = splice(@_, 0, 2); + $item = shift or croak "Item required"; + # Can't just invert _headcheck because coords_of() needs _daycheck, + # and _headcheck uses coords_of() + $item =~ /^\d{1,2}$/ && $item <= 31; +} + +sub _headcheck { + # Check if an item is a header + !_daycheck(@_); +} + +# Constructors/Destructors + +sub new { + my $class = shift; + my %parms = @_; + my(%attrs, %tattrs); + foreach (keys %parms) { + if (__PACKAGE__->is_calmonth_attr($_)) { + $attrs{$_} = $parms{$_}; + } + else { + $tattrs{$_} = $parms{$_}; + } + } + + my $self = __PACKAGE__->class_element_table->new(%tattrs); + bless $self, $class; + + # set defaults + $self->set_defaults; + + # Enable blank cell fill so BGCOLOR shows up by default + # (HTML::ElementTable) + $self->blank_fill(1); + + my $month = delete $attrs{month}; + my $year = delete $attrs{year}; + if (!$month || !$year) { + my ($nmonth,$nyear) = (localtime(time))[4,5]; + ++$nmonth; $nyear += 1900; + $month ||= $nmonth; + $year ||= $nyear; + } + $self->month($month); + $self->year($year); + + # set overrides + $self->$_($attrs{$_}) foreach (keys %attrs); + + $self->loc($self->class_locale->new( + id => $self->locale, + full_days => $self->full_days, + full_months => $self->full_months, + )) or croak "Problem creating locale " . $self->locale . "\n"; + + # For now, this is the only time this will every happen for this + # object. It is now 'initialized'. + $self->_date($month, $year); + + $self; +} + +{ + +package HTML::CalendarMonth::Accessor; + +use strict; +use vars qw($VERSION @ISA); + +$VERSION = '0.01'; + +use Carp; + +use Class::Accessor; + +@ISA = qw(Class::Accessor); + +my %Objects; + +# Default complex attributes +my %Calmonth_Attrs = ( + head_m => 1, # Month heading mode + head_y => 1, # Year heading mode + head_dow => 1, # DOW heading mode + head_week => 0, # European week number mode + year_span => 2, # Default col span of year + + week_begin => 1, # What DOW (1-7) is the 1st DOW? + + historic => 1, # If able to choose, use 'cal' + # rather than Date::Calc, which + # blindly extrapolates Gregorian + + row_offset => 0, # Displacment within table + col_offset => 0, + + alias => {}, # What gets displayed if not + # the default item + + month => '', # These will get initialized + year => '', + + locale => 'en_US', + full_days => 0, + full_months => 1, + + datetool => '', + caltool => '', + + # internal muckety muck + _cal => '', + _itoc => {}, + _ctoi => {}, + _caltool => '', + _weeknums => '', + + dow1st => '', + lastday => '', + loc => '', +); + +__PACKAGE__->mk_accessors(keys %Calmonth_Attrs); + +# Class::Accessor overrides + +sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + foreach (sort keys %Calmonth_Attrs) { + $self->$_($Calmonth_Attrs{$_}); + } + $self; +} + +sub set { + my($self, $key) = splice(@_, 0, 2); + if (@_ == 1) { + $Objects{$self}{$key} = $_[0]; + } + elsif (@_ > 1) { + $Objects{$self}{$key} = [@_]; + } + else { + Carp::confess("Wrong number of arguments received"); + } +} + +sub get { + my $self = shift; + if (@_ == 1) { + return $Objects{$self}{$_[0]}; + } + elsif ( @_ > 1 ) { + return @{$Objects{$self}{@_}}; + } + else { + Carp::confess("Wrong number of arguments received."); + } +} + +sub is_calmonth_attr { shift; exists $Calmonth_Attrs{shift()} } + +sub set_defaults { + my $self = shift; + foreach (keys %Calmonth_Attrs) { + $self->$_($Calmonth_Attrs{$_}); + } + $self; +} + +} # end HTML::CalendarMonth::Accessor + +# Go forth and prosper. +1; + +__END__ + +=head1 NAME + +HTML::CalendarMonth - Perl extension for generating and manipulating HTML calendar months + +=head1 SYNOPSIS + + use HTML::CalendarMonth; + use HTML::AsSubs; + + # Using HTML::AsSubs + $c = HTML::CalendarMonth->new( month => 3, year => 69 ); + $c->item($c->year, $c->month)->attr(bgcolor => 'wheat'); + $c->item($c->year, $c->month)->wrap_content(font({size => '+2'})); + $c->item(12, 16, 28)->wrap_content(strong()); + print $c->as_HTML; + + # Using regular HTML::Element creation + $c2 = HTML::CalendarMonth->new( month => 8, year => 79 ); + $c2->item($c2->year, $c2->month)->attr(bgcolor => 'wheat'); + $f = HTML::Element->new('font', size => '+2'); + $c2->item($c2->year, $c2->month)->wrap_content($f); + $c2->item_daycol('Su', 'Sa')->attr(bgcolor => 'cyan'); + print $c2->as_HTML; + + # Full locale support via DateTime::Locale + $c3 HTML::CalendarMonth->new( month => 8, year => 79, locale => 'fr' ); + print $c3->as_HTML + +=head1 DESCRIPTION + +HTML::CalendarMonth is a subclass of HTML::ElementTable. See +L for how that class works, for it affects this +module on many levels. Like HTML::ElementTable, HTML::CalendarMonth +behaves as if it were an HTML::ElementSuper, which is a regular +HTML::Element with methods added to easily manipulate the appearance of +the HTML table containing the calendar. + +The primary interaction with HTML::CalendarMonth is through I. An +I is merely a symbol that represents the content of the cell of +interest within the calendar. For instance, the element representing the +14th day of the month would be returned by C<$c-Eitem(14)>. +Similarly, the element representing the header for Monday would be +returned by C<$c-Eitem('Mo')>. If the year happened to by 1984, then +C<$c-Eitem(1984)> would return the cell representing the year. Since +years and particular months change frequently, it is probably more +useful to take advantage of the C and C methods, which +return the respective item symbol for the current calendar. In the prior +example, using 1984, the following is equivalent: C<$c-Eitem($c- +Eyear())>. + +Multiple cells of the calendar can be manipulated as if they were a +single element. For instance, C<$c-Eitem(15)-Eattr(bgcolor +=E 'cyan')> would alter the background color of the cell +representing the 15th. By the same token, C<$c-Eitem(15, 16, 17, +23)-Eattr(bgcolor =E 'cyan')> would do the same thing for all +cells containing the item symbols passed to the C method. + +The calendar structure is still nothing more than a table structure; the +same table structure provided by the HTML::ElementTable class. In +addition to the I based access methods above, calendar cells can +still be accessed using row and column grid coordinates using the +C method provided by the table class. All coordinate-based +methods in the table class are accessible to the calendar class. + +The module includes support for week-of-the-year numbering, arbitrary +1st day of the week definitions, and aliasing so that you can express +any element in any language HTML can handle. + +Dates that are beyond the range of the built-in time functions of perl +are handled either by the 'cal' command, Date::Calc, or Date::Manip. The +presence of any one of these utilities and modules will suffice for +these far flung date calculations. If you want to use week-of-year +numbering, then either one of the date modules is required. + +Full locale support is offered via DateTime::Locale. For a full list of +supported locale id's, look at HTML::CalendarMonth::Locale->locales() or +DateTime::Locale->ids(). + +=head1 METHODS + +All arguments appearing in [brackets] are optional, and do not represent +anonymous array references. + +=over + +B + +=item new() + +With no arguments, the constructor will return a calendar object +representing the current month with a default appearance. The initial +configuration of the calendar is controlled by special attributes. Non- +calendar related attributes are passed along to HTML::ElementTable. Any +non-table related attributes left after that are passed to HTML::Element +while constructing the EtableE tag. See L if +you are interested in attributes that can be passed along to that class. + +Special Attributes for HTML::CalendarMonth: + +=over + +=item month + +1-12, or Jan-Dec. Defaults to current month. + +=item year + +Four digit representation. Defaults to current year. + +=item head_m + +Specifies whether to display the month header. Default 1. + +=item head_y + +Specifies whether to display the year header. Default 1. + +=item head_dow + +Specifies whether to display days of the week header. Default 1. + +=item locale + +Specifies a locale in which to render the calendar. Default is 'en_US'. +See L for more information. If for some +reason you prefer to use different labels than those provided by +C, see the C attribute below. + +=item full_days + +Specifies whether or not to use full day names or their abbreviated +names. Default is 0, use abbreviated names. + +=item full_months + +Specifies whether or not to use full month names or their abbriviated +names. Default is 1, use full names. + +=item alias + +Takes a hash reference mapping labels provided by C to any +custom label you prefer. Lookups, such as C, will still use +the locale string, but when the calendar is rendered the aliased value +will appear. + +=item head_week + +Specifies whether to display the week-of-year numbering. Default 0. + +=item week_begin + +Specify first day of the week, which can be 1..7, starting with Sunday. +Defaults to 1, or Sunday. In order to specify Monday, set this to 2, +and so on. + +=item row_offset + +Specifies the offset of the first calendar row within the table +containing the calendar. This is 0 by default, making the first row of +the table the same as the first row of the calendar. + +=item col_offset + +Specifies the offset of the first calendar column within the table +containing the calendar. This is 0 by default, making the first column +of the table the same as the first row of the calendar. + +=item historic + +This option is ignored for dates that do not exceed the range of the built- +in perl time functions. For dates that B exceed these ranges, this +option specifies the default calculation method. When set, if the 'cal' +utility is available on your system, that will be used rather than the +Date::Calc or Date::Manip modules. This can be an issue since the date +modules blindly extrapolate the Gregorian calendar, whereas 'cal' takes +some of these quirks into account. If 'cal' is not available on your +system, this attribute is meaningless. Defaults to 1. + +=back + +=back + +B + +The following methods return lists of item symbols that are related in +some way to the provided list of items. The returned symbols may then +be used as arguments to the glob methods detailed further below. When +these methods deal with 'rows' and 'columns', they are only concerned +with the cells in the calendar -- not the cells that might be present +in the surrounding table if you have extended it. If you have not set +row or column offsets, or extended the span of the containing table, +then these rows and columns are functionally equivalent to the table +rows and columns. + +=over + +=item row_items(item1, [item2, ...]) + +Returns all item symbols in rows shared by the provided item symbols. + +=item col_items(item1, [item2, ...]) + +Returns all item symbols in columns shared by the provided item symbols. + +=item daycol_items(col_item1, [col_item2, ...]) + +Same as col_items(), but the returned item symbols are limited to those +that are not header items (month, year, day-of-week). + +=item row_of(item1, [item2, ...]) + +Returns the row numbers of rows containing the provided item symbols. + +=item col_of(item1, [item2, ...]) + +Returns the column numbers of columns containing the provided +item symbols. + +=item lastday() + +Returns the number of the last day of the month. + +=item dow1st() + +Returns the column number for the first day of the month. + +=item days() + +Returns a list of all days of the month. + +=item dayheaders() + +Returns a list of all day headers (Su..Sa) + +=item headers() + +Returns a list of all headers (month, year, dayheaders) + +=item items() + +Returns a list of all item symbols in the calendar. + +=item first_col() + +Returns the number of the first column of the calendar. This could be +different from that of the surrounding table if the table was extended, +but otherwise should be identical. + +=item last_col() + +Returns the number of the last column of the calendar. This could be +different from that of the surrounding table if the table was extended, +but should otherwise be identical. + +=item first_row() + +Returns the number of the first row of the calendar. This could be +different from that of the surrounding table if offsets were made. + +=item first_week_row() + +Returns the number of the first row of the calendar containing day items +(ie, the first week). This could vary depending on table offsets and +header modes. + +=item last_row() + +Returns the number of the last row of the calendar. This could be +different from that of the surrounding table if the table was extended, +but should otherwise be identical. + +=back + +B + +Glob methods return references that are functionally equivalent to an +individual calendar cell. Mostly, they provide item based analogues to +the glob methods provided in HTML::ElementTable. In methods dealing with +rows, columns, and boxes, the globs include empty calendar cells (which +would otherwise need to be accessed through native HTML::ElementTable +methods). The row and column numbers returned by the item methods above +are compatible with the grid based methods in HTML::ElementTable. + +For details on how these globs work, check out L and +L. + +=over + +=item item(item1, [item2, ...]) + +Returns all cells containing the provided item symbols. + +=item item_row(item1, [item2, ...]) + +Returns all cells in all rows occupied by the provided item symbols. + +=item item_col(item1, [item2, ...]) + +Returns all cells in all columns occupied by the provided item symbols. + +=item item_daycol(item1, [item2, ...]) + +Same as item_col(), except limits the cells to non header cells. + +=item item_box(item1a, item1b, [item2a, item2b, ...]) + +Returns all cells in the boxes defined by the item pairs provided. + +=item allheaders() + +Returns all header cells. + +=item alldays() + +Returns all non header cells, including empty cells. + +=item all() + +Returns all cells in the calendar, including empty cells. + +=back + +B + +The following methods provide ways of translating between various item +symbols, coordinates, and other representations. + +=over + +=item coords_of(item) + +Returns the row and column of the provided item symbol, for use with the +grid based methods in HTML::ElementTable. + +=item item_at(row,column) + +Returns the item symbol of the item at the provided coordinates, for use +with the item based methods of HTML::CalendarMonth. + +=item monthname(monthnum) + +Returns the name (item symbol) of the month number provided, where +I can be 1..12. + +=item monthnum(monthname) + +Returns the number (1..12) of the month name provided. Only a minimal +case-insensitive match on the month name is necessary; the proper item +symbol for the month will be determined from this match. + +=item dayname(daynum) + +Returns the name (item symbol) of the day of week header for a number of +a day of the week, where I is 1..7. + +=item daynum(dayname) + +Returns the number of the day of the week given the symbolic name for +that day (Su..Sa). + +=item daytime(day) + +Returns the number in seconds since the epoch for a given day. The day +must be present in the current calendar. + +=back + +=head1 Notes On Dates And Spatial Relationships + +One of the nice things about having a calendar represented as a table +accessible with grid coordinates is that some of the trickier date +calculations become trivial. You can use packages such as I +or I for these sort of things, but the algorithms are often +derived from a common human activity: looking at a calendar on a wall. +Say, for instance, that you are interested in "the third Friday of every +month". If you are using a calendar with Sunday as the first day of the +week, then Fridays will always be in column 5, starting from 0. +Likewise, due to the fact that supressed headers are merely I in +the actual table, the first row with dates in a calendar structure will +B be 2, even if the month, year, or day headers are disabled. +The third friday of every month therefore becomes C<$c-Ecell(2,5)>, +regardless of the particular month. Likewise, the "nth dayname/week of +the month" can always be mapped to table coordinates. + +The particulars of this grid mapping are affected if you have redefined +what the first day of the week is, or if you have tweaked the table +beyond the bounds of the calendar itself. There are methods that can +help under these circumstances, though. For instance, in our example +where we are interested in the 3rd Friday of the month, the row number +is accessed with C<$c-Efirst_week_row + 2>, whereas the column +number could be derived with C<$c-Elast_col - 1>. + +=head1 REQUIRES + +HTML::ElementTable + +=head1 OPTIONAL + +Date::Calc or Date::Manip (only if you want week-of-year numbering or +non-contemporary dates on a system without the I command) + +=head1 AUTHOR + +Matthew P. Sisk, EFE + +=head1 COPYRIGHT + +Copyright (c) 1998-2005 Matthew P. Sisk. All rights reserved. All wrongs +revenged. This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +A useful page of examples can be found at +http://www.mojotoad.com/sisk/projects/HTML-CalendarMonth. + +For information on iso639 standards for abbreviations for language +names, see http://www.loc.gov/standards/iso639-2/englangn.html + +HTML::ElementTable(3), HTML::Element(3), perl(1) + +=cut diff --git a/1.16/lib/HTML/CalendarMonth/DateTool.pm b/1.16/lib/HTML/CalendarMonth/DateTool.pm new file mode 100644 index 0000000..0e81716 --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool.pm @@ -0,0 +1,318 @@ +package HTML::CalendarMonth::DateTool; + +# Base class for determining what date calculation package to use. + +use strict; +use Carp; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my $DEBUG = 0; + +my %Toolmap = ( + 'Time::Local' => 'TimeLocal', + 'Date::Calc' => 'DateCalc', + 'DateTime' => 'DateTime', + 'Date::Manip' => 'DateManip', + 'cal' => 'Cal', +); + +sub toolmap { + shift; + my $str = shift; + my $tool = $Toolmap{$str}; + unless ($tool) { + foreach (values %Toolmap) { + if ($str =~ /^$_$/i) { + $tool = $_; + last; + } + } + } + return undef unless $tool; + join('::', __PACKAGE__, $tool); +} + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + my %parms = @_; + $self->{year} = $parms{year} or croak "missing year (YYYY)\n"; + $self->{month} = $parms{month} or croak "missing month num (1-12)\n"; + $self->{weeknum} = $parms{weeknum}; + $self->{historic} = $parms{historic}; + if ($parms{datetool}) { + $self->{datetool} = $self->toolmap($parms{datetool}) + or croak "Sorry, didn't find a tool for datetool '$parms{datetool}'\n"; + } + my $dc = $self->_summon_date_class; + unless (eval "require $dc") { + croak "Problem loading $dc ($@)\n"; + } + print STDERR "Using date class $dc\n" if $DEBUG; + # rebless into new class + bless $self, $dc; +} + +sub year { shift->{year} } +sub month { shift->{month} } +sub weeknum { shift->{weeknum} } +sub historic { shift->{historic} } +sub datetool { shift->{datetool} } + +sub cal_cmd { + my $self = shift; + unless (exists $self->{cal_cmd}) { + my $cal; + foreach (qw(/usr/bin /bin /usr/local/bin)) { + if (-x "$_/cal") { + $cal = "$_/cal"; + last; + } + } + $self->{cal_cmd} = $cal || undef; + } + $self->{cal_cmd}; +} + +sub day_epoch { + # in case our subclasses are lazy + my($self, $day, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + Time::Local::timegm(0,0,0,1,$month,$year); +} + +sub skips { + my $self = shift; + @_ ? $self->{skips} = shift : $self->{skips}; +} + +sub dow1st { (shift->dow1st_and_lastday)[0] } + +sub lastday { (shift->dow1st_and_lastday)[1] } + +sub _summon_date_class { + my $self = shift; + return $self->datetool if $self->datetool; + my $dc; + if ( $self->_test_for_timelocal ) { + $dc = __PACKAGE__ . '::TimeLocal'; + } + elsif ( $self->_test_for_cal ) { + $dc = __PACKAGE__ . '::Cal'; + } + elsif ( $self->_test_for_datecalc ) { + $dc = __PACKAGE__ . '::DateCalc'; + } + elsif ( $self->_test_for_datetime ) { + $dc = __PACKAGE__ . '::DateTime'; + } + elsif( $self->_test_for_datemanip ) { + $dc = __PACKAGE__ . '::DateManip'; + } + else { + croak <<__NOTOOL; +No valid date mechanism found. Install Date::Calc, DateTime, or +Date::Manip, or try using a date between 1970 and 2038 so that +Time::Local can be used. +__NOTOOL + } + $dc; +} + +sub _dump_tests { + my $self = shift; + print "Time::Local : ", $self->_test_for_timelocal, "\n"; + print " cal : ", $self->_test_for_cal, "\n"; + print " Date::Calc : ", $self->_test_for_datecalc, "\n"; + print " DateTime : ", $self->_test_for_datetime, "\n"; + print "Date::Manip : ", $self->_test_for_datemanip, "\n"; +} + +sub _test_for_timelocal { + my $self = shift; + my $year = $self->year; + my $weeknum = $self->weeknum; + !$weeknum && eval "require Time::Local" && + (!defined $year || (($year >= 1970) && ($year < 2038))); +} + +sub _test_for_cal { + my $self = shift; + my $weeknum = $self->weeknum; + my $historic = $self->historic; + my $cal = $self->cal_cmd; + !$weeknum && $historic && $cal; +} + +sub _test_for_datecalc { eval "require Date::Calc" } + +sub _test_for_datetime { eval "require DateTime" } + +sub _test_for_datemanip { eval "require Date::Manip" } + +1; + +__END__ + +=head1 NAME + +HTML::CalendarMonth::DateTool - Base class for determining which date package to use for calendrical calculations. + +=head1 SYNOPSIS + + my $date_tool = HTML::CalendarMonth::DateTool->new( + year => $YYYY_year, + month => $one_thru_12_month, + weeknum => $weeknum_mode, + historic => $historic_mode, + datetool => $specific_datetool_if_desired, + ); + +=head1 DESCRIPTION + +This module attempts to utilize the best date calculation package +available on the current system. For most contemporary dates this +usually ends up being the internal Time::Local package of perl. For more +exotic dates, or when week number of the years are desired, other +methods are attempted including DateTime, Date::Calc, Date::Manip, and +the unix 'cal' command. Each of these has a specific subclass of this +module offering the same utility methods needed by HTML::CalendarMonth. + +=head1 METHODS + +=over + +=item new() + +Constructor. Takes the following parameters: + +=over + +=item year + +Year of calendar in question (required). If you are rendering exotic +dates (i.e. dates outside of 1970 to 2038) then something besides +Time::Local will be used for calendrical calculations. + +=item month + +Month of calendar in question (required). 1 through 12. + +=item weeknum + +Optional. When specified, will limit class excursions to those that are +currently set up for week of year calculations. + +=item historic + +Optional. If the 'cal' command is available, use it rather than other available +date modules since the 'cal' command accurately handles some specific +historical artifacts such as the transition from Julian to Gregorian. + +=item datetool + +Optional. Mostly for debugging, this option can be used to indicate a +specific HTML::CalendarMonth::DateTool subclass for instantiation. The +value can be either the actual utility class, e.g., Date::Calc, or the +name of the CalendarMonth handler leaf class, e.g. DateCalc. For the +'cal' command, use 'cal'. + +=back + +=back + +There are number of methods automatically available: + +=over + +=item month() + +=item year() + +=item weeknum() + +=item historical() + +=item datetool() + +Accessors for the parameters provided to C above. + +=item dow1st() + +Returns the day of week number for the 1st of the C and C +specified during the call to C. Relies on the presence of +C. + +=item lastday() + +Returns the last day of the month for the C and C specified +during the call to C. Relies on the presence of +C. + +=back + +=head1 Overridden methods + +Subclasses of this module must provide at least the C and +C methods. + +=over + +=item dow1st_and_lastday() + +Required. Provides a list containing the day of the week of the first day of the +month along with the last day of the month. + +=item day_epoch() + +Optional unless interested in epoch values for wacky dates. For a given +day, and optionally C and C if they are different from +those specified in C, provide the unix epoch in seconds for that +day at midnight. + +=back + +If the subclass is expected to provide week of year numbers, three more +methods are necessary: + +=over + +=item dow() + +For a given day, and optionally C and C if they are +different from those specified in C, provide the day of week +number. (1=Sunday, 6=Saturday). + +=item add_days($days, $delta, $day, [$month], [$year]) + +For a given day, and optionally C and C if they are +different from those specified in C, provide a list of year, +month, and day once C days have been added. + +=item week_of_year($day, [$month], [$year]) + +For a given day, and optionally C and C if they are +different from those specified in C, provide a list with the week +number of the year along with the year. (some days of a particular year +can end up belonging to the prior or following years). + +=back + +=head1 AUTHOR + +Matthew P. Sisk, EFE + +=head1 COPYRIGHT + +Copyright (c) 2005 Matthew P. Sisk. All rights reserved. All wrongs +revenged. This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +HTML::CalendarMonth(3), Time::Local(3), DateTime(3), Date::Calc(3), +Date::Manip(3), cal(1) diff --git a/1.16/lib/HTML/CalendarMonth/DateTool/Cal.pm b/1.16/lib/HTML/CalendarMonth/DateTool/Cal.pm new file mode 100644 index 0000000..6542a42 --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool/Cal.pm @@ -0,0 +1,39 @@ +package HTML::CalendarMonth::DateTool::Cal; + +# Interface to unix 'cal' command + +use strict; +use Carp; + +use vars qw(@ISA $VERSION); + +@ISA = qw(HTML::CalendarMonth::DateTool); + +$VERSION = '0.01'; + +use Time::Local; + +sub dow1st_and_lastday { + my($self, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + my $cmd = $self->cal_cmd or croak "cal command not found\n"; + + my @cal = grep(!/^\s*$/,`$cmd $month $year`); + chomp @cal; + my @days = grep(/\d+/,split(/\s+/,$cal[2])); + my $dow1st = 6 - $#days; + my($lastday) = $cal[$#cal] =~ /(\d+)\s*$/; + # With dow1st and lastday, one builds a calendar sequentially. + # Historically, in particular Sep 1752, days have been skipped. Here's + # the chance to catch that. + $self->skips(undef); + if ($month == 9 && $year == 1752) { + my %skips; + grep(++$skips{$_}, 3 .. 13); + $self->skips(\%skips); + } + ($dow1st, $lastday); +} + +1; diff --git a/1.16/lib/HTML/CalendarMonth/DateTool/DateCalc.pm b/1.16/lib/HTML/CalendarMonth/DateTool/DateCalc.pm new file mode 100644 index 0000000..97db691 --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool/DateCalc.pm @@ -0,0 +1,65 @@ +package HTML::CalendarMonth::DateTool::DateCalc; + +# Interface to Date::Calc + +use strict; +use Carp; + +use vars qw(@ISA $VERSION); + +@ISA = qw(HTML::CalendarMonth::DateTool); + +$VERSION = '0.01'; + +use Date::Calc qw(Days_in_Month Day_of_Week Add_Delta_Days + Weeks_in_Year Week_of_Year Week_Number Mktime + ); + +sub dow1st_and_lastday { + my($self, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + my $lastday = Days_in_Month($year, $month); + # Date::Calc uses 1..7 as indicies in the week, starting with Monday. + # Internally, we use 0..6, starting with Sunday. These turn out to be + # identical except for Sunday. + my $dow1st = $self->dow(1); + $dow1st = 0 if $dow1st == 7; + ($dow1st, $lastday); +} + +sub day_epoch { + my($self, $day, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + Mktime($year, $month, $day, 0, 0, 0); +} + +sub dow { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + Day_of_Week($year, $month, $day); +} + +sub add_days { + my($self, $delta, $day, $month, $year) = @_; + $delta || croak "Delta (in days) required.\n"; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my($y, $m, $d) = Add_Delta_Days($year, $month, $day, $delta); + ($d, $m, $y); +} + +sub week_of_year { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my($week, $year) = Week_of_Year($year, $month, $day); + ($year, $week); +} + +1; diff --git a/1.16/lib/HTML/CalendarMonth/DateTool/DateManip.pm b/1.16/lib/HTML/CalendarMonth/DateTool/DateManip.pm new file mode 100644 index 0000000..0dadef0 --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool/DateManip.pm @@ -0,0 +1,71 @@ +package HTML::CalendarMonth::DateTool::DateManip; + +# Interface to Date::Manip + +use strict; +use Carp; + +use vars qw(@ISA $VERSION); + +@ISA = qw(HTML::CalendarMonth::DateTool); + +$VERSION = '0.01'; + +use Date::Manip qw(Date_DaysInMonth Date_DayOfWeek DateCalc + UnixDate Date_SecsSince1970); + +sub dow1st_and_lastday { + my($self, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + my $lastday = Date_DaysInMonth($month, $year); + # Date::Manip uses 1 for Monday, 7 for Sunday as well. + my $dow1st = $self->dow(1); + ($dow1st, $lastday); +} + +sub day_epoch { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + Date_SecsSince1970($month, $day, $year, 0, 0, 0); +} + +sub dow { + # Date::Manip uses 1..7 as indicies in the week, starting with Monday. + # Internally, we use 0..6, starting with Sunday. These turn out to be + # identical except for Sunday. + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $dow = Date_DayOfWeek($month, $day, $year); + $dow = 0 if $dow == 7; + $dow; +} + +sub add_days { + my($self, $delta, $day, $month, $year) = @_; + $delta || croak "Delta (in days) required.\n"; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $date = DateCalc(sprintf("%04d%02d%02d", $year, $month, $day), + "+ $delta days"); + my($y, $m, $d) = $date =~ /^(\d{4})(\d\d)(\d\d)/; + $_ += 0 foreach ($y, $m, $d); + ($d, $m, $y); +} + +sub week_of_year { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $week = UnixDate(sprintf("%04d%02d%02d", $year, $month, $day), '%U'); + $week += 0; + ($year, $week); +} + +1; diff --git a/1.16/lib/HTML/CalendarMonth/DateTool/DateTime.pm b/1.16/lib/HTML/CalendarMonth/DateTool/DateTime.pm new file mode 100644 index 0000000..f6e366d --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool/DateTime.pm @@ -0,0 +1,84 @@ +package HTML::CalendarMonth::DateTool::DateTime; + +# Interface to DateTime + +use strict; +use Carp; + +use vars qw(@ISA $VERSION); + +@ISA = qw(HTML::CalendarMonth::DateTool); + +$VERSION = '0.01'; + +use DateTime; + +sub dow1st_and_lastday { + my($self, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + my $lastday = $self->_last_dom_dt($year, $month); + my $dow1st = $self->dow(1); + ($dow1st, $lastday->day); +} + +sub day_epoch { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $dt = $self->_new_dt($year, $month, $day); + $dt->epoch; +} + +sub dow { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $dt = $self->_new_dt($year, $month, $day); + $dt->dow; +} + +sub add_days { + my($self, $delta, $day, $month, $year) = @_; + $delta || croak "Delta (in days) required.\n"; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $dt = $self->_new_dt($year, $month, $day); + $dt->add(days => $delta); + ($dt->day, $dt->month, $dt->year); +} + +sub week_of_year { + my($self, $day, $month, $year) = @_; + $day || croak "Day required.\n"; + $month ||= $self->month; + $year ||= $self->year; + my $dt = $self->_new_dt($year, $month, $day); + # returns ($year, $week) + $dt->week; +} + +sub _new_dt { + my $self = shift; + my($year, $month, $day) = @_; + $year or croak "year and month required\n"; + my %parms = (year => $year); + $parms{month} = $month if $month; + $parms{day} = $day if $day; + $parms{hour} = 0; + $parms{minute} = 0; + $parms{second} = 0; + DateTime->new(%parms); +} + +sub _last_dom_dt { + my $self = shift; + my($year, $month) = @_; + $year && $month or croak "Year and month required.\n"; + DateTime->last_day_of_month(year => $year, month => $month); +} + +1; diff --git a/1.16/lib/HTML/CalendarMonth/DateTool/TimeLocal.pm b/1.16/lib/HTML/CalendarMonth/DateTool/TimeLocal.pm new file mode 100644 index 0000000..05b084e --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/DateTool/TimeLocal.pm @@ -0,0 +1,39 @@ +package HTML::CalendarMonth::DateTool::TimeLocal; + +# Interface to Time::Local + +use strict; +use Carp; + +use vars qw(@ISA $VERSION); + +@ISA = qw(HTML::CalendarMonth::DateTool); + +$VERSION = '0.01'; + +use Time::Local; + +sub dow1st_and_lastday { + my($self, $month, $year) = @_; + $month ||= $self->month; + $year ||= $self->year; + # map month to 0-12 + --$month; + # years since 1900...hooh-rah for POSIX... + $year -= 1900; + my $nmonth = $month + 1; + my $nyear = $year; + if ($nmonth > 11) { + # Happy new year + $nmonth = 0; + ++$nyear; + } + # Leave dow of 1st in 0-based format + my $dow1st = (gmtime(Time::Local::timegm(0,0,0,1,$month,$year)))[6]; + # Last day is one day prior to 1st of month after + my $lastday = (gmtime(Time::Local::timegm(0,0,0,1,$nmonth,$nyear) + - 60*60*24))[3]; + ($dow1st, $lastday); +} + +1; diff --git a/1.16/lib/HTML/CalendarMonth/Locale.pm b/1.16/lib/HTML/CalendarMonth/Locale.pm new file mode 100644 index 0000000..bb45bb3 --- /dev/null +++ b/1.16/lib/HTML/CalendarMonth/Locale.pm @@ -0,0 +1,302 @@ +package HTML::CalendarMonth::Locale; + +# Front end class around DateTime::Locale. In addition to providing +# access to the DT::Locale class and locale-specific instance, this +# class prepares some other hashes and lookups utilized by +# HTML::CalendarMonth. + +use strict; +use Carp; + +use DateTime::Locale; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my %Register; + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + my %parms = @_; + my $id = $parms{id} or croak "Locale id required (eg 'en_US')\n"; + $self->{id} = $id; + $self->{full_days} = exists $parms{full_days} ? $parms{full_days} : 0; + $self->{full_months} = exists $parms{full_months} ? $parms{full_months} : 1; + unless ($Register{$id}) { + $Register{$id} = $self->locale->load($id) + or croak "Problem loading locale '$id'\n"; + } + $self; +} + + +sub locale { 'DateTime::Locale' } + +sub loc { $Register{shift->id} } + +sub locales { shift->locale->ids } + +sub id { shift->{id} } +sub full_days { shift->{full_days} } +sub full_months { shift->{full_months} } + +sub days { + my $self = shift; + my $id = $self->id; + unless ($Register{$id}{days}) { + # we've always used Sunday as first day... + my $method = $self->full_days ? 'day_names' : 'day_abbreviations'; + my @days = @{$self->loc->$method}; + unshift(@days, pop @days); + $Register{$id}{days} = \@days; + } + wantarray ? @{$Register{$id}{days}} : $Register{$id}{days}; +} + +sub months { + my $self = shift; + my $id = $self->id; + unless ($Register{$id}{months}) { + my $method = $self->full_months ? 'month_names' : 'month_abbreviations'; + $Register{$id}{months} = [@{$self->loc->$method}]; + } + wantarray ? @{$Register{$id}{months}} : $Register{$id}{months}; +} + +sub minmatch { + my $self = shift; + my $id = $self->id; + unless ($Register{$id}{minmatch}) { + $Register{$id}{days_minmatch} = + $self->minmatch_hash(@{$self->days}); + } + $Register{$id}{days_minmatch}; +} + +sub daynums { + my $self = shift; + my $id = $self->id; + unless ($Register{$id}{daynum}) { + my %daynum; + my $days = $self->days; + $daynum{$days->[$_]} = $_ foreach 0 .. $#$days; + $Register{$id}{daynum} = \%daynum; + } + $Register{$id}{daynum}; +} + +sub daynum { + my($self, $day) = @_; + defined $day or croak "day of week label required\n"; + my $days = $self->days; + $days->{$day} or croak "Failed daynum lookup for '$day'\n"; +} + +sub monthnums { + my $self = shift; + my $id = $self->id; + unless ($Register{$id}{monthnum}) { + my %monthnum; + my $months = $self->months; + $monthnum{$months->[$_]} = $_ foreach 0 .. $#$months; + $Register{$id}{monthnum} = \%monthnum; + } + $Register{$id}{monthnum}; +} + +sub monthnum { + my($self, $month) = @_; + defined $month or croak "month label required\n"; + my $monthnums = $self->monthnums; + $monthnums->{$month} or croak "Failed monthnum lookup for '$month'\n"; +} + +### + +sub locale_map { + my $self = shift; + my %map; + foreach my $id ($self->locales) { + $map{$id} = $self->locale->load($id)->name; + } + wantarray ? %map : \%map; +} + +### + +sub minmatch_hash { + # given a list, provide a reverse lookup of minimal values for each + # label in the list + my $whatever = shift; + my @labels = @_; + my $cc = 1; + my %minmatch; + while (@labels) { + my %scratch; + foreach my $i (0 .. $#labels) { + my $str = $labels[$i]; + my $chrs = substr($str, 0, $cc); + $scratch{$chrs} ||= []; + push(@{$scratch{$chrs}}, $i); + } + my @keep_i; + foreach (keys %scratch) { + if (@{$scratch{$_}} == 1) { + $minmatch{$_} = $labels[$scratch{$_}[0]]; + } + else { + push(@keep_i, @{$scratch{$_}}); + } + } + @labels = @labels[@keep_i]; + ++$cc; + } + \%minmatch; +} + +sub minmatch_pattern { join('|',keys %{shift->minmatch}) } + +1; + +__END__ + +=head1 NAME + +HTML::CalendarMonth::Locale - Front end class for DateTime::Locale + +=head1 SYNOPSIS + + use HTML::CalendarMonth::Locale; + + my $loc = HTML::CalendarMonth::Locale->new( id => 'en_US' ); + + # list of days of the week for locale + my @days = $loc->days; + + # list of months of the year for locale + my @months = $loc->months; + + # the name of the current locale, as supplied the id parameter to + # new() + my $locale_name = $loc->id; + + # the actual DateTime::Locale object + my $loc = $loc->loc; + + 1; + +=head1 DESCRIPTION + +HTML::CalendarMonth utilizes the powerful locale capabilities of +DateTime::Locale for rendering its calendars. The default locale is +'en_US' but many others are available. To see this list, invoke the +class method HTML::CalendarMonth::Locale->locales() which in turn +invokes DateTime::Locale::ids(). + +This module is mostly intended for internal usage within +HTML::CalendarMonth, but some of its functionality may be of use for +developers: + +=head1 METHODS + +=item new() + +Constructor. Takes the following parameters: + +=over + +=item id + +Locale id, e.g. 'en_US'. + +=item full_days + +Specifies whether full day names or their abbreviations are desired. +Default 0, use abbreviated days. + +=item full_months + +Specifies whether full month names or their abbreviations are desired. +Default 1, use full months. + +=back + +=item id() + +Returns the locale id used during object construction. + +=item locale() + +Accessor method for the DateTime::Locale class, which in turn offers +several class methods of specific interest. See L. + +=item loc() + +Accessor method for the DateTime::Locale instance as specified by C. +See L. + +=item locales() + +Lists all available locale ids. Equivalent to locale()->ids(), or +DateTime::Locale->ids(). + +=item days() + +Returns a list of days of the week, Sunday first. These are the actual +days used for rendering the calendars, so depending on which attributes +were provided to C, this list will either be abbreviations or +full names. The default uses abbreviated day names. Returns a list in +list context or an array ref in scalar context. + +=item months() + +Returns a list of months of the year, beginning with January. Depending +on which attributes were provided to C, this list will either be +full names or abbreviations. The default uses full names. Returns a list +in list context or an array ref in scalar context. + +=item minmatch() + +Provides a hash reference containing minimal match strings for each +month of the year, e.g., 'N' for November, 'Ja' for January, 'Jul' for +July, 'Jun' for June, etc. + +=item daynums() + +Provides a hash reference containing day of week numbers for each day +name. + +=item daynum($day) + +Provides the day of week number for a particular day name. + +=item monthnums() + +Provides a hash reference containing month of year numbers for each +month name. + +=item monthnum($month) + +Provides the month of year number for a particular month name. + +=item minmatch_hash(@list) + +This is the method used to generate the minimal match hash referenced +above. Given an arbitrary list, a hash reference will be returned with +minimal match strings as keys and full names as values. + +=head1 AUTHOR + +Matthew P. Sisk, EFE + +=head1 COPYRIGHT + +Copyright (c) 2005 Matthew P. Sisk. All rights reserved. All wrongs +revenged. This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +HTML::CalendarMonth(3), DateTime::Locale(3) diff --git a/1.16/t/00_basic.t b/1.16/t/00_basic.t new file mode 100755 index 0000000..4034ae6 --- /dev/null +++ b/1.16/t/00_basic.t @@ -0,0 +1,6 @@ +use Test::More tests => 3; +BEGIN { + use_ok('HTML::CalendarMonth'); + use_ok('HTML::CalendarMonth::Locale'); + use_ok('HTML::CalendarMonth::DateTool'); +} diff --git a/1.16/t/01_autodetect.t b/1.16/t/01_autodetect.t new file mode 100755 index 0000000..e1f0dc8 --- /dev/null +++ b/1.16/t/01_autodetect.t @@ -0,0 +1,15 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 15 ; $method = '' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +check_basic_with_datetool($method); diff --git a/1.16/t/02_timelocal.t b/1.16/t/02_timelocal.t new file mode 100755 index 0000000..bff1ea0 --- /dev/null +++ b/1.16/t/02_timelocal.t @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 17 ; $method = 'Time::Local' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +SKIP: { + eval "use $method"; + skip("$method not installed", $test_count) if $@; + check_datetool($method); + check_basic_with_datetool($method); +} diff --git a/1.16/t/03_datetime.t b/1.16/t/03_datetime.t new file mode 100755 index 0000000..b7f48e3 --- /dev/null +++ b/1.16/t/03_datetime.t @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 19 ; $method = 'DateTime' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +SKIP: { + eval "use $method"; + skip("$method not installed", $test_count) if $@; + check_datetool($method); + check_basic_with_datetool($method); + check_woy_with_datetool($method); +} diff --git a/1.16/t/04_datemanip.t b/1.16/t/04_datemanip.t new file mode 100755 index 0000000..d651858 --- /dev/null +++ b/1.16/t/04_datemanip.t @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 19 ; $method = 'Date::Manip' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +SKIP: { + eval "use $method"; + skip("$method not installed", $test_count) if $@; + check_datetool($method); + check_basic_with_datetool($method); + check_woy_with_datetool($method); +} diff --git a/1.16/t/05_datecalc.t b/1.16/t/05_datecalc.t new file mode 100755 index 0000000..cdfa55c --- /dev/null +++ b/1.16/t/05_datecalc.t @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 19 ; $method = 'Date::Calc' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +SKIP: { + eval "use $method"; + skip("$method not installed", $test_count) if $@; + check_datetool($method); + check_basic_with_datetool($method); + check_woy_with_datetool($method); +} diff --git a/1.16/t/06_cal.t b/1.16/t/06_cal.t new file mode 100755 index 0000000..58457b2 --- /dev/null +++ b/1.16/t/06_cal.t @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; + +my($test_count, $method); +BEGIN { $test_count = 17 ; $method = 'cal' } + +use Test::More tests => $test_count; + +use FindBin; +use lib $FindBin::RealBin; + +use testload; + +SKIP: { + my $CAL; + chomp($CAL = `which cal`); + skip("$method not installed", $test_count) unless -x $CAL; + check_datetool($method); + check_basic_with_datetool($method); +} diff --git a/1.16/t/20_i8n.t b/1.16/t/20_i8n.t new file mode 100755 index 0000000..e59361f --- /dev/null +++ b/1.16/t/20_i8n.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; + +use Test::More tests => 2; + +use HTML::CalendarMonth; +use HTML::CalendarMonth::Locale; + +my $basque; +eval do { local $/; }; +die "Oops on eval: $@\n" if $@; + +# i8n (use basque as example) +my @stoof = HTML::CalendarMonth::Locale->locales; +ok(@stoof > 20, 'i8n: locale ids retreived'); +my($year, $month) = (2000, 12); +my $b = HTML::CalendarMonth->new( + year => $year, + month => $month, + head_week => 1, + locale => 'eu', +); +my $bstr = $b->as_HTML; +chomp($bstr); +cmp_ok($bstr, 'eq', $basque, "i8n: ($year/$month : Basque) using auto-detect"); + +__DATA__ +$basque = '
abendua2000
igalasazogorlr 
          1248
345678949
1011121314151650
1718192021222351
2425262728293052
31            1
'; diff --git a/1.16/t/test.dat b/1.16/t/test.dat new file mode 100644 index 0000000..34eafca --- /dev/null +++ b/1.16/t/test.dat @@ -0,0 +1,101 @@ +2000/10 2000/12 2002/03 +2000/10 2 +
October2000
MonTueWedThuFriSatSun
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2000/12 2 +
December2000
MonTueWedThuFriSatSun
        123
45678910
11121314151617
18192021222324
25262728293031
+2002/01 1 +
January2002
SunMonTueWedThuFriSat
    12345
6789101112
13141516171819
20212223242526
2728293031   
+2002/02 2 +
February2002
MonTueWedThuFriSatSun
        123
45678910
11121314151617
18192021222324
25262728     
+2002/03 1 +
March2002
SunMonTueWedThuFriSat
          12
3456789
10111213141516
17181920212223
24252627282930
31           
+2002/04 2 +
April2002
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
2930         
+2002/05 1 +
May2002
SunMonTueWedThuFriSat
      1234
567891011
12131415161718
19202122232425
262728293031 
+2002/06 2 +
June2002
MonTueWedThuFriSatSun
          12
3456789
10111213141516
17181920212223
24252627282930
+2002/07 1 +
July2002
SunMonTueWedThuFriSat
  123456
78910111213
14151617181920
21222324252627
28293031     
+2002/08 2 +
August2002
MonTueWedThuFriSatSun
      1234
567891011
12131415161718
19202122232425
262728293031 
+2002/09 1 +
September2002
SunMonTueWedThuFriSat
1234567
891011121314
15161718192021
22232425262728
2930         
+2002/10 2 +
October2002
MonTueWedThuFriSatSun
  123456
78910111213
14151617181920
21222324252627
28293031     
+2002/11 1 +
November2002
SunMonTueWedThuFriSat
          12
3456789
10111213141516
17181920212223
24252627282930
+2002/12 2 +
December2002
MonTueWedThuFriSatSun
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2003/01 1 +
January2003
SunMonTueWedThuFriSat
      1234
567891011
12131415161718
19202122232425
262728293031 
+2003/02 2 +
February2003
MonTueWedThuFriSatSun
          12
3456789
10111213141516
17181920212223
2425262728   
+2003/03 1 +
March2003
SunMonTueWedThuFriSat
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2003/04 2 +
April2003
MonTueWedThuFriSatSun
  123456
78910111213
14151617181920
21222324252627
282930       
+2003/05 1 +
May2003
SunMonTueWedThuFriSat
        123
45678910
11121314151617
18192021222324
25262728293031
+2003/06 2 +
June2003
MonTueWedThuFriSatSun
            1
2345678
9101112131415
16171819202122
23242526272829
30           
+2003/07 1 +
July2003
SunMonTueWedThuFriSat
    12345
6789101112
13141516171819
20212223242526
2728293031   
+2003/08 2 +
August2003
MonTueWedThuFriSatSun
        123
45678910
11121314151617
18192021222324
25262728293031
+2003/09 1 +
September2003
SunMonTueWedThuFriSat
  123456
78910111213
14151617181920
21222324252627
282930       
+2003/10 2 +
October2003
MonTueWedThuFriSatSun
    12345
6789101112
13141516171819
20212223242526
2728293031   
+2003/11 1 +
November2003
SunMonTueWedThuFriSat
            1
2345678
9101112131415
16171819202122
23242526272829
30           
+2003/12 2 +
December2003
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
293031       
+2004/01 1 +
January2004
SunMonTueWedThuFriSat
        123
45678910
11121314151617
18192021222324
25262728293031
+2004/02 2 +
February2004
MonTueWedThuFriSatSun
            1
2345678
9101112131415
16171819202122
23242526272829
+2004/03 1 +
March2004
SunMonTueWedThuFriSat
  123456
78910111213
14151617181920
21222324252627
28293031     
+2004/04 2 +
April2004
MonTueWedThuFriSatSun
      1234
567891011
12131415161718
19202122232425
2627282930   
+2004/05 1 +
May2004
SunMonTueWedThuFriSat
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2004/06 2 +
June2004
MonTueWedThuFriSatSun
  123456
78910111213
14151617181920
21222324252627
282930       
+2004/07 1 +
July2004
SunMonTueWedThuFriSat
        123
45678910
11121314151617
18192021222324
25262728293031
+2004/08 2 +
August2004
MonTueWedThuFriSatSun
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2004/09 1 +
September2004
SunMonTueWedThuFriSat
      1234
567891011
12131415161718
19202122232425
2627282930   
+2004/10 2 +
October2004
MonTueWedThuFriSatSun
        123
45678910
11121314151617
18192021222324
25262728293031
+2004/11 1 +
November2004
SunMonTueWedThuFriSat
  123456
78910111213
14151617181920
21222324252627
282930       
+2004/12 2 +
December2004
MonTueWedThuFriSatSun
    12345
6789101112
13141516171819
20212223242526
2728293031   
+2005/01 1 +
January2005
SunMonTueWedThuFriSat
            1
2345678
9101112131415
16171819202122
23242526272829
3031         
+2005/02 2 +
February2005
MonTueWedThuFriSatSun
  123456
78910111213
14151617181920
21222324252627
28           
+2005/03 1 +
March2005
SunMonTueWedThuFriSat
    12345
6789101112
13141516171819
20212223242526
2728293031   
+2005/04 2 +
April2005
MonTueWedThuFriSatSun
        123
45678910
11121314151617
18192021222324
252627282930 
+2005/05 1 +
May2005
SunMonTueWedThuFriSat
1234567
891011121314
15161718192021
22232425262728
293031       
+2005/06 2 +
June2005
MonTueWedThuFriSatSun
    12345
6789101112
13141516171819
20212223242526
27282930     
+2005/07 1 +
July2005
SunMonTueWedThuFriSat
          12
3456789
10111213141516
17181920212223
24252627282930
31           
+2005/08 2 +
August2005
MonTueWedThuFriSatSun
1234567
891011121314
15161718192021
22232425262728
293031       
+2005/09 1 +
September2005
SunMonTueWedThuFriSat
        123
45678910
11121314151617
18192021222324
252627282930 
+2005/10 2 +
October2005
MonTueWedThuFriSatSun
          12
3456789
10111213141516
17181920212223
24252627282930
31           
+2005/11 1 +
November2005
SunMonTueWedThuFriSat
    12345
6789101112
13141516171819
20212223242526
27282930     
+2005/12 2 +
December2005
MonTueWedThuFriSatSun
      1234
567891011
12131415161718
19202122232425
262728293031 
diff --git a/1.16/t/testload.pm b/1.16/t/testload.pm new file mode 100644 index 0000000..902069d --- /dev/null +++ b/1.16/t/testload.pm @@ -0,0 +1,131 @@ +package testload; + +use vars qw( @ISA @EXPORT $Dat_Dir ); + +use strict; +use Test::More; + +my $DEBUG = 0; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw( $Dat_Dir check_datetool + check_basic_with_datetool + check_woy_with_datetool + ); + +use File::Spec; + +use HTML::CalendarMonth; +use HTML::CalendarMonth::DateTool; + +my $base_dir; +BEGIN { + my $pkg = __PACKAGE__; + $pkg =~ s%::%/%g; + $pkg .= '.pm'; + $pkg = File::Spec->canonpath($INC{$pkg}); + $pkg =~ s/\/[^\/]+\.pm$//; + $base_dir = $pkg; +} +$Dat_Dir = $base_dir; + +my($tcount, $rds, %dates, @tmethods, @twy_methods, @Cals); + +# Required test dates +open(D, "$Dat_Dir/test.dat") or die "Problem reading $Dat_Dir/test.dat: $!\n"; +$rds = ; +foreach (split(' ', $rds)) { + ++$dates{$_}; +} + +my %WOY_data; +eval join('', ); +die "Oops on eval: $@\n" if $@; + +# Today's date +my($month, $year) = (localtime(time))[4,5]; +++$month; +$year += 1900; + +# Flag tests for a year +foreach my $y ($year .. $year + 1) { + foreach my $m (1 .. 12) { + ++$dates{sprintf("%d/%02d", $y, $m)}; + } +} + +# Yank test cases +while () { + chomp; + my($d, $wb) = split(' ', $_); + my($y, $m) = split('/', $d); + my $cal = ; + push(@Cals, [$y, $m, $cal, $wb]) if $dates{"$y/$m"}; +} + +close(D); + +sub check_datetool { + my $datetool = shift; + my $module = HTML::CalendarMonth::DateTool->toolmap($datetool); + ok($module, "toolmap($datetool) : $module"); + require_ok($module); +} + +sub check_basic_with_datetool { + my $datetool = shift; + my @days = qw( Sun Mon Tue Wed Thr Fri Sat ); + my $method = $datetool || 'auto-select'; + foreach my $cal (@Cals) { + my $c = HTML::CalendarMonth->new( + year => $cal->[0], + month => $cal->[1], + week_begin => $cal->[3], + datetool => $datetool, + ); + my $day1 = $days[$cal->[3] - 1]; + cmp_ok($c->as_HTML, 'eq', $cal->[2], + sprintf("(%d/%-02d %s 1st day) using %s", + $cal->[0], $cal->[1], $day1, $method)); + if ($DEBUG && $c->as_HTML ne $cal->[2]) { + debug_dump('Broken', $c->as_HTML, 'Test Data', $cal->[2]); + } + } +} + +sub check_woy_with_datetool { + my $datetool = shift; + my $year = 2000; + foreach my $month (qw(01 12)) { + my $tc = $WOY_data{"$year/$month"}; + my $cal = HTML::CalendarMonth->new( + year => $year, + month => $month, + head_week => 1, + datetool => $datetool, + ); + my $ct = $cal->as_HTML; + chomp $ct; + cmp_ok($ct, 'eq', $tc, "($year/$month week of year) using $datetool"); + if ($DEBUG && $ct ne $tc) { + debug_dump('Broken', $ct, 'Test Data', $tc); + } + } +} + +sub debug_dump { + my($l1, $str1, $l2, $str2) = @_; + local(*DUMP); + open(DUMP, ">$DEBUG") or die "Could not dump to $DEBUG: $!\n"; + print DUMP "
$l1$l2
\n"; + print DUMP "$str1\n\n"; + print DUMP "$str2\n
\n"; + close(DUMP); + print STDERR "\nDumped tables to $DEBUG. Aborting test.\n"; + exit; +} + +__DATA__ +$WOY_data{'2000/01'} = '
January2000
SunMonTueWedThuFriSat 
            152
23456781
91011121314152
161718192021223
232425262728294
3031          5
'; +$WOY_data{'2000/12'} = '
December2000
SunMonTueWedThuFriSat 
          1248
345678949
1011121314151650
1718192021222351
2425262728293052
31            1
'; -- 2.39.2