[완료]Perl로 두 날짜 사이의 주말과 주일 뺀 일하는 날들의값 구하기 + 공휴일 포함하기

lovethecorners의 이미지

두 날짜가운에 주말과 주일을 뺀 일들의 갯수를 구하려합니다. 예를 들어 2009, 9, 9 과 2009, 10, 9 사이의 business days들의 값만 구하려고 하는데요,

Perl의 Date::Calc을 이용할수있지만 주말과 주일 모두 포함합니다.

my @first = (2009,9,9);
my @second = (2009,10,9);
my $days = Delta_Days( @first, @second );

이러면 30을 얻을수는 있는데, 주말과 주일을 빼고 얻는 방법은 없을까요? Date::Manip를 이용하면 어떻하면 될수도 있을것 같은데... 잘 않되네요.

bootmeta의 이미지

#!/usr/bin/perl -w
 
use strict;
use warnings;
 
package main;
 
use Date::Calc qw(:all);
 
sub get_biz_days {
    my @ymd1 = ($_[0], $_[1], $_[2]);
    my @ymd2 = ($_[3], $_[4], $_[5]);
 
    my $days = &Delta_Days( @ymd1, @ymd2 );
#    print "two different days delta is $days\n";
 
    use integer;
    my $week_count = $days / 7; # 완전한 7days(1주일) count
 
    my $biz_count = $week_count * 5;  # biz_count 초기화
    my $mod_count = $days % 7;        # 자투리 day
 
#    print "mod_count is $mod_count\n";
 
    my $w_day = Day_of_Week(@ymd1);
 
    for my $e (1 .. $mod_count) {         # 자투리 day들에 대해 주말 확인
        $biz_count += 1 if ($w_day < 6);  # 주말이 아니면 $biz_count 증가
        $w_day += 1;
        $w_day = 1 if (($w_day) > 7);  # 다음날이 sunday를 초과하면 monday로 다시 설정
    }
 
    return $biz_count;
}
 
my $biz_days = get_biz_days((2009,9,11), (2009,9,14));
print "biz_days is $biz_days \n";

perl은 기본 문법 밖에 모르니, 잘못된 부분이 있더라도 이해 부탁
잘못되거나 빠뜨린 부분이 있으면 가차없이 태클 부탁

ai의 이미지

토요일과 일요일만 제외하는 경우에 아래와 같이 계산할 수 있습니다.

#!/usr/bin/perl -w
# vim:ai ci et sm sw=2 sts=2
 
use strict;
use POSIX;
 
# example
print &get_biz_days((2009,9,9),(2009,10,9)), "\n";
 
# return business days between two dates
sub get_biz_days
{
  # date2 should be bigger than date1
  my $date1 = &_greg2jd(+shift,+shift,+shift);
  my $date2 = &_greg2jd(+shift,+shift,+shift);
 
  # count all days
  my $days = $date2 - $date1 + 1;
 
  # count non-working days
  my $sun = floor(($date2+1)/7) - ceil(($date1+1)/7) + 1;
  my $sat = floor(($date2+2)/7) - ceil(($date1+2)/7) + 1;
 
  return $days - $sun - $sat;
}
 
# return Julian period
sub _greg2jd
{
  use integer;
  # XXX : boundary check
  my $arg = { 'y' => shift, 'm' => shift, 'd' => shift, };
 
  my $a = (14-$arg->{m})/12;
  my $y = $arg->{y}+4800-$a;
  my $m = $arg->{m}+12*$a-3;
 
  my $j = $arg->{d} + (153*$m+2)/5 + $y*365 + $y/4 - $y/100 + $y/400 -32045;
  return $j;
}

--
War doesnt determine whos right, just whos left.

War doesnt determine whos right, just whos left.

aero의 이미지

Date::Calc 나 DateTime 모듈 같은 것을 사용해도 되지만 Perl 기본 Core 모듈이 아니라서
추가로 설치해야하죠.

날짜관련 모듈중에 Core모듈인 Time::Piece ( http://perldoc.perl.org/Time/Piece.html )라는 모듈이 있는데 이것을 이용해서 코딩해봤습니다.

#!/usr/bin/env perl
use strict;
use warnings;
use POSIX;
use Time::Piece;
 
print get_biz_days( '2009-09-07', '2009-09-14' ), "\n";
 
sub get_biz_days {
    my ( $from, $to ) = @_;
    $from = Time::Piece->strptime( $from, '%Y-%m-%d' )->mjd;
    $to   = Time::Piece->strptime(   $to, '%Y-%m-%d' )->mjd;
    my $func = sub {
        my ( $offset, $to, $from ) = @_;
        return floor( ( $to + $offset ) / 7 ) - ceil( ( $from +$offset ) / 7 ) + 1;
    };
    my ( $sun_cnt, $sat_cnt ) = ( $func->(4,$to,$from), $func->(5,$to,$from) );
    return $to - $from + 1 - $sun_cnt - $sat_cnt;
}

위에 분들도 잘 코딩해주셨지만 보면 현대적 Perl과는 좀 거리가 먼 형식( 함수호출시 &를 붙이는 등)이
좀 보이네요. 여기를 참고하시면 현대적 Perl 스타일로 변화하는데 도움이 되실겁니다.

ai의 이미지

아.. 그렇군요. 현대적 perl 스타일이라.. 공부가 많이 되었습니다. ^~^
perl 은 그저 손가는대로 코드를 써도 원하는대로 동작해 주는 언어라 별로 고민같은거 안하고 예전 스타일로 머물러 있었나 봅니다.

지금까지 투고하신 perl 관련글에서 도움을 많이 얻고 있고요, 특히 cpan 모듈을 잘 사용하시는 모습이 인상적이었습니다. 앞으로도 잘 부탁드리겠습니다!
(제가 쓰는 장비에서도 perl 버전이 좀 올라가면 좋으련만, 5.00503 에서는 Time::Piece 가 코어 모듈은 아니네요.)

--
War doesnt determine whos right, just whos left.

War doesnt determine whos right, just whos left.

aero의 이미지

현대적 Perl 5모습을 보시려면
http://ironman.enlightenedperl.org/ 의 RSS,ATOM을 구독하시면 많은 도움이 되실겁니다.

그러고 보니 Time::Piece는 Perl 5의 최신 버젼인 Perl 5.10.1 버젼에서 Core로 들어갔더군요.
제가 5.10.1을 쓰다보니 - -;

시스템의 Perl 버젼이 낮으면 따로 컴파일해서 까셔도 됩니다.
http://lumberjaph.net/blog/index.php/2009/08/23/perl-5101-released/ 를 참고

여러 버젼의 Perl이 시스템에 설치되어있을 경우 perl 바이너리의 경로를 shebang line에서 바꾸는것 보다는

PATH 환경변수를 적절히 조정하여 사용하고 싶은 perl 바이너리가 있는 경로가 먼저나오게 한다음
shebang line을

#!/usr/bin/env perl

로 하면 PATH환경 변수에서 먼저 찾아지는 perl로 실행을 합니다.

ai의 이미지

네.. 불편함을 견디다 못해서 일부 기계에는 perl 5.8 정도를 설치해 놓기도 했습니다.
그런데 관리해야 하는 기계가 좀 많은데다, 개중에는 인터넷과 단절되어 있기도 하고..
결국에는 가장 낮은 버전의 perl 에서도 돌아가는 코드를 쓰게 되더라구요.
운영 서버라는건 꽤 답답한 환경이구나..라고 생각하고 있습니다.

--
War doesnt determine whos right, just whos left.

War doesnt determine whos right, just whos left.

lovethecorners의 이미지

답글 달아 주신분들 많이 감사드립니다. 많은 도움이 되었습니다. 제목 수정을 했습니다.

전 Date::Manip가지고 머리 굴리다 이렇게도 되는것을 알았습니다.

my $date1=&ParseDate($string1);
my $date2=&ParseDate($string2);
my $delta=&DateCalc($date1,$date2,\$err,2);

리턴이 되는 값들중에 토요일과 일요일을 뺀 값을 보내주네요.

그런데 공휴일까지 넣어서 계산을 하려면 어떤 방법이 되는지 한번더 알려주시면 감사하겠습니다.

---------------------------
http://pwbmini.tistory.com/

aero의 이미지

http://search.cpan.org/perldoc?Date::Manip 문서를 보면

$d = DateCalc($d1,$d2 [,\$err] [,$mode]);

If $mode is 2, a business mode is used. That is, the calculation is done using business days, ignoring holidays, weekends, etc. In order to correctly use this mode, a config file must exist which contains the section defining holidays (see documentation on the config file below). The config file can also define the work week and the hours of the work day, so it is possible to have different config files for different businesses.

http://cpansearch.perl.org/src/SBECK/Date-Manip-5.54/DateManip.cnf 같은 설정파일을 만들어 놓으면
알아서 빼주는 것 같네요.

lovethecorners의 이미지

답글 감사합니다.

저도 문서를 다시읽어본후 파일을 만들어서 하면되겠구나 했는데, Manip.pm을 수정후에도 변화가 없었습니다. 그래서 다시 찾아보니 Date::Business를 사용할수도 있겠더군요. 그래서 예제에 있는 sub를 만들어서 테스트했는데 잘 되는것 같습니다.
---------------------------
http://pwbmini.tistory.com/

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.