end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転します

小数が実体のexcel日付&時刻型を perlでパース

どうやら、excelの日付&時刻型の実体は、日付を整数部、時刻を小数部にした小数で保持しているみたい。

オリジナル excelの日付&時刻型
2011/12/4 9:42:35 40881.4045717593

Spreadsheet::ParseExcel を使えば、perlexcel(xls)ファイルを解析できますが、以前、この日付時刻型にハマったことがありましたが、Spreadsheet::WriteExcel::Utility の xl_parse_time() に、その答えがありました。

そこで、xl_parse_time() を写経して、excel形式との可逆変換を行えるparserを書いてみました。

#!/usr/local/bin/perl
use strict;
use Date::Calc qw(Delta_DHMS Add_Delta_Days);
use Data::Dumper;

my @EPOCH = (1899, 12, 31, 0, 0, 0);

main();

sub main {

    my @org_date_time = (2011,12,4,9,42,35);
    print sprintf("ORIGINAL DATE TIME: %d/%d/%d %d:%d:%d",@org_date_time),"\n";

    my $excel_date = date2excel_date(2011,12,4,9,42,35);
    print "ENCODE EXCEL TYPE : $excel_date\n";

    my @date_time = excel_date2date($excel_date);
    print sprintf("DECODE ORIGINAL   : %d/%d/%d %d:%d:%d",@date_time),"\n";

}

#refer to http://search.cpan.org/perldoc?Spreadsheet::WriteExcel::Utility
sub date2excel_date {
    my ($years, $months, $days, $hours, $minutes, $seconds) = @_;

    return undef unless $years;

    $months  ||= 1;
    $days    ||= 1;
    $hours   ||= 0;
    $minutes ||= 0;
    $seconds ||= 0;
    my @date = ($years, $months, $days, $hours, $minutes, $seconds);

    #日時の差分
    ($days, $hours, $minutes, $seconds) = Date::Calc::Delta_DHMS(@EPOCH, @date);

    my $date = $days + ($hours*3600 +$minutes*60 +$seconds)/(24*60*60);

    # Add a day for Excel's missing leap day in 1900  閏年...?
    $date++ if ($date > 59);

    return $date;
}

sub excel_date2date {
    my ($excel_date) = @_;

    $excel_date-- if $excel_date > 60;

    my $date_part = int($excel_date);
    my $time_part = $excel_date - $date_part;
    my @time = excel_time2time($time_part);

    my @date = Date::Calc::Add_Delta_Days(@EPOCH[0..2],$date_part);
    return (@date,@time);
}

sub excel_time2time {
    my ($excel_time) = @_;

    $excel_time = $excel_time*(24*60*60);

    my $hour =		int($excel_time / 3600);
    my $minutes =	int(($excel_time - $hour*3600) / 60);
    my $seconds =	$excel_time % 60;
    return ($hour,$minutes,$seconds);
}

実行結果

[endo@colinux tmp]$ ./foo.pl 
ORIGINAL DATE TIME: 2011/12/4 9:42:35
ENCODE EXCEL TYPE : 40881.4045717593
DECODE ORIGINAL   : 2011/12/4 9:42:35