LP#1635737 Add optional context to interval_to_seconds user/dbwells/lp1635737_interval_to_seconds_contextual
authorDan Wells <dbw2@calvin.edu>
Fri, 21 Jul 2017 18:17:34 +0000 (14:17 -0400)
committerDan Wells <dbw2@calvin.edu>
Fri, 21 Jul 2017 18:17:34 +0000 (14:17 -0400)
Any given interval (e.g. "1 month") can be a different amount of
seconds depending on the context (i.e. "1 month" after February 1 is
March 1, but "1 month" after March 1 is April 1, yet March is longer
than February).  This affects months all the time, but also can affect
days, hours, and even seconds once you consider DST and "leap" times.

By giving an optional context to interval_to_seconds, you can find the
true number of seconds in, for example, "1 month", when starting from
"February 1" (the context).

Signed-off-by: Dan Wells <dbw2@calvin.edu>
src/perl/lib/OpenSRF/Utils.pm

index f840967..90beb82 100644 (file)
@@ -7,6 +7,7 @@ use FileHandle;
 use Digest::MD5 qw(md5 md5_hex md5_base64);
 use Exporter;
 use DateTime;
+use DateTime::Duration;
 use DateTime::Format::ISO8601;
 use DateTime::TimeZone;
 
@@ -200,7 +201,7 @@ sub noo_es_time {
 }
 
 
-=head2 $thing->interval_to_seconds('interval') OR interval_to_seconds('interval')
+=head2 $thing->interval_to_seconds('interval', ['context']) OR interval_to_seconds('interval', ['context'])
 
 =head2 $thing->seconds_to_interval($seconds) OR seconds_to_interval($seconds)
 
@@ -245,29 +246,59 @@ for months (really (365 * 1d)/12 ... that may get smarter, though)
 
 for years (this is 365 * 1d)
 
+Passing in an optional 'context' (DateTime object) will give you the number of seconds for the passed interval *starting from* the given date (e.g. '1 month' from a context of 'February 1' would return the number of seconds needed to get to 'March 1', not the generic calculation of 1/12 of the seconds in a normal year).
+
 =back
 
 =cut
 sub interval_to_seconds {
-    my $self = shift;
-    my $interval = shift || $self;
+    my $class = shift; # throwaway
+    my $interval = ($class eq __PACKAGE__) ? shift : $class;
+    my $context = shift;
 
     $interval =~ s/(\d{2}):(\d{2}):(\d{2})/ $1 h $2 min $3 s /go;
 
     $interval =~ s/and/,/g;
     $interval =~ s/,/ /g;
 
-    my $amount = 0;
-    while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
-        my ($sign, $count, $type) = ($1, $2, $3);
-        $count = "$sign$count" if ($sign);
-        $amount += $count if ($type =~ /^s/);
-        $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
-        $amount += 60 * 60 * $count if ($type =~ /^h/);
-        $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
-        $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
-        $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
-        $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
+    my $amount;
+    if ($context) {
+        my $dur = DateTime::Duration->new();
+        while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
+            my ($sign, $count, $type) = ($1, $2, $3);
+            my $func = ($sign eq '-') ? 'subtract' : 'add';
+            if ($type =~ /^s/) {
+                $type = 'seconds';
+            } elsif ($type =~ /^m(?!o)/oi) {
+                $type = 'minutes';
+            } elsif ($type =~ /^h/) {
+                $type = 'hours';
+            } elsif ($type =~ /^d/oi) {
+                $type = 'days';
+            } elsif ($type =~ /^w/oi) {
+                $type = 'weeks';
+            } elsif ($type =~ /^mo/io) {
+                $type = 'months';
+            } elsif ($type =~ /^y/oi) {
+                $type = 'years';
+            }
+            $dur->$func($type => $count);
+        }
+        my $later = $context->clone->add_duration($dur);
+        $amount = $later->subtract_datetime_absolute($context)->in_units( 'seconds' );
+    } else {
+        $amount = 0;
+        while ($interval =~ /\s*([\+-]?)\s*(\d+)\s*(\w+)\s*/g) {
+            my ($sign, $count, $type) = ($1, $2, $3);
+            $count = "$sign$count" if ($sign);
+            $amount += $count if ($type =~ /^s/);
+            $amount += 60 * $count if ($type =~ /^m(?!o)/oi);
+            $amount += 60 * 60 * $count if ($type =~ /^h/);
+            $amount += 60 * 60 * 24 * $count if ($type =~ /^d/oi);
+            $amount += 60 * 60 * 24 * 7 * $count if ($type =~ /^w/oi);
+            $amount += ((60 * 60 * 24 * 365)/12) * $count if ($type =~ /^mo/io);
+            $amount += 60 * 60 * 24 * 365 * $count if ($type =~ /^y/oi);
+        }
     }
     return $amount;
 }