On Sat, Apr 23, 2016 at 4:56 PM, Zefram <zef...@fysh.org> wrote:

> Eric Brine wrote:
> >The binary search found:
> ...
> >2013-11-03T03:45:00 UTC
> >2013-11-03T04:07:30 UTC
>
> You should only examine integral minutes.  Truncate to the minute
> when bisecting, before you pass your bisected time to DateTime.  Or,
> to avoid fractions entirely, extend the search radius to 2048 minutes.
> Either way, do not burden DateTime with any arithmetical job.
>
> Here's an implementation of what I mean:
>
>     sub start_of_day {
>         my($tgt_date_str, $zone) = @_;
>         $tgt_date_str =~ /\A([0-9]{4})-([0-9]{2})-([0-9]{2})\z/ or die;
>         my $tgt_date_ut = DateTime->new(year => "$1", month => "$2",
>                 day => "$3", time_zone => "UTC");
>         my($tgt_date_epoch_min) = $tgt_date_ut->epoch / 60;
>         my($tgt_date_rd) = $tgt_date_ut->utc_rd_values;
>         my $left_epoch_min = $tgt_date_epoch_min - 1440;
>         my $right_epoch_min = $tgt_date_epoch_min + 1440;
>         while(($right_epoch_min - $left_epoch_min) > 1) {
>             my $try_epoch_min = ($left_epoch_min + $right_epoch_min) >> 1;
>             my $try_dt = DateTime->from_epoch(epoch => $try_epoch_min*60,
>                     time_zone => $zone);
>             (($try_dt->local_rd_values)[0] >= $tgt_date_rd ?
>                     $right_epoch_min : $left_epoch_min) = $try_epoch_min;
>         }
>         return DateTime->from_epoch(epoch => $right_epoch_min*60);
>     }
>
> Use as in:
>
>     @ARGV == 2 or die;
>     my($tgt_date_str, $zone_name) = @ARGV;
>     my $zone = DateTime::TimeZone->new(name => $zone_name);
>     print start_of_day($tgt_date_str, $zone), "Z\n";
>
> -zefram
>

Thanks. I'll study this. I didn't think dividing by 60, adding 60 and
subtracting 60 was safe before of leap seconds.

Reply via email to