時刻の差を求める(時刻に日付を含む)




  1. Perl




  2. 日付・時刻


 時刻に日付を含んでいる場合にその差を求めるにはTime::Localモジュールのtimelocal関数で、日付を含む時刻を秒に変換して差を求めます。

 ログなどでは書式化された時刻が記述されていますので、その状態から時刻の差を求めるサンプルを作成しました。

use strict;
use warnings;

# 差は2秒 これを計算で求める
print "(1)時刻の差を求める( 時刻に日付を含む )\n";
my $time_str1 = '30/Sep/2006:23:59:59'; 
my $time_str2 = '01/Oct/2006:00:00:01';


# 時刻の文字列表現を日付を含んだ時刻情報のハッシュに変換する。
my $time1 = time_from_str($time_str1);
my $time2 = time_from_str($time_str2);

# 時刻情報のハッシュを秒に変換して経過秒を計算
my $sec_interval = time_to_sec($time2) - time_to_sec($time1);

print "時刻の差は$sec_interval秒です。\n";


# 時刻の文字列表現を時刻情報を表すハッシュに変換する関数
sub time_from_str {
  my $time_str = shift;
  return unless $time_str;
  
  my $time = {};
  
  # 日付を含む時刻の書式にマッチしたら
  if ($time_str =~ m#^(\d{2})/(\w{3})/(\d{4}):(\d{2}):(\d{2}):(\d{2})$#) {
    
    #ハッシュを作成
    @{$time}{qw/mday mon year hour min sec/}
      = ($1, $2, $3, $4, $5, $6);
    
    #月の英語表記と月の番号を対応させたハッシュ
    my %num_of_mon = ( 
      jan => 0, feb => 1, mar => 2, apr => 3, may =>  4,  jun =>  5,
      jul => 6, aug => 7, sep => 8, oct => 9, nov => 10,  dec => 11,
    );

    # 月の表記が間違っている場合はreturn
    $time->{mon} = $num_of_mon{lc($time->{mon})};
    return unless defined $time->{mon};
    
    $time->{year} -= 1900;
    
    return wantarray ? %$time : $time;
  }
  return;
}

# 時刻を秒に変換する関数
sub time_to_sec {
  my $time;
  if (ref $_[0] eq 'HASH') {
    $time = shift;
  }
  else {
    %$time = @_;
  }
  
  return unless defined $time->{sec};
  return unless defined $time->{min};
  return unless defined $time->{hour};
  return unless defined $time->{mday};
  return unless defined $time->{mon};
  return unless defined $time->{year};
  
  # Time::Localモジュールを使用して秒に変換
  require Time::Local;
  my $sec = Time::Local::timelocal(
    $time->{sec},
    $time->{min},
    $time->{hour},
    $time->{mday},
    $time->{mon},
    $time->{year},
  );
  
  return $sec;
}

 実行結果

(1)時刻の差を求める( 時刻に日付を含む )
時刻の差は2秒です。

解説

時刻の文字列表現を時刻情報を表すハッシュに変換する

 '30/Sep/2006:23:59:59' のような日付を含む時刻を時刻情報を表すハッシュに変換しています。

 時刻情報を表すハッシュには、sec,min,hour,mday,mon,year という6つのキーを持たせています。

 正規表現で、日付と時刻を切り出してから,Sepのような月の英語名の表記を月の番号に変換しています。

sub time_from_str {
  my $time_str = shift;
  return unless $time_str;
  
  my $time = {};
  
  # 日付を含む時刻の書式にマッチしたら
  if ($time_str =~ m#^(\d{2})/(\w{3})/(\d{4}):(\d{2}):(\d{2}):(\d{2})$#) {
    
    #ハッシュを作成
    @{$time}{qw/mday mon year hour min sec/}
      = ($1, $2, $3, $4, $5, $6);
    
    #月の英語表記と月の番号を対応させたハッシュ
    my %num_of_mon = ( 
      jan => 0, feb => 1, mar => 2, apr => 3, may =>  4,  jun =>  5,
      jul => 6, aug => 7, sep => 8, oct => 9, nov => 10,  dec => 11,
    );

    # 月の表記が間違っている場合はreturn
    $time->{mon} = $num_of_mon{lc($time->{mon})};
    return unless defined $time->{mon};
    
    $time->{year} -= 1900;
    
    return wantarray ? %$time : $time;
  }
  return;
}

 その他は、「時刻の差を求める(24時間を越えない場合)」と同じです。

時刻を秒に変換する

 日付を含む時刻を秒に変換(エポック秒)に変換するにはTime::Localモジュールのtimelocal関数を使用します。

 その他は、「時刻の差を求める(24時間を越えない場合)」と同じです。

# 時刻情報のハッシュを秒に変換して経過秒を計算
my $sec_interval = time_to_sec($time2) - time_to_sec($time1);

# 時刻を秒に変換する関数
sub time_to_sec {
  my $time;
  if (ref $_[0] eq 'HASH') {
    $time = shift;
  }
  else {
    %$time = @_;
  }
  
  return unless defined $time->{sec};
  return unless defined $time->{min};
  return unless defined $time->{hour};
  return unless defined $time->{mday};
  return unless defined $time->{mon};
  return unless defined $time->{year};
  
  # Time::Localモジュールを使用して秒に変換
  require Time::Local;
  my $sec = Time::Local::timelocal(
    $time->{sec},
    $time->{min},
    $time->{hour},
    $time->{mday},
    $time->{mon},
    $time->{year},
  );
  
  return $sec;
}