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.