<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    On 7/12/2012 8:45 PM, Joel Berger wrote:<br>
    <blockquote
cite="mid:CAAMA-9PjV02dHNd7EFHn7ZrLTdYGh-vUq41hPAMbrw3MHY16tQ@mail.gmail.com"
      type="cite">
      <pre wrap="">I tried this using a free web service, and I got right to the point of
dealing with DateTime math, so I leave that to you :-)

The webservice is at <a class="moz-txt-link-freetext" href="http://www.holidaywebservice.com">http://www.holidaywebservice.com</a>
my code is at <a class="moz-txt-link-freetext" href="https://gist.github.com/3102219">https://gist.github.com/3102219</a>

All it does now is get the holidays this month. From there, after
deciding if you have passed the last one, add a month and request
again. Repeat as needed.</pre>
    </blockquote>
    <br>
    I took a crack at it as well.  My goal was simplicity without using
    a lot of complex (and perhaps poorly documented) objects that like
    DateTime::Set::ICal .<br>
    <br>
    I saw magically generating holidays as the hard part and I'd prefer
    not to be tied to a web service, so I found a list of holidays
    online and pasted them into a __DATA__ section.  <br>
    <br>
    Once you have that semi-ugly solution, the date logic is very
    simple: just compare the whole list of dates to today, or a date of
    your choice; discard past dates and return the date with the
    smallest delta.   <br>
    <br>
    I don't know what Richard is trying to do, but this script gives
    complete control over what's a holiday and once you find the
    holidays, it's self-contained.  Of course,  it will run out of
    holidays some day; as written, the script will stop working in
    2014.  It probably wouldn't take long to add 50 years of holidays. 
    If you were running this script several times a second on a server
    AND you had 50 years of holidays, you might need a more
    refined/elegant algorithm (e.g., ignore all years' data except this
    year and next year).<br>
    <br>
    I did find Date::Manip extraordinarily frustrating to work with. 
    This line took 10 minutes of my life:<br>
    <br>
        my $sec = Delta_Format($d, 0, "%sh"); # convert to seconds<br>
    <br>
    because Delta_Format advertises:<br>
    <br>
    <pre class="sh_perl sh_sourceCode" style="background-color: rgb(255, 255, 255); border: 1px solid rgb(136, 136, 136); color: rgb(0, 0, 0); padding: 1em; white-space: pre; font-weight: normal; font-style: normal; font-variant: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-position: initial initial; background-repeat: initial initial; ">   <span class="sh_variable" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">%Xv</span>  <span class="sh_symbol">:</span> <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">print</span> the value of the field X

   <span class="sh_variable" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">%Xd</span>  <span class="sh_symbol">:</span> <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">print</span> the value of the field X <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">and</span> all
          smaller units in terms of X

   <span class="sh_variable" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">%Xh</span>  <span class="sh_symbol">:</span> <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">print</span> the value of field X <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">and</span> all
          larger units in terms of X

   <span class="sh_variable" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">%Xt</span>  <span class="sh_symbol">:</span> <span class="sh_keyword" style="color: rgb(178, 104, 24); font-weight: normal; font-style: normal; ">print</span> the value of all fields in
          terms of X</pre>
    <br>
    So I originally tried to convert to days directly:<br>
    <br>
        my $days = Delta_Format($d, 2, "%dt"); # convert to days<br>
    <br>
    but this always returns zero because the deltas all look like
    0:0:0:0:1272:0:0.  I would think that 1272 hours "in terms of X"
    where X=days would be something like 53 days.  But I was wrong,
    somehow it's 0 days.  I have no idea why the deltas are like this. 
    Date::Manip::Delta seems to suggest that the deltas returned from
    Date::Manip functions should be normalized (whatever "been made
    consistent with the type of data they represent" means) and the
    deltas I was getting seems non-normalized.  So maybe that's a bug or
    maybe there's some reason like hours results in an integer
    representation or something.  But when I switched to seconds, it
    works.  That kinda sucks, but who knows WTF is supposed to be
    happening because X isn't actually ever defined in the description
    of Delta_Format()  ... Maybe 'd' doesn't means days and is
    undefined, so I get a zero. I first thought that X=4 because I
    wanted the fourth field... But no, if you review Date::Manip::Delta
    ... well, it certainly won't condescend to even mentioning
    Delta_Format() but it does describe a printf method and says "Here,
    X is one of (y,M,w,d,h,m,s)".  Examining Date::Manip::Examples might
    also have helped clue me to X.  So, if you like programming to be
    like a text adventure game... Date::Manip is the library for you!<br>
    <br>
    Anyway, here's what I get when I run this today (just after midnight
    in Chicago on 7/13/2012):<br>
    <br>
    [amead@cow3 perl]$ ./tmp.pl<br>
    The next holiday is Labor Day on Sep 3, 2012 in about 52 days<br>
    The next holiday after Jan 2, 2013 is Martin Luther King Day on Jan
    21, 2013 in about 19 days<br>
    <br>
    And below is the script.  <br>
    <br>
    -Alan<br>
    <br>
    #!/usr/bin/perl<br>
    <br>
    use strict;<br>
    use warnings;<br>
    <br>
    <br>
    use Date::Manip;<br>
    # I'm going to assume DM6;see the Date::Manip documentation<br>
    <br>
    # read the holiday data<br>
    our %holidays = ();<br>
    while (my $line = <DATA>) {<br>
      $line =~ s/[\r\n]+//g; # remove newlines<br>
      $line =~ s/#.*$//g;  # remove comments<br>
      $line =~ s/^\s+//g;  # remove leading psaces<br>
      $line =~ s/\s+$//g;  # remove trailing psaces<br>
      $line =~ s/\s*\|\s*/|/g;  # remove spaces around the delimiter<br>
      next unless( $line ); # skip blank/comment lines<br>
      my($date, $holiday) = split /\|/, $line;<br>
      $holidays{$date} = $holiday; # NB: don't reverse this<br>
    }<br>
    <br>
    close(DATA);<br>
    <br>
    { # first example<br>
    <br>
      my($inseconds, $next_holday, $next_date) = next_holiday();<br>
      printf "The next holiday is $next_holday on $next_date in about
    %.0f days\n", $inseconds/(24*3600);<br>
    <br>
    }<br>
    <br>
    { # second example<br>
    <br>
      my $today = 'Jan 2, 2013';<br>
      my($inseconds, $next_holday, $next_date) = next_holiday( $today );<br>
      printf "The next holiday after $today is $next_holday on
    $next_date in about %.0f days\n", $inseconds/(24*3600);<br>
    <br>
    }<br>
    <br>
    <br>
    sub next_holiday {<br>
      my $from_date = shift || 'today';<br>
    <br>
      my $soonest = 32000000; # slightly more than a year of seconds<br>
      my $soonest_date = '';<br>
      my $soonest_hol = '';<br>
      for my $date ( keys %holidays ) {<br>
        my $d = DateCalc($from_date,$date);<br>
        #print "date-$date, hol=$holidays{$date}, d=$d\n";<br>
        my $sec = Delta_Format($d, 0, "%sh"); # convert to seconds<br>
        next unless( $sec >= 0 ); # skip past holidays<br>
        #print "$date, $holidays{$date} : sec=$sec, soonest=$soonest\n";<br>
        if( $sec < $soonest ) {<br>
          $soonest = $sec;<br>
          $soonest_date = $date;<br>
          $soonest_hol = $holidays{$date};<br>
        }<br>
      }<br>
    <br>
      return( $soonest, $soonest_hol, $soonest_date );<br>
    <br>
    }<br>
    <br>
    <br>
    __DATA__<br>
    # cut-and-pasted from
    <a class="moz-txt-link-freetext" href="http://www.timeanddate.com/calendar/?year=2012&country=1">http://www.timeanddate.com/calendar/?year=2012&country=1</a><br>
    # and <a class="moz-txt-link-freetext" href="http://www.timeanddate.com/calendar/?year=2013&country=1">http://www.timeanddate.com/calendar/?year=2013&country=1</a><br>
    Jan 1, 2012|New Year's Day<br>
    Jan 2, 2012|New Year's Day observed<br>
    Jan 16, 2012|Martin Luther King Day<br>
    Feb 14, 2012|Valentine's Day<br>
    Feb 20, 2012|Presidents' Day<br>
    Apr 8, 2012|Easter Sunday<br>
    May 13, 2012|Mother's Day<br>
    May 28, 2012|Memorial Day<br>
    Jun 17, 2012|Father's Day<br>
    Jul 4, 2012|Independence Day<br>
    Sep 3, 2012|Labor Day<br>
    Oct 8, 2012|Columbus Day (Most regions)<br>
    Oct 31, 2012|Halloween<br>
    Nov 6, 2012|Election Day<br>
    Nov 11, 2012|Veterans Day<br>
    Nov 12, 2012|Veterans Day observed<br>
    Nov 22, 2012|Thanksgiving Day<br>
    Dec 24, 2012|Christmas Eve<br>
    Dec 25, 2012|Christmas Day<br>
    Dec 31, 2012|New Year's Eve<br>
    Jan 1, 2013|New Year's Day<br>
    Jan 21, 2013|Martin Luther King Day<br>
    Feb 14, 2013|Valentine's Day<br>
    Feb 18, 2013|Presidents' Day<br>
    Mar 31, 2013|Easter Sunday<br>
    May 12, 2013|Mother's Day<br>
    May 27, 2013|Memorial Day<br>
    Jun 16, 2013|Father's Day<br>
    Jul 4, 2013|Independence Day<br>
    Sep 2, 2013|Labor Day<br>
    Oct 14, 2013|Columbus Day (Most regions)<br>
    Oct 31, 2013|Halloween<br>
    Nov 11, 2013|Veterans Day<br>
    Nov 28, 2013|Thanksgiving Day<br>
    Dec 24, 2013|Christmas Eve<br>
    Dec 25, 2013|Christmas Day<br>
    Dec 31, 2013|New Year's Eve<br>
    # paste more data here for additional years...<br>
    <br>
    <br>
    <br>
    <pre class="moz-signature" cols="72">-- 
Alan D. Mead, Ph.D.
Assistant Professor of Industrial and Organizational Psychology
College of Psychology
Illinois Institute of Technology
3101 South Dearborn, 2nd floor
Chicago IL 60616

+312.567.5933 (Campus)
+815.588.3846 (Home Office)
+312.567.3493 (Fax)

<a class="moz-txt-link-freetext" href="http://www.iit.edu/~mead">http://www.iit.edu/~mead</a>
<a class="moz-txt-link-freetext" href="http://www.alanmead.org">http://www.alanmead.org</a>

Announcing the Journal of Computerized Adaptive Testing (JCAT), a
peer-reviewed electronic journal designed to advance the science and
practice of computerized adaptive testing: <a class="moz-txt-link-freetext" href="http://www.iacat.org/jcat">http://www.iacat.org/jcat</a></pre>
    <br>
    <br>
  </body>
</html>