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