in reply to Date column in excel


The format of Excel dates is explained in the Dates in Excel section of the Spreadsheet::WriteExcel manual.

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. The integer part of the number stores the number of days since the epoch and the fractional part stores the percentage of the day. ...

You can use the functions in Spreadsheet::WriteExcel::Utility or DateTime::Format::Excel to generate dates in this format.

Or, the following is a standalone function based on a routine in Spreadsheet::WriteExcelXML:

###################################################################### +######### # # convert_date_time($date_time_string, $epoch) # # The function takes a date and time in ISO8601 "yyyy-mm-ddThh:mm:ss.s +s" format # and converts it to a decimal number representing a valid Excel date. # # Dates and times in Excel are represented by real numbers. The intege +r part of # the number stores the number of days since the epoch and the fractio +nal part # stores the percentage of the day in seconds. The epoch can be either + 1900 or # 1904. # # Parameters: $date_time_string: String in one of the following format +s: # yyyy-mm-ddThh:mm:ss.ss # Standard # yyyy-mm-ddT # Date only # Thh:mm:ss.ss # Time only # # $epoch: Boolean. Set the epoch as 1900 or 1904. Defaults + to 1900. # # Returns: # A decimal number representing a valid Excel date, or # undef if the date is invalid. # sub convert_date_time { my $date_time = $_[0]; my $date_1904 = $_[1]; my $days = 0; # Number of days since epoch my $seconds = 0; # Time expressed as fraction of 24h hours in se +conds my ($year, $month, $day); my ($hour, $min, $sec); # Strip leading and trailing whitespace. $date_time =~ s/^\s+//; $date_time =~ s/\s+$//; # Check for invalid date char. return if $date_time =~ /[^0-9T:\-\.Z]/; # Check for "T" after date or before time. return unless $date_time =~ /\dT|T\d/; # Strip trailing Z in ISO8601 date. $date_time =~ s/Z$//; # Split into date and time. my ($date, $time) = split /T/, $date_time; # We allow the time portion of the input DateTime to be optional. if ($time ne '') { # Match hh:mm:ss.sss+ where the seconds are optional if ($time =~ /^(\d\d):(\d\d)(:(\d\d(\.\d+)?))?/) { $hour = $1; $min = $2; $sec = $4 || 0; } else { return undef; # Not a valid time format. } # Some boundary checks return if $hour >= 24; return if $min >= 60; return if $sec >= 60; # Excel expresses seconds as a fraction of the number in 24 ho +urs. $seconds = ($hour *60*60 + $min *60 + $sec) / (24 *60 *60); } # We allow the date portion of the input DateTime to be optional. return $seconds if $date eq ''; # Match date as yyyy-mm-dd. if ($date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) { $year = $1; $month = $2; $day = $3; } else { return undef; # Not a valid date format. } # Special cases for Excel. if (not $date_1904) { return $seconds if $date eq '1899-12-31'; # Excel 1900 ep +och return $seconds if $date eq '1900-01-00'; # Excel 1900 ep +och return 60 + $seconds if $date eq '1900-02-29'; # Excel false l +eapday } # We calculate the date by calculating the number of days since th +e epoch # and adjust for the number of leap days. We calculate the number +of leap # days by normalising the year in relation to the epoch. Thus the +year 2000 # becomes 100 for 4 and 100 year leapdays and 400 for 400 year lea +pdays. # my $epoch = $date_1904 ? 1904 : 1900; my $offset = $date_1904 ? 4 : 0; my $norm = 300; my $range = $year -$epoch; # Set month days and check for leap year. my @mdays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); my $leap = 0; $leap = 1 if $year % 4 == 0 and $year % 100 or $year % 400 +== 0; $mdays[1] = 29 if $leap; # Some boundary checks return if $year < $epoch or $year > 9999; return if $month < 1 or $month > 12; return if $day < 1 or $day > $mdays[$month -1]; # Accumulate the number of days since the epoch. $days = $day; # Add days for current + month $days += $mdays[$_] for 0 .. $month -2; # Add days for past mo +nths $days += $range *365; # Add days for past ye +ars $days += int(($range) / 4); # Add leapdays $days -= int(($range +$offset) /100); # Subtract 100 year le +apdays $days += int(($range +$offset +$norm)/400); # Add 400 year leapday +s $days -= $leap; # Already counted abov +e # Adjust for Excel erroneously treating 1900 as a leap year. $days++ if $date_1904 == 0 and $days > 59; return $days + $seconds; }

--
John.