Utility.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. # DISCLAIMER OF WARRANTY
  2. # Because this software is licensed free of charge, there is no warranty for the software,
  3. # to the extent permitted by applicable law. Except when otherwise stated in writing
  4. # the copyright holders and/or other parties provide the software "as is" without
  5. # warranty of any kind, either expressed or implied, including, but not limited to,
  6. # the implied warranties of merchantability and fitness for a particular purpose.
  7. # The entire risk as to the quality and performance of the software is with you.
  8. # Should the software prove defective, you assume the cost of all necessary
  9. # servicing, repair, or correction.
  10. # In no event unless required by applicable law or agreed to in writing will any
  11. # copyright holder, or any other party who may modify and/or redistribute the software
  12. # as permitted by the above licence, be liable to you for damages, including any general,
  13. # special, incidental, or consequential damages arising out of the use or inability
  14. # to use the software (including but not limited to loss of data or data being rendered
  15. # inaccurate or losses sustained by you or third parties or a failure of the software
  16. # to operate with any other software), even if such holder or other party
  17. # has been advised of the possibility of such damages.
  18. # AUTHOR
  19. # John McNamara jmcnamara@cpan.org
  20. # COPYRIGHT
  21. # Copyright MM-MMX, John McNamara.
  22. # All Rights Reserved. This module is free software. It may be used,
  23. # redistributed and/or modified under the terms of
  24. # the Artistic License(full text of the Artistic License http://dev.perl.org/licenses/artistic.html).
  25. package Spreadsheet::WriteExcel::Utility;
  26. ###############################################################################
  27. #
  28. # Utility - Helper functions for Spreadsheet::WriteExcel.
  29. #
  30. # Copyright 2000-2010, John McNamara, jmcnamara@cpan.org
  31. #
  32. #
  33. use Exporter;
  34. use strict;
  35. use autouse 'Date::Calc' => qw(Delta_DHMS Decode_Date_EU Decode_Date_US);
  36. use autouse 'Date::Manip' => qw(ParseDate Date_Init);
  37. # Do all of the export preparation
  38. use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  39. # Row and column functions
  40. my @rowcol = qw(
  41. xl_rowcol_to_cell
  42. xl_cell_to_rowcol
  43. xl_range_formula
  44. xl_inc_row
  45. xl_dec_row
  46. xl_inc_col
  47. xl_dec_col
  48. );
  49. # Date and Time functions
  50. my @dates = qw(
  51. xl_date_list
  52. xl_date_1904
  53. xl_parse_time
  54. xl_parse_date
  55. xl_parse_date_init
  56. xl_decode_date_EU
  57. xl_decode_date_US
  58. );
  59. @ISA = qw(Exporter);
  60. @EXPORT_OK = ();
  61. @EXPORT = (@rowcol, @dates);
  62. %EXPORT_TAGS = (
  63. rowcol => \@rowcol,
  64. dates => \@dates
  65. );
  66. $VERSION = '2.37';
  67. =head1 NAME
  68. Utility - Helper functions for Spreadsheet::WriteExcel.
  69. =head1 SYNOPSIS
  70. Functions to help with some common tasks when using Spreadsheet::WriteExcel.
  71. These functions mainly relate to dealing with rows and columns in A1 notation and to handling dates and times.
  72. use Spreadsheet::WriteExcel::Utility; # Import everything
  73. ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
  74. $str = xl_rowcol_to_cell(1, 2); # C2
  75. $str = xl_inc_col('Z1' ); # AA1
  76. $str = xl_dec_col('AA1' ); # Z1
  77. $date = xl_date_list(2002, 1, 1); # 37257
  78. $date = xl_parse_date("11 July 1997"); # 35622
  79. $time = xl_parse_time('3:21:36 PM'); # 0.64
  80. $date = xl_decode_date_EU("13 May 2002"); # 37389
  81. =head1 DESCRIPTION
  82. This 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:
  83. Row and column functions: these are used to deal with Excel's A1 representation of cells. The functions in this category are:
  84. xl_rowcol_to_cell
  85. xl_cell_to_rowcol
  86. xl_range_formula
  87. xl_inc_row
  88. xl_dec_row
  89. xl_inc_col
  90. xl_dec_col
  91. 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:
  92. xl_date_list
  93. xl_date_1904
  94. xl_parse_time
  95. xl_parse_date
  96. xl_parse_date_init
  97. xl_decode_date_EU
  98. xl_decode_date_US
  99. All of these functions are exported by default. However, you can use import lists if you wish to limit the functions that are imported:
  100. use Spreadsheet::WriteExcel::Utility; # Import everything
  101. use Spreadsheet::WriteExcel::Utility qw(xl_date_list); # xl_date_list only
  102. use Spreadsheet::WriteExcel::Utility qw(:rowcol); # Row/col functions
  103. use Spreadsheet::WriteExcel::Utility qw(:dates); # Date functions
  104. =head1 ROW AND COLUMN FUNCTIONS
  105. Spreadsheet::WriteExcel supports two forms of notation to designate the position of cells: Row-column notation and A1 notation.
  106. 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:
  107. (0, 0) # The top left cell in row-column notation.
  108. ('A1') # The top left cell in A1 notation.
  109. (1999, 29) # Row-column notation.
  110. ('AD2000') # The same cell in A1 notation.
  111. Row-column notation is useful if you are referring to cells programmatically:
  112. for my $i (0 .. 9) {
  113. $worksheet->write($i, 0, 'Hello'); # Cells A1 to A10
  114. }
  115. A1 notation is useful for setting up a worksheet manually and for working with formulas:
  116. $worksheet->write('H1', 200);
  117. $worksheet->write('H2', '=H7+1');
  118. The functions in the following sections can be used for dealing with A1 notation, for example:
  119. ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
  120. $str = xl_rowcol_to_cell(1, 2); # C2
  121. Cell references in Excel can be either relative or absolute. Absolute references are prefixed by the dollar symbol as shown below:
  122. A1 # Column and row are relative
  123. $A1 # Column is absolute and row is relative
  124. A$1 # Column is relative and row is absolute
  125. $A$1 # Column and row are absolute
  126. 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.
  127. =cut
  128. ###############################################################################
  129. ###############################################################################
  130. =head2 xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
  131. Parameters: $row: Integer
  132. $col: Integer
  133. $row_absolute: Boolean (1/0) [optional, default is 0]
  134. $col_absolute: Boolean (1/0) [optional, default is 0]
  135. Returns: A string in A1 cell notation
  136. This function converts a zero based row and column cell reference to a A1 style string:
  137. $str = xl_rowcol_to_cell(0, 0); # A1
  138. $str = xl_rowcol_to_cell(0, 1); # B1
  139. $str = xl_rowcol_to_cell(1, 0); # A2
  140. The optional parameters C<$row_absolute> and C<$col_absolute> can be used to indicate if the row or column is absolute:
  141. $str = xl_rowcol_to_cell(0, 0, 0, 1); # $A1
  142. $str = xl_rowcol_to_cell(0, 0, 1, 0); # A$1
  143. $str = xl_rowcol_to_cell(0, 0, 1, 1); # $A$1
  144. See L<ROW AND COLUMN FUNCTIONS> for an explanation of absolute cell references.
  145. =cut
  146. ###############################################################################
  147. #
  148. # xl_rowcol_to_cell($row, $col, $row_absolute, $col_absolute)
  149. #
  150. sub xl_rowcol_to_cell {
  151. my $row = $_[0];
  152. my $col = $_[1];
  153. my $row_abs = $_[2] ? '$' : '';
  154. my $col_abs = $_[3] ? '$' : '';
  155. my $int = int ($col / 26);
  156. my $frac = $col % 26;
  157. my $chr1 =''; # Most significant character in AA1
  158. if ($int > 0) {
  159. $chr1 = chr( ord('A') + $int -1 );
  160. }
  161. my $chr2 = chr( ord('A') + $frac );
  162. # Zero index to 1-index
  163. $row++;
  164. return $col_abs . $chr1 . $chr2 . $row_abs. $row;
  165. }
  166. ###############################################################################
  167. ###############################################################################
  168. =head2 xl_cell_to_rowcol($string)
  169. Parameters: $string String in A1 format
  170. Returns: List ($row, $col)
  171. 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.
  172. my ($row, $col) = xl_cell_to_rowcol('A1'); # (0, 0)
  173. my ($row, $col) = xl_cell_to_rowcol('B1'); # (0, 1)
  174. my ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2)
  175. my ($row, $col) = xl_cell_to_rowcol('$C2' ); # (1, 2)
  176. my ($row, $col) = xl_cell_to_rowcol('C$2' ); # (1, 2)
  177. my ($row, $col) = xl_cell_to_rowcol('$C$2'); # (1, 2)
  178. =cut
  179. ###############################################################################
  180. #
  181. # xl_cell_to_rowcol($string)
  182. #
  183. # Returns: ($row, $col, $row_absolute, $col_absolute)
  184. #
  185. # The $row_absolute and $col_absolute parameters aren't documented because they
  186. # mainly used internally and aren't very useful to the user.
  187. #
  188. sub xl_cell_to_rowcol {
  189. my $cell = shift;
  190. $cell =~ /(\$?)([A-I]?[A-Z])(\$?)(\d+)/;
  191. my $col_abs = $1 eq "" ? 0 : 1;
  192. my $col = $2;
  193. my $row_abs = $3 eq "" ? 0 : 1;
  194. my $row = $4;
  195. # Convert base26 column string to number
  196. # All your Base are belong to us.
  197. my @chars = split //, $col;
  198. my $expn = 0;
  199. $col = 0;
  200. while (@chars) {
  201. my $char = pop(@chars); # LS char first
  202. $col += (ord($char) -ord('A') +1) * (26**$expn);
  203. $expn++;
  204. }
  205. # Convert 1-index to zero-index
  206. $row--;
  207. $col--;
  208. return $row, $col, $row_abs, $col_abs;
  209. }
  210. ###############################################################################
  211. ###############################################################################
  212. =head2 xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
  213. Parameters: $sheetname String
  214. $row_1: Integer
  215. $row_2: Integer
  216. $col_1: Integer
  217. $col_2: Integer
  218. Returns: A worksheet range formula as a string.
  219. This function converts zero based row and column cell references to an A1 style formula string:
  220. my $str = xl_range_formula('Sheet1', 0, 9, 0, 0); # =Sheet1!$A$1:$A$10
  221. my $str = xl_range_formula('Sheet2', 6, 65, 1, 1); # =Sheet2!$B$7:$B$66
  222. my $str = xl_range_formula('New data', 1, 8, 2, 2); # ='New data'!$C$2:$C$9
  223. This is useful for setting ranges in Chart objects:
  224. $chart->add_series(
  225. categories => xl_range_formula('Sheet1', 1, 9, 0, 0),
  226. values => xl_range_formula('Sheet1', 1, 9, 1, 1);,
  227. );
  228. # Which is the same as:
  229. $chart->add_series(
  230. categories => '=Sheet1!$A$2:$A$10',
  231. values => '=Sheet1!$B$2:$B$10',
  232. );
  233. =cut
  234. ###############################################################################
  235. #
  236. # xl_range_formula($sheetname, $row_1, $row_2, $col_1, $col_2)
  237. #
  238. sub xl_range_formula {
  239. my ($sheetname, $row_1, $row_2, $col_1, $col_2) = @_;
  240. # Use Excel's conventions and quote the sheet name if it contains any
  241. # non-word character or if it isn't already quoted.
  242. if ($sheetname =~ /\W/ && $sheetname !~ /^'/) {
  243. $sheetname = q(') . $sheetname . q(');
  244. }
  245. my $range1 = xl_rowcol_to_cell($row_1, $col_1, 1, 1);
  246. my $range2 = xl_rowcol_to_cell($row_2, $col_2, 1, 1);
  247. return '=' . $sheetname . '!' . $range1 . ':' . $range2;
  248. }
  249. ###############################################################################
  250. ###############################################################################
  251. =head2 xl_inc_row($string)
  252. Parameters: $string, a string in A1 format
  253. Returns: Incremented string in A1 format
  254. 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:
  255. my $str = xl_inc_row('A1' ); # A2
  256. my $str = xl_inc_row('B$2' ); # B$3
  257. my $str = xl_inc_row('$C3' ); # $C4
  258. my $str = xl_inc_row('$D$4'); # $D$5
  259. =cut
  260. ###############################################################################
  261. #
  262. # xl_inc_row($string)
  263. #
  264. sub xl_inc_row {
  265. my $cell = shift;
  266. my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
  267. return xl_rowcol_to_cell(++$row, $col, $row_abs, $col_abs);
  268. }
  269. ###############################################################################
  270. ###############################################################################
  271. =head2 xl_dec_row($string)
  272. Parameters: $string, a string in A1 format
  273. Returns: Decremented string in A1 format
  274. 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:
  275. my $str = xl_dec_row('A2' ); # A1
  276. my $str = xl_dec_row('B$3' ); # B$2
  277. my $str = xl_dec_row('$C4' ); # $C3
  278. my $str = xl_dec_row('$D$5'); # $D$4
  279. =cut
  280. ###############################################################################
  281. #
  282. # xl_dec_row($string)
  283. #
  284. # Decrements the row number of an Excel cell reference in A1 notation.
  285. # For example C4 to C3
  286. #
  287. # Returns: a cell reference string.
  288. #
  289. sub xl_dec_row {
  290. my $cell = shift;
  291. my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
  292. return xl_rowcol_to_cell(--$row, $col, $row_abs, $col_abs);
  293. }
  294. ###############################################################################
  295. ###############################################################################
  296. =head2 xl_inc_col($string)
  297. Parameters: $string, a string in A1 format
  298. Returns: Incremented string in A1 format
  299. 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:
  300. my $str = xl_inc_col('A1' ); # B1
  301. my $str = xl_inc_col('Z1' ); # AA1
  302. my $str = xl_inc_col('$B1' ); # $C1
  303. my $str = xl_inc_col('$D$5'); # $E$5
  304. =cut
  305. ###############################################################################
  306. #
  307. # xl_inc_col($string)
  308. #
  309. # Increments the column number of an Excel cell reference in A1 notation.
  310. # For example C3 to D3
  311. #
  312. # Returns: a cell reference string.
  313. #
  314. sub xl_inc_col {
  315. my $cell = shift;
  316. my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
  317. return xl_rowcol_to_cell($row, ++$col, $row_abs, $col_abs);
  318. }
  319. ###############################################################################
  320. ###############################################################################
  321. =head2 xl_dec_col($string)
  322. Parameters: $string, a string in A1 format
  323. Returns: Decremented string in A1 format
  324. 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:
  325. my $str = xl_dec_col('B1' ); # A1
  326. my $str = xl_dec_col('AA1' ); # Z1
  327. my $str = xl_dec_col('$C1' ); # $B1
  328. my $str = xl_dec_col('$E$5'); # $D$5
  329. =cut
  330. ###############################################################################
  331. #
  332. # xl_dec_col($string)
  333. #
  334. sub xl_dec_col {
  335. my $cell = shift;
  336. my ($row, $col, $row_abs, $col_abs) = xl_cell_to_rowcol($cell);
  337. return xl_rowcol_to_cell($row, --$col, $row_abs, $col_abs);
  338. }
  339. =head1 TIME AND DATE FUNCTIONS
  340. 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.
  341. 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.
  342. The epoch can be either 1900 or 1904. Excel for Windows uses 1900 and Excel for Macintosh uses 1904. The epochs are:
  343. 1900: 0 January 1900 i.e. 31 December 1899
  344. 1904: 1 January 1904
  345. Excel 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.
  346. 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
  347. 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<set_num_format()> method in the Spreadsheet::WriteExcel documentation:
  348. $date = xl_date_list(2001, 1, 1, 12, 30);
  349. $format->set_num_format('mmm d yyyy hh:mm AM/PM');
  350. $worksheet->write('A1', $date , $format); # Jan 1 2001 12:30 AM
  351. To 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.
  352. 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.
  353. =cut
  354. ###############################################################################
  355. ###############################################################################
  356. =head2 xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
  357. Parameters: $years: Integer
  358. $months: Integer [optional, default is 1]
  359. $days: Integer [optional, default is 1]
  360. $hours: Integer [optional, default is 0]
  361. $minutes: Integer [optional, default is 0]
  362. $seconds: Float [optional, default is 0]
  363. Returns: A number that represents an Excel date
  364. or undef for an invalid date.
  365. Requires: Date::Calc
  366. 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>.
  367. $date1 = xl_date_list(2002, 1, 2); # 2 Jan 2002
  368. $date2 = xl_date_list(2002, 1, 2, 12); # 2 Jan 2002 12:00 pm
  369. $date3 = xl_date_list(2002, 1, 2, 12, 30); # 2 Jan 2002 12:30 pm
  370. $date4 = xl_date_list(2002, 1, 2, 12, 30, 45); # 2 Jan 2002 12:30:45 pm
  371. 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.
  372. =cut
  373. ###############################################################################
  374. #
  375. # xl_date_list($years, $months, $days, $hours, $minutes, $seconds)
  376. #
  377. sub xl_date_list {
  378. return undef unless @_;
  379. my $years = $_[0];
  380. my $months = $_[1] || 1;
  381. my $days = $_[2] || 1;
  382. my $hours = $_[3] || 0;
  383. my $minutes = $_[4] || 0;
  384. my $seconds = $_[5] || 0;
  385. my @date = ($years, $months, $days, $hours, $minutes, $seconds);
  386. my @epoch = (1899, 12, 31, 0, 0, 0);
  387. ($days, $hours, $minutes, $seconds) = Delta_DHMS(@epoch, @date);
  388. my $date = $days + ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
  389. # Add a day for Excel's missing leap day in 1900
  390. $date++ if ($date > 59);
  391. return $date;
  392. }
  393. ###############################################################################
  394. ###############################################################################
  395. =head2 xl_parse_time($string)
  396. Parameters: $string, a textual representation of a time
  397. Returns: A number that represents an Excel time
  398. or undef for an invalid time.
  399. This function converts a time string into a number that represents an Excel time. The following time formats are valid:
  400. hh:mm [AM|PM]
  401. hh:mm [AM|PM]
  402. hh:mm:ss [AM|PM]
  403. hh:mm:ss.ss [AM|PM]
  404. The meridian, AM or PM, is optional and case insensitive. A 24 hour time is assumed if the meridian is omitted
  405. $time1 = xl_parse_time('12:18');
  406. $time2 = xl_parse_time('12:18:14');
  407. $time3 = xl_parse_time('12:18:14 AM');
  408. $time4 = xl_parse_time('1:18:14 AM');
  409. Time in Excel is expressed as a fraction of the day in seconds. Therefore you can calculate an Excel time as follows:
  410. $time = ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
  411. =cut
  412. ###############################################################################
  413. #
  414. # xl_parse_time($string)
  415. #
  416. sub xl_parse_time {
  417. my $time = shift;
  418. if ($time =~ /(\d{1,2}):(\d\d):?((?:\d\d)(?:\.\d+)?)?(?:\s+)?(am|pm)?/i) {
  419. my $hours = $1;
  420. my $minutes = $2;
  421. my $seconds = $3 || 0;
  422. my $meridian = lc($4) || '';
  423. # Normalise midnight and midday
  424. $hours = 0 if ($hours == 12 && $meridian ne '');
  425. # Add 12 hours to the pm times. Note: 12.00 pm has been set to 0.00.
  426. $hours += 12 if $meridian eq 'pm';
  427. # Calculate the time as a fraction of 24 hours in seconds
  428. return ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);
  429. }
  430. else {
  431. return undef; # Not a valid time string
  432. }
  433. }
  434. ###############################################################################
  435. ###############################################################################
  436. =head2 xl_parse_date($string)
  437. Parameters: $string, a textual representation of a date and time
  438. Returns: A number that represents an Excel date
  439. or undef for an invalid date.
  440. Requires: Date::Manip and Date::Calc
  441. This function converts a date and time string into a number that represents an Excel date.
  442. The 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.
  443. xl_parse_date_init("TZ=GMT","DateFormat=non-US");
  444. $date1 = xl_parse_date("11/7/97");
  445. $date2 = xl_parse_date("Friday 11 July 1997");
  446. $date3 = xl_parse_date("10:30 AM Friday 11 July 1997");
  447. $date4 = xl_parse_date("Today");
  448. $date5 = xl_parse_date("Yesterday");
  449. 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:
  450. $time = xl_parse_date("10:30 AM");
  451. $time -= int($time);
  452. =cut
  453. ###############################################################################
  454. #
  455. # xl_parse_date($string)
  456. #
  457. sub xl_parse_date {
  458. my $date = ParseDate($_[0]);
  459. return undef unless defined $date;
  460. # Unpack the return value from ParseDate()
  461. my ($years, $months, $days, $hours, undef, $minutes, undef, $seconds) =
  462. unpack("A4 A2 A2 A2 C A2 C A2", $date);
  463. # Convert to Excel date
  464. return xl_date_list($years, $months, $days, $hours, $minutes, $seconds);
  465. }
  466. ###############################################################################
  467. ###############################################################################
  468. =head2 xl_parse_date_init("variable=value", ...)
  469. Parameters: A list of Date::Manip variable strings
  470. Returns: A list of all the Date::Manip strings
  471. Requires: Date::Manip
  472. This 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.
  473. This 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.
  474. xl_parse_date_init("TZ=MST","DateFormat=US");
  475. $date1 = xl_parse_date("11/7/97"); # November 7th 1997
  476. xl_parse_date_init("TZ=GMT","DateFormat=non-US");
  477. $date1 = xl_parse_date("11/7/97"); # July 11th 1997
  478. =cut
  479. ###############################################################################
  480. #
  481. # xl_parse_date_init("variable=value", ...)
  482. #
  483. sub xl_parse_date_init {
  484. Date_Init(@_); # How lazy is that.
  485. }
  486. ###############################################################################
  487. ###############################################################################
  488. =head2 xl_decode_date_EU($string)
  489. Parameters: $string, a textual representation of a date and time
  490. Returns: A number that represents an Excel date
  491. or undef for an invalid date.
  492. Requires: Date::Calc
  493. This function converts a date and time string into a number that represents an Excel date.
  494. The 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:
  495. "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":
  496. 0 E<lt>= $year E<lt> 70 ==> $year += 2000;
  497. 70 E<lt>= $year E<lt> 100 ==> $year += 1900;
  498. The time portion of the string is parsed using the C<xl_parse_time()> function described above.
  499. 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.
  500. $date1 = xl_decode_date_EU("11/7/97"); #11 July 1997
  501. $date2 = xl_decode_date_EU("Sat 12 Sept 1998");
  502. $date3 = xl_decode_date_EU("4:30 AM Sat 12 Sept 1998");
  503. =cut
  504. ###############################################################################
  505. #
  506. # xl_decode_date_EU($string)
  507. #
  508. sub xl_decode_date_EU {
  509. return undef unless @_;
  510. my $date = shift;
  511. my @date;
  512. my $time = 0;
  513. # Remove and decode the time portion of the string
  514. if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
  515. $time = xl_parse_time($1);
  516. return undef unless defined $time;
  517. }
  518. # Return if the string is now blank, i.e. it contained a time only.
  519. return $time if $date =~ /^\s*$/;
  520. # Decode the date portion of the string
  521. @date = Decode_Date_EU($date);
  522. return undef unless @date;
  523. return xl_date_list(@date) + $time;
  524. }
  525. ###############################################################################
  526. ###############################################################################
  527. =head2 xl_decode_date_US($string)
  528. Parameters: $string, a textual representation of a date and time
  529. Returns: A number that represents an Excel date
  530. or undef for an invalid date.
  531. Requires: Date::Calc
  532. This function converts a date and time string into a number that represents an Excel date.
  533. The 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:
  534. "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":
  535. 0 <= $year < 70 ==> $year += 2000;
  536. 70 <= $year < 100 ==> $year += 1900;
  537. The time portion of the string is parsed using the C<xl_parse_time()> function described above.
  538. 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.
  539. $date1 = xl_decode_date_US("11/7/97"); # 7 November 1997
  540. $date2 = xl_decode_date_US("12 Sept Saturday 1998");
  541. $date3 = xl_decode_date_US("4:30 AM 12 Sept Sat 1998");
  542. =cut
  543. ###############################################################################
  544. #
  545. # xl_decode_date_US($string)
  546. #
  547. sub xl_decode_date_US {
  548. return undef unless @_;
  549. my $date = shift;
  550. my @date;
  551. my $time = 0;
  552. # Remove and decode the time portion of the string
  553. if ($date =~ s/(\d{1,2}:\d\d:?(\d\d(\.\d+)?)?(\s+)?(am|pm)?)//i) {
  554. $time = xl_parse_time($1);
  555. return undef unless defined $time;
  556. }
  557. # Return if the string is now blank, i.e. it contained a time only.
  558. return $time if $date =~ /^\s*$/;
  559. # Decode the date portion of the string
  560. @date = Decode_Date_US($date);
  561. return undef unless @date;
  562. return xl_date_list(@date) + $time;
  563. }
  564. ###############################################################################
  565. ###############################################################################
  566. =head2 xl_date_1904($date)
  567. Parameters: $date, an Excel date with a 1900 epoch
  568. Returns: an Excel date with a 1904 epoch or zero if
  569. the $date is before 1904
  570. This function converts an Excel date based on the 1900 epoch into a date based on the 1904 epoch.
  571. $date1 = xl_date_list(2002, 1, 13); # 13 Jan 2002, 1900 epoch
  572. $date2 = xl_date_1904($date1); # 13 Jan 2002, 1904 epoch
  573. See also the C<set_1904()> workbook method in the Spreadsheet::WriteExcel documentation.
  574. =cut
  575. ###############################################################################
  576. #
  577. # xl_decode_date_US($string)
  578. #
  579. sub xl_date_1904 {
  580. my $date = $_[0] || 0;
  581. if ($date < 1462) {
  582. # before 1904
  583. $date = 0;
  584. }
  585. else {
  586. $date -= 1462;
  587. }
  588. return $date;
  589. }
  590. =head1 REQUIREMENTS
  591. The 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.
  592. For more information about "autousing" refer to the documentation on the C<autouse> pragma.
  593. =head1 BUGS
  594. When 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:
  595. "Subroutine xxx redefined ..."
  596. The current workaround for this is to put C<use warnings;> near the beginning of your program.
  597. =head1 AUTHOR
  598. John McNamara jmcnamara@cpan.org
  599. =head1 COPYRIGHT
  600. © MM-MMX, John McNamara.
  601. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
  602. =cut
  603. 1;
  604. __END__