File Coverage

blib/lib/Spreadsheet/WriteExcelXML/Utility.pm
Criterion Covered Total %
statement 44 117 37.6
branch 5 44 11.3
condition 1 24 4.1
subroutine 7 19 36.8
pod 14 14 100.0
total 71 218 32.5


line stmt bran cond sub pod time code
1             package Spreadsheet::WriteExcelXML::Utility;
2              
3             ###############################################################################
4             #
5             # Utility - Helper functions for Spreadsheet::WriteExcelXML.
6             #
7             # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
8             #
9             #
10              
11 23     23   138 use Exporter;
  23         44  
  23         1007  
12 23     23   155 use strict;
  23         47  
  23         1337  
13 23     23   27961 use autouse 'Date::Calc' => qw(Delta_DHMS Decode_Date_EU Decode_Date_US);
  23         23914  
  23         158  
14 23     23   2202 use autouse 'Date::Manip' => qw(ParseDate Date_Init);
  23         58  
  23         163  
15              
16              
17             # Do all of the export preparation
18 23     23   1885 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  23         44  
  23         59814  
19              
20             # Row and column functions
21             my @rowcol = qw(
22             xl_rowcol_to_cell
23             xl_cell_to_rowcol
24             xl_range_formula
25             xl_inc_row
26             xl_dec_row
27             xl_inc_col
28             xl_dec_col
29             );
30              
31             # Date and Time functions
32             my @dates = qw(
33             xl_date_list
34             xl_date_1904
35             xl_parse_time
36             xl_parse_date
37             xl_parse_date_init
38             xl_decode_date_EU
39             xl_decode_date_US
40             );
41              
42              
43             @ISA = qw(Exporter);
44             @EXPORT_OK = ();
45             @EXPORT = (@rowcol, @dates);
46             %EXPORT_TAGS = (
47             rowcol => \@rowcol,
48             dates => \@dates
49             );
50              
51             $VERSION = '0.14';
52              
53              
54              
55              
56             =head1 NAME
57              
58             Utility - Helper functions for Spreadsheet::WriteExcelXML.
59              
60              
61              
62             =head1 SYNOPSIS
63              
64             Functions to help with some common tasks when using Spreadsheet::WriteExcelXML.
65              
66             These functions mainly relate to dealing with rows and columns in A1 notation and to handling dates and times.
67              
68             use Spreadsheet::WriteExcelXML::Utility; # Import everything
69              
70             ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
71             $str = xl_rowcol_to_cell(1, 2); # C2
72             $str = xl_inc_col('Z1' ); # AA1
73             $str = xl_dec_col('AA1' ); # Z1
74              
75             $date = xl_date_list(2002, 1, 1); # 37257
76             $date = xl_parse_date("11 July 1997"); # 35622
77             $time = xl_parse_time('3:21:36 PM'); # 0.64
78             $date = xl_decode_date_EU("13 May 2002"); # 37389
79              
80              
81              
82              
83             =head1 DESCRIPTION
84              
85             This module provides a set of functions to help with some common tasks encountered when using the Spreadsheet::WriteExcelXML module. The two main categories of function are:
86              
87             Row and column functions: these are used to deal with Excel's A1 representation of cells. The functions in this category are:
88              
89             xl_rowcol_to_cell
90             xl_cell_to_rowcol
91             xl_range_formula
92             xl_inc_row
93             xl_dec_row
94             xl_inc_col
95             xl_dec_col
96              
97             Date and Time functions: these are used to convert dates and times to the numeric format used by Excel. The functions in this category are:
98              
99             xl_date_list
100             xl_date_1904
101             xl_parse_time
102             xl_parse_date
103             xl_parse_date_init
104             xl_decode_date_EU
105             xl_decode_date_US
106              
107             All of these functions are exported by default. However, you can use import lists if you wish to limit the functions that are imported:
108              
109             use Spreadsheet::WriteExcelXML::Utility; # Import everything
110             use Spreadsheet::WriteExcelXML::Utility qw(xl_date_list); # xl_date_list only
111             use Spreadsheet::WriteExcelXML::Utility qw(:rowcol); # Row/col functions
112             use Spreadsheet::WriteExcelXML::Utility qw(:dates); # Date functions
113              
114              
115              
116             =head1 ROW AND COLUMN FUNCTIONS
117              
118              
119             Spreadsheet::WriteExcelXML supports two forms of notation to designate the position of cells: Row-column notation and A1 notation.
120              
121             Row-column notation uses a zero based index for both row and column while A1 notation uses the standard Excel alphanumeric sequence of column letter and 1-based row. Columns range from A to IV i.e. 0 to 255, rows range from 1 to 16384 in Excel 5 and 65536 in Excel 97. For example:
122              
123             (0, 0) # The top left cell in row-column notation.
124             ('A1') # The top left cell in A1 notation.
125              
126             (1999, 29) # Row-column notation.
127             ('AD2000') # The same cell in A1 notation.
128              
129             Row-column notation is useful if you are referring to cells programmatically:
130              
131             for my $i (0 .. 9) {
132             $worksheet->write($i, 0, 'Hello'); # Cells A1 to A10
133             }
134              
135             A1 notation is useful for setting up a worksheet manually and for working with formulas:
136              
137             $worksheet->write('H1', 200);
138             $worksheet->write('H2', '=H7+1');
139              
140             The functions in the following sections can be used for dealing with A1 notation, for example:
141              
142             ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
143             $str = xl_rowcol_to_cell(1, 2); # C2
144              
145              
146             Cell references in Excel can be either relative or absolute. Absolute references are prefixed by the dollar symbol as shown below:
147              
148             A1 # Column and row are relative
149             $A1 # Column is absolute and row is relative
150             A$1 # Column is relative and row is absolute
151             $A$1 # Column and row are absolute
152              
153             An absolute reference only has an effect if the cell is copied. Refer to the Excel documentation for further details. All of the following functions support absolute references.
154              
155             =cut
156              
157              
158              
159              
160             ###############################################################################
161             ###############################################################################
162              
163             =head2 xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
164              
165             Parameters: $row: Integer
166             $col: Integer
167             $row_absolute: Boolean (1/0) [optional, default is 0]
168             $col_absolute: Boolean (1/0) [optional, default is 0]
169              
170             Returns: A string in A1 cell notation
171              
172              
173             This function converts a zero based row and column cell reference to a A1 style string:
174              
175             $str = xl_rowcol_to_cell(0, 0); # A1
176             $str = xl_rowcol_to_cell(0, 1); # B1
177             $str = xl_rowcol_to_cell(1, 0); # A2
178              
179              
180             The optional parameters C<$row_absolute> and C<$col_absolute> can be used to indicate if the row or column is absolute:
181              
182             $str = xl_rowcol_to_cell(0, 0, 0, 1); # $A1
183             $str = xl_rowcol_to_cell(0, 0, 1, 0); # A$1
184             $str = xl_rowcol_to_cell(0, 0, 1, 1); # $A$1
185              
186             See L for an explanation of absolute cell references.
187              
188              
189             =cut
190             ###############################################################################
191             #
192             # xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
193             #
194             sub xl_rowcol_to_cell {
195              
196 6     6 1 8 my $row = $_[0];
197 6         7 my $col = $_[1];
198 6 50       13 my $row_abs = $_[2] ? '$' : '';
199 6 50       13 my $col_abs = $_[3] ? '$' : '';
200 6         7 my $col_str = '';
201              
202             # Change from 0-indexed to 1 indexed.
203 6         15 $row++;
204 6         7 $col++;
205              
206 6         83 while ( $col ) {
207             # Set remainder from 1 .. 26
208 6   50     15 my $remainder = $col % 26 || 26;
209              
210             # Convert the $remainder to a character. C-ishly.
211 6         12 my $col_letter = chr( ord( 'A' ) + $remainder - 1 );
212              
213             # Accumulate the column letters, right to left.
214 6         12 $col_str = $col_letter . $col_str;
215              
216             # Get the next order of magnitude.
217 6         18 $col = int( ( $col - 1 ) / 26 );
218             }
219              
220 6         22 return $col_abs . $col_str . $row_abs . $row;
221             }
222              
223              
224             ###############################################################################
225             ###############################################################################
226              
227             =head2 xl_cell_to_rowcol($string)
228              
229              
230             Parameters: $string String in A1 format
231              
232             Returns: List ($row, $col)
233              
234             This function converts an Excel cell reference in A1 notation to a zero based row and column. The function will also handle Excel's absolute, C<$>, cell notation.
235              
236             my ($row, $col) = xl_cell_to_rowcol('A1'); # (0, 0)
237             my ($row, $col) = xl_cell_to_rowcol('B1'); # (0, 1)
238             my ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
239             my ($row, $col) = xl_cell_to_rowcol('$C2' ); # (1, 2)
240             my ($row, $col) = xl_cell_to_rowcol('C$2' ); # (1, 2)
241             my ($row, $col) = xl_cell_to_rowcol('$C$2'); # (1, 2)
242              
243             =cut
244             ###############################################################################
245             #
246             # xl_cell_to_rowcol($string)
247             #
248             # Returns: ($row, $col, $row_absolute, $col_absolute)
249             #
250             # The $row_absolute and $col_absolute parameters aren't documented because they
251             # mainly used internally and aren't very useful to the user.
252             #
253             sub xl_cell_to_rowcol {
254              
255 16     16 1 25 my $cell = shift;
256              
257 16         49 $cell =~ /(\$?)([A-Z]{1,3})(\$?)(\d+)/;
258              
259 16 100       45 my $col_abs = $1 eq "" ? 0 : 1;
260 16         28 my $col = $2;
261 16 50       38 my $row_abs = $3 eq "" ? 0 : 1;
262 16         24 my $row = $4;
263              
264             # Convert base26 column string to number
265             # All your Base are belong to us.
266 16         41 my @chars = split //, $col;
267 16         22 my $expn = 0;
268 16         22 $col = 0;
269              
270 16         38 while (@chars) {
271 20         30 my $char = pop(@chars); # LS char first
272 20         40 $col += (ord($char) -ord('A') +1) * (26**$expn);
273 20         53 $expn++;
274             }
275              
276             # Convert 1-index to zero-index
277 16         23 $row--;
278 16         21 $col--;
279              
280 16         73 return $row, $col, $row_abs, $col_abs;
281             }
282              
283              
284              
285              
286             ###############################################################################
287             ###############################################################################
288              
289             =head2 xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
290              
291             Parameters: $sheetname String
292             $row_1: Integer
293             $row_2: Integer
294             $col_1: Integer
295             $col_2: Integer
296              
297             Returns: A worksheet range formula as a string.
298              
299             This function converts zero based row and column cell references to an A1 style formula string:
300              
301             my $str = xl_range_formula('Sheet1', 0, 9, 0, 0); # =Sheet1!$A$1:$A$10
302             my $str = xl_range_formula('Sheet2', 6, 65, 1, 1); # =Sheet2!$B$7:$B$66
303             my $str = xl_range_formula('New data', 1, 8, 2, 2); # ='New data'!$C$2:$C$9
304              
305              
306             This is useful for setting ranges in Chart objects:
307              
308              
309             $chart->add_series(
310             categories => xl_range_formula('Sheet1', 1, 9, 0, 0),
311             values => xl_range_formula('Sheet1', 1, 9, 1, 1),
312             );
313              
314             # Which is the same as:
315              
316             $chart->add_series(
317             categories => '=Sheet1!$A$2:$A$10',
318             values => '=Sheet1!$B$2:$B$10',
319             );
320              
321              
322             =cut
323             ###############################################################################
324             #
325             # xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
326             #
327             sub xl_range_formula {
328              
329 0     0 1   my ($sheetname, $row_1, $row_2, $col_1, $col_2) = @_;
330              
331             # Use Excel's conventions and quote the sheet name if it contains any
332             # non-word character or if it isn't already quoted.
333 0 0 0       if ($sheetname =~ /\W/ && $sheetname !~ /^'/) {
334 0           $sheetname = q(') . $sheetname . q(');
335             }
336              
337 0           my $range1 = xl_rowcol_to_cell($row_1, $col_1, 1, 1);
338 0           my $range2 = xl_rowcol_to_cell($row_2, $col_2, 1, 1);
339              
340 0           return '=' . $sheetname . '!' . $range1 . ':' . $range2;
341             }
342              
343              
344              
345              
346             ###############################################################################
347             ###############################################################################
348              
349             =head2 xl_inc_row($string)
350              
351              
352             Parameters: $string, a string in A1 format
353              
354             Returns: Incremented string in A1 format
355              
356             This functions takes a cell reference string in A1 notation and increments the row. The function will also handle Excel's absolute, C<$>, cell notation:
357              
358             my $str = xl_inc_row('A1' ); # A2
359             my $str = xl_inc_row('B$2' ); # B$3
360             my $str = xl_inc_row('$C3' ); # $C4
361             my $str = xl_inc_row('$D$4'); # $D$5
362              
363              
364             =cut
365             ###############################################################################
366             #
367             # xl_inc_row($string)
368             #
369             sub xl_inc_row {
370              
371 0     0 1   my $cell = shift;
372 0           my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
373              
374 0           return xl_rowcol_to_cell(++$row, $col, $row_abs, $col_abs);
375             }
376              
377              
378              
379              
380             ###############################################################################
381             ###############################################################################
382              
383             =head2 xl_dec_row($string)
384              
385              
386             Parameters: $string, a string in A1 format
387              
388             Returns: Decremented string in A1 format
389              
390             This functions takes a cell reference string in A1 notation and decrements the row. The function will also handle Excel's absolute, C<$>, cell notation:
391              
392             my $str = xl_dec_row('A2' ); # A1
393             my $str = xl_dec_row('B$3' ); # B$2
394             my $str = xl_dec_row('$C4' ); # $C3
395             my $str = xl_dec_row('$D$5'); # $D$4
396              
397              
398             =cut
399             ###############################################################################
400             #
401             # xl_dec_row($string)
402             #
403             # Decrements the row number of an Excel cell reference in A1 notation.
404             # For example C4 to C3
405             #
406             # Returns: a cell reference string.
407             #
408             sub xl_dec_row {
409              
410 0     0 1   my $cell = shift;
411 0           my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
412              
413 0           return xl_rowcol_to_cell(--$row, $col, $row_abs, $col_abs);
414             }
415              
416              
417              
418              
419             ###############################################################################
420             ###############################################################################
421              
422             =head2 xl_inc_col($string)
423              
424              
425             Parameters: $string, a string in A1 format
426              
427             Returns: Incremented string in A1 format
428              
429             This functions takes a cell reference string in A1 notation and increments the column. The function will also handle Excel's absolute, C<$>, cell notation:
430              
431             my $str = xl_inc_col('A1' ); # B1
432             my $str = xl_inc_col('Z1' ); # AA1
433             my $str = xl_inc_col('$B1' ); # $C1
434             my $str = xl_inc_col('$D$5'); # $E$5
435              
436              
437             =cut
438             ###############################################################################
439             #
440             # xl_inc_col($string)
441             #
442             # Increments the column number of an Excel cell reference in A1 notation.
443             # For example C3 to D3
444             #
445             # Returns: a cell reference string.
446             #
447             sub xl_inc_col {
448              
449 0     0 1   my $cell = shift;
450 0           my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
451              
452 0           return xl_rowcol_to_cell($row, ++$col, $row_abs, $col_abs);
453             }
454              
455              
456              
457              
458             ###############################################################################
459             ###############################################################################
460              
461             =head2 xl_dec_col($string)
462              
463             Parameters: $string, a string in A1 format
464              
465             Returns: Decremented string in A1 format
466              
467             This functions takes a cell reference string in A1 notation and decrements the column. The function will also handle Excel's absolute, C<$>, cell notation:
468              
469             my $str = xl_dec_col('B1' ); # A1
470             my $str = xl_dec_col('AA1' ); # Z1
471             my $str = xl_dec_col('$C1' ); # $B1
472             my $str = xl_dec_col('$E$5'); # $D$5
473              
474              
475             =cut
476             ###############################################################################
477             #
478             # xl_dec_col($string)
479             #
480             sub xl_dec_col {
481              
482 0     0 1   my $cell = shift;
483 0           my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
484              
485 0           return xl_rowcol_to_cell($row, --$col, $row_abs, $col_abs);
486             }
487              
488              
489              
490              
491             =head1 TIME AND DATE FUNCTIONS
492              
493              
494             Dates and times in Excel are represented by real numbers, for example "Jan 1 2001 12:30 AM" is represented by the number 36892.521.
495              
496             The integer part of the number stores the number of days since the epoch and the fractional part stores the percentage of the day in seconds.
497              
498             The epoch can be either 1900 or 1904. Excel for Windows uses 1900 and Excel for Macintosh uses 1904. The epochs are:
499              
500             1900: 0 January 1900 i.e. 31 December 1899
501             1904: 1 January 1904
502              
503             Excel on Windows and the Macintosh will convert automatically between one system and the other. By default Spreadsheet::WriteExcelXML uses the 1900 format. To use the 1904 epoch you must use the C workbook method, see the Spreadsheet::WriteExcelXML documentation.
504              
505             There are two things to note about the 1900 date format. The first is that the epoch starts on 0 January 1900. The second is that the year 1900 is erroneously but deliberately treated as a leap year. Therefore you must add an extra day to dates after 28 February 1900. The functions in the following section will deal with these issues automatically. The reason for this anomaly is explained at http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
506              
507             Note, a date or time in Excel is like any other number. To display the number as a date you must apply a number format to it: Refer to the C method in the Spreadsheet::WriteExcelXML documentation:
508              
509             $date = xl_date_list(2001, 1, 1, 12, 30);
510             $format->set_num_format('mmm d yyyy hh:mm AM/PM');
511             $worksheet->write('A1', $date , $format); # Jan 1 2001 12:30 AM
512              
513             To use these functions you must install the C and C modules. See L and the individual requirements of each functions.
514              
515             See also the DateTime::Format::Excel module,http://search.cpan.org/search?dist=DateTime-Format-Excel which is part of the DateTime project and which deals specifically with converting dates and times to and from Excel's format.
516              
517              
518             =cut
519              
520              
521             ###############################################################################
522             ###############################################################################
523              
524             =head2 xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
525              
526              
527             Parameters: $years: Integer
528             $months: Integer [optional, default is 1]
529             $days: Integer [optional, default is 1]
530             $hours: Integer [optional, default is 0]
531             $minutes: Integer [optional, default is 0]
532             $seconds: Float [optional, default is 0]
533              
534             Returns: A number that represents an Excel date
535             or undef for an invalid date.
536              
537             Requires: Date::Calc
538              
539             This function converts an array of data into a number that represents an Excel date. All of the parameters are optional except for C<$years>.
540              
541             $date1 = xl_date_list(2002, 1, 2); # 2 Jan 2002
542             $date2 = xl_date_list(2002, 1, 2, 12); # 2 Jan 2002 12:00 pm
543             $date3 = xl_date_list(2002, 1, 2, 12, 30); # 2 Jan 2002 12:30 pm
544             $date4 = xl_date_list(2002, 1, 2, 12, 30, 45); # 2 Jan 2002 12:30:45 pm
545              
546             This function can be used in conjunction with functions that parse date and time strings. In fact it is used in most of the following functions.
547              
548              
549             =cut
550             ###############################################################################
551             #
552             # xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
553             #
554             sub xl_date_list {
555              
556 0 0   0 1   return undef unless @_;
557              
558 0           my $years = $_[0];
559 0   0       my $months = $_[1] || 1;
560 0   0       my $days = $_[2] || 1;
561 0   0       my $hours = $_[3] || 0;
562 0   0       my $minutes = $_[4] || 0;
563 0   0       my $seconds = $_[5] || 0;
564              
565 0           my @date = ($years, $months, $days, $hours, $minutes, $seconds);
566 0           my @epoch = (1899, 12, 31, 0, 0, 0);
567              
568 0           ($days, $hours, $minutes, $seconds) = Delta_DHMS(@epoch, @date);
569              
570 0           my $date = $days + ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
571              
572             # Add a day for Excel's missing leap day in 1900
573 0 0         $date++ if ($date > 59);
574              
575 0           return $date;
576             }
577              
578              
579             ###############################################################################
580             ###############################################################################
581              
582             =head2 xl_parse_time($string)
583              
584              
585             Parameters: $string, a textual representation of a time
586              
587             Returns: A number that represents an Excel time
588             or undef for an invalid time.
589              
590             This function converts a time string into a number that represents an Excel time. The following time formats are valid:
591              
592             hh:mm [AM|PM]
593             hh:mm [AM|PM]
594             hh:mm:ss [AM|PM]
595             hh:mm:ss.ss [AM|PM]
596              
597              
598             The meridian, AM or PM, is optional and case insensitive. A 24 hour time is assumed if the meridian is omitted
599              
600             $time1 = xl_parse_time('12:18');
601             $time2 = xl_parse_time('12:18:14');
602             $time3 = xl_parse_time('12:18:14 AM');
603             $time4 = xl_parse_time('1:18:14 AM');
604              
605             Time in Excel is expressed as a fraction of the day in seconds. Therefore you can calculate an Excel time as follows:
606              
607             $time = ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
608              
609              
610             =cut
611             ###############################################################################
612             #
613             # xl_parse_time($string)
614             #
615             sub xl_parse_time {
616              
617 0     0 1   my $time = shift;
618              
619 0 0         if ($time =~ /(\d{1,2}):(\d\d):?((?:\d\d)(?:\.\d+)?)?(?:\s+)?(am|pm)?/i) {
620              
621 0           my $hours = $1;
622 0           my $minutes = $2;
623 0   0       my $seconds = $3 || 0;
624 0   0       my $meridian = lc($4) || '';
625              
626             # Normalise midnight and midday
627 0 0 0       $hours = 0 if ($hours == 12 && $meridian ne '');
628              
629             # Add 12 hours to the pm times. Note: 12.00 pm has been set to 0.00.
630 0 0         $hours += 12 if $meridian eq 'pm';
631              
632             # Calculate the time as a fraction of 24 hours in seconds
633 0           return ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
634              
635             }
636             else {
637 0           return undef; # Not a valid time string
638             }
639             }
640              
641              
642             ###############################################################################
643             ###############################################################################
644              
645             =head2 xl_parse_date($string)
646              
647              
648             Parameters: $string, a textual representation of a date and time
649              
650             Returns: A number that represents an Excel date
651             or undef for an invalid date.
652              
653             Requires: Date::Manip and Date::Calc
654              
655             This function converts a date and time string into a number that represents an Excel date.
656              
657             The parsing is performed using the C function of the Date::Manip module. Refer to the Date::Manip documentation for further information about the date and time formats that can be parsed. In order to use this function you will probably have to initialise some Date::Manip variables via the C function, see below.
658              
659             xl_parse_date_init("TZ=GMT","DateFormat=non-US");
660              
661             $date1 = xl_parse_date("11/7/97");
662             $date2 = xl_parse_date("Friday 11 July 1997");
663             $date3 = xl_parse_date("10:30 AM Friday 11 July 1997");
664             $date4 = xl_parse_date("Today");
665             $date5 = xl_parse_date("Yesterday");
666              
667             Note, if you parse a string that represents a time but not a date this function will add the current date. If you want the time without the date you can do something like the following:
668              
669             $time = xl_parse_date("10:30 AM");
670             $time -= int($time);
671              
672              
673             =cut
674             ###############################################################################
675             #
676             # xl_parse_date($string)
677             #
678             sub xl_parse_date {
679              
680 0     0 1   my $date = ParseDate($_[0]);
681              
682 0 0         return undef unless defined $date;
683              
684             # Unpack the return value from ParseDate()
685 0           my ($years, $months, $days, $hours, undef, $minutes, undef, $seconds) =
686             unpack("A4 A2 A2 A2 C A2 C A2", $date);
687              
688             # Convert to Excel date
689 0           return xl_date_list($years, $months, $days, $hours, $minutes, $seconds);
690             }
691              
692              
693              
694              
695             ###############################################################################
696             ###############################################################################
697              
698             =head2 xl_parse_date_init("variable=value", ...)
699              
700              
701             Parameters: A list of Date::Manip variable strings
702              
703             Returns: A list of all the Date::Manip strings
704              
705             Requires: Date::Manip
706              
707             This function is used to initialise variables required by the Date::Manip module. You should call this function before calling C. It need only be called once.
708              
709             This function is a thin wrapper for the C function. You can use C directly if you wish. Refer to the Date::Manip documentation for further information.
710              
711             xl_parse_date_init("TZ=MST","DateFormat=US");
712             $date1 = xl_parse_date("11/7/97"); # November 7th 1997
713              
714             xl_parse_date_init("TZ=GMT","DateFormat=non-US");
715             $date1 = xl_parse_date("11/7/97"); # July 11th 1997
716              
717              
718             =cut
719             ###############################################################################
720             #
721             # xl_parse_date_init("variable=value", ...)
722             #
723             sub xl_parse_date_init {
724              
725 0     0 1   Date_Init(@_); # How lazy is that.
726             }
727              
728              
729              
730              
731             ###############################################################################
732             ###############################################################################
733              
734             =head2 xl_decode_date_EU($string)
735              
736              
737             Parameters: $string, a textual representation of a date and time
738              
739             Returns: A number that represents an Excel date
740             or undef for an invalid date.
741              
742             Requires: Date::Calc
743              
744             This function converts a date and time string into a number that represents an Excel date.
745              
746             The date parsing is performed using the C function of the Date::Calc module. Refer to the Date::Calc for further information about the date formats that can be parsed. Also note the following from the Date::Calc documentation:
747              
748             "If the year is given as one or two digits only (i.e., if the year is less than 100), it is mapped to the window 1970 -2069 as follows":
749              
750             0 E= $year E 70 ==> $year += 2000;
751             70 E= $year E 100 ==> $year += 1900;
752              
753             The time portion of the string is parsed using the C function described above.
754              
755             Note: the EU in the function name means that a European date format is assumed if it is not clear from the string. See the first example below.
756              
757             $date1 = xl_decode_date_EU("11/7/97"); #11 July 1997
758             $date2 = xl_decode_date_EU("Sat 12 Sept 1998");
759             $date3 = xl_decode_date_EU("4:30 AM Sat 12 Sept 1998");
760              
761              
762             =cut
763             ###############################################################################
764             #
765             # xl_decode_date_EU($string)
766             #
767             sub xl_decode_date_EU {
768              
769 0 0   0 1   return undef unless @_;
770              
771 0           my $date = shift;
772 0           my @date;
773 0           my $time = 0;
774              
775             # Remove and decode the time portion of the string
776 0 0         if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
777 0           $time = xl_parse_time($1);
778 0 0         return undef unless defined $time;
779             }
780              
781             # Return if the string is now blank, i.e. it contained a time only.
782 0 0         return $time if $date =~ /^\s*$/;
783              
784             # Decode the date portion of the string
785 0           @date = Decode_Date_EU($date);
786 0 0         return undef unless @date;
787              
788 0           return xl_date_list(@date) + $time;
789             }
790              
791              
792              
793             ###############################################################################
794             ###############################################################################
795              
796             =head2 xl_decode_date_US($string)
797              
798              
799             Parameters: $string, a textual representation of a date and time
800              
801             Returns: A number that represents an Excel date
802             or undef for an invalid date.
803              
804             Requires: Date::Calc
805              
806             This function converts a date and time string into a number that represents an Excel date.
807              
808             The date parsing is performed using the C function of the Date::Calc module. Refer to the Date::Calc for further information about the date formats that can be parsed. Also note the following from the Date::Calc documentation:
809              
810             "If the year is given as one or two digits only (i.e., if the year is less than 100), it is mapped to the window 1970 -2069 as follows":
811              
812             0 <= $year < 70 ==> $year += 2000;
813             70 <= $year < 100 ==> $year += 1900;
814              
815             The time portion of the string is parsed using the C function described above.
816              
817             Note: the US in the function name means that an American date format is assumed if it is not clear from the string. See the first example below.
818              
819             $date1 = xl_decode_date_US("11/7/97"); # 7 November 1997
820             $date2 = xl_decode_date_US("12 Sept Saturday 1998");
821             $date3 = xl_decode_date_US("4:30 AM 12 Sept Sat 1998");
822              
823              
824             =cut
825             ###############################################################################
826             #
827             # xl_decode_date_US($string)
828             #
829             sub xl_decode_date_US {
830              
831 0 0   0 1   return undef unless @_;
832              
833 0           my $date = shift;
834 0           my @date;
835 0           my $time = 0;
836              
837             # Remove and decode the time portion of the string
838 0 0         if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
839 0           $time = xl_parse_time($1);
840 0 0         return undef unless defined $time;
841             }
842              
843             # Return if the string is now blank, i.e. it contained a time only.
844 0 0         return $time if $date =~ /^\s*$/;
845              
846             # Decode the date portion of the string
847 0           @date = Decode_Date_US($date);
848 0 0         return undef unless @date;
849              
850 0           return xl_date_list(@date) + $time;
851             }
852              
853              
854              
855              
856             ###############################################################################
857             ###############################################################################
858              
859             =head2 xl_date_1904($date)
860              
861              
862             Parameters: $date, an Excel date with a 1900 epoch
863              
864             Returns: an Excel date with a 1904 epoch or zero if
865             the $date is before 1904
866              
867              
868             This function converts an Excel date based on the 1900 epoch into a date based on the 1904 epoch.
869              
870              
871             $date1 = xl_date_list(2002, 1, 13); # 13 Jan 2002, 1900 epoch
872             $date2 = xl_date_1904($date1); # 13 Jan 2002, 1904 epoch
873              
874              
875             See also the C workbook method in the Spreadsheet::WriteExcelXML documentation.
876              
877             =cut
878             ###############################################################################
879             #
880             # xl_decode_date_US($string)
881             #
882             sub xl_date_1904 {
883              
884 0   0 0 1   my $date = $_[0] || 0;
885              
886 0 0         if ($date < 1462) {
887             # before 1904
888 0           $date = 0;
889             }
890             else {
891 0           $date -= 1462;
892             }
893              
894 0           return $date;
895             }
896              
897              
898              
899              
900              
901             =head1 REQUIREMENTS
902              
903             The date and time functions require functions from the C and C modules. The required functions are "autoused" from these modules so that you do not have to install them unless you wish to use the date and time routines. Therefore it is possible to use the row and column functions without having C and C installed.
904              
905             For more information about "autousing" refer to the documentation on the C pragma.
906              
907              
908              
909             =head1 BUGS
910              
911             When using the autoused functions from C and C on Perl 5.6.0 with C<-w> you will get a warning like this:
912              
913             "Subroutine xxx redefined ..."
914              
915             The current workaround for this is to put C near the beginning of your program.
916              
917              
918              
919             =head1 AUTHOR
920              
921             John McNamara jmcnamara@cpan.org
922              
923              
924              
925              
926             =head1 COPYRIGHT
927              
928             � MM-MMXI, John McNamara.
929              
930             All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
931              
932             =cut
933              
934              
935              
936             1;
937              
938              
939             __END__