From af95c671101e79fd83a83fb663d7743d45777fa7 Mon Sep 17 00:00:00 2001
From: Anton Vakhrushev <anwinged@ya.ru>
Date: Sun, 17 Nov 2019 20:20:05 +0300
Subject: [PATCH] Rewrite diff methods

---
 spec/entity_spec.cr    | 41 ++++++++++++++++++++++++++---
 spec/profile_spec.cr   | 41 +++++++----------------------
 src/dayoff/entities.cr | 52 +++++++++++++++++-------------------
 src/dayoff/profile.cr  | 60 ++++++++++++++++--------------------------
 4 files changed, 92 insertions(+), 102 deletions(-)

diff --git a/spec/entity_spec.cr b/spec/entity_spec.cr
index 5b0683e..d693f21 100644
--- a/spec/entity_spec.cr
+++ b/spec/entity_spec.cr
@@ -3,10 +3,43 @@ require "./spec_helper"
 module Dayoff::Test
   extend self
 
-  describe "can check same date" do
+  it "planned can calc in range" do
     planned_date = PlannedDate.new(t(1, 12), 8)
-    date = t(1, 20)
-    res = planned_date.same_date? date
-    res.should be_true
+    t1 = t(1, 0)
+    t2 = t(2, 0)
+    res = planned_date.in_range t1, t2
+    res.total_hours.should eq 8
+  end
+
+  it "planned can calc in range 2" do
+    planned_date = PlannedDate.new(t(3, 0), 8)
+    t1 = t(1, 0)
+    t2 = t(2, 0)
+    res = planned_date.in_range t1, t2
+    res.total_hours.should eq 0
+  end
+
+  it "planned can calc in range 2" do
+    planned_date = PlannedDate.new(d(1), 8)
+    t1 = t(1, 0).at_beginning_of_day
+    t2 = t(1, 0).at_end_of_day
+    res = planned_date.in_range t1, t2
+    res.total_hours.should eq 8
+  end
+
+  it "worked can calc in range" do
+    work_record = WorkRecord.new(t(1, 10), t(1, 20))
+    t1 = t(1, 15).at_beginning_of_day
+    t2 = t(1, 15)
+    res = work_record.in_range t1, t2
+    res.total_hours.should eq 5
+  end
+
+  it "worked can calc in range 2" do
+    work_record = WorkRecord.new(t(1, 10), t(1, 20))
+    t1 = t(2, 15).at_beginning_of_day
+    t2 = t(2, 15)
+    res = work_record.in_range t1, t2
+    res.total_hours.should eq 0
   end
 end
diff --git a/spec/profile_spec.cr b/spec/profile_spec.cr
index e4fb326..017f8ea 100644
--- a/spec/profile_spec.cr
+++ b/spec/profile_spec.cr
@@ -18,15 +18,6 @@ module Dayoff::Test
   end
 
   describe Profile do
-    it "can calc work hours" do
-      storage = MemoryStorage.new
-      storage.set_work_records [
-        WorkRecord.new(t(1, 10), t(1, 20)),
-      ]
-      prof = Profile.new(storage)
-      prof.get_work_hours(t(2, 0)).total_hours.should eq 10
-    end
-
     it "can write new record" do
       storage = MemoryStorage.new
       storage.set_work_records [
@@ -54,27 +45,6 @@ module Dayoff::Test
       records.last.finish.should eq finish_time
     end
 
-    it "can calc planned hours" do
-      prof = create_profile
-      span = prof.get_planned_hours t(3, 12)
-      expected = 8 * 3
-      expected.should eq span.total_hours
-    end
-
-    it "can calc work hours" do
-      prof = create_profile
-      span = prof.get_work_hours t(3, 12)
-      expected = 10 * 2
-      expected.should eq span.total_hours
-    end
-
-    it "can calc remaining time" do
-      prof = create_profile
-      span = prof.remaining_time t(3, 12)
-      expected = 8 * 3 - 10 * 2
-      expected.should eq span.total_hours
-    end
-
     it "not start twice" do
       prof = create_profile
       start_time = t(3, 10, 0)
@@ -95,10 +65,17 @@ module Dayoff::Test
       end
     end
 
+    it "can calc remaining time" do
+      prof = create_profile
+      span = prof.remaining_time t(3, 12)
+      expected = 8 * 3 - 10 * 2
+      expected.should eq span.total_hours
+    end
+
     it "can calc diff on concrete date" do
       prof = create_profile
-      span = prof.date_status d(1)
-      span.total_hours.should eq -2
+      span = prof.date_status t(1, 15)
+      span.total_hours.should eq 3
     end
   end
 end
diff --git a/src/dayoff/entities.cr b/src/dayoff/entities.cr
index 441eb0f..421daa0 100644
--- a/src/dayoff/entities.cr
+++ b/src/dayoff/entities.cr
@@ -23,12 +23,12 @@ module Dayoff
     def initialize(@date : Time, @hours : Int32)
     end
 
-    def time_span : Time::Span
-      Time::Span.new(hours: hours, minutes: 0, seconds: 0)
-    end
-
-    def same_date?(d : Time) : Bool
-      @date.date == d.date
+    def in_range(from_time : Time, to_time : Time) : Time::Span
+      if @date >= from_time && @date < to_time
+        Time::Span.new(hours: @hours, minutes: 0, seconds: 0)
+      else
+        Time::Span.zero
+      end
     end
   end
 
@@ -61,32 +61,28 @@ module Dayoff
       !@finish.nil?
     end
 
-    def finished_to_time?(time : Time) : Bool
-      @finish && @finish <= time
-    end
-
-    def calc_span(time : Time) : Time::Span
-      if @finish.nil?
-        if @start <= time
-          time - @start
-        else
-          Time::Span.zero
-        end
+    def in_range(from_time : Time, to_time : Time) : Time::Span
+      if finished?
+        in_range_finished from_time, to_time
       else
-        if time > finish!
-          finish! - @start
-        elsif time > @start
-          time - @start
-        else
-          Time::Span.zero
-        end
+        in_range_not_finished from_time, to_time
       end
     end
 
-    def on_date(d : Time) : Time::Span
-      if @start.date == d.date
-        fin = @finish || d
-        fin - start
+    private def in_range_finished(from_time : Time, to_time : Time) : Time::Span
+      if @start <= to_time && finish! >= from_time
+        normalized_start = Math.max(@start, from_time)
+        normalized_finish = Math.min(finish!, to_time)
+        normalized_finish - normalized_start
+      else
+        Time::Span.zero
+      end
+    end
+
+    private def in_range_not_finished(from_time : Time, to_time : Time) : Time::Span
+      if @start <= to_time
+        normalized_start = Math.max(@start, from_time)
+        to_time - normalized_start
       else
         Time::Span.zero
       end
diff --git a/src/dayoff/profile.cr b/src/dayoff/profile.cr
index 4e8759b..757fe17 100644
--- a/src/dayoff/profile.cr
+++ b/src/dayoff/profile.cr
@@ -21,21 +21,8 @@ module Dayoff
       @wrecords = @storage.get_work_records
     end
 
-    def get_planned_hours(on_time : Time) : Time::Span
-      check_date = on_time.at_beginning_of_day
-      @pdates.reduce(Time::Span.zero) do |acc, wd|
-        if wd.date <= check_date
-          acc + wd.time_span
-        else
-          acc
-        end
-      end
-    end
-
-    def get_work_hours(on_time : Time) : Time::Span
-      @wrecords.reduce(Time::Span.zero) do |acc, wr|
-        acc + wr.calc_span on_time
-      end
+    private def started_point
+      @wrecords.find { |x| x.started? }
     end
 
     def started? : Bool
@@ -65,36 +52,33 @@ module Dayoff
       @storage.set_work_records @wrecords
     end
 
-    def remaining_time(on_time : Time) : Time::Span
-      planned = get_planned_hours on_time
-      worked = get_work_hours on_time
-      planned - worked
-    end
-
-    def date_status(d : Time) : Time::Span
-      planned = get_planned_hours_on_date d
-      worked = get_work_hours_on_date d
-      planned - worked
-    end
-
-    private def get_planned_hours_on_date(d : Time) : Time::Span
-      @pdates.reduce(Time::Span.zero) do |acc, wd|
-        if wd.same_date? d
-          acc + wd.time_span
-        else
-          acc
-        end
+    def get_planned(from_time : Time, to_time : Time) : Time::Span
+      @pdates.reduce(Time::Span.zero) do |acc, pd|
+        acc + pd.in_range(from_time, to_time)
       end
     end
 
-    private def get_work_hours_on_date(d : Time) : Time::Span
+    def get_worked(from_time : Time, to_time : Time) : Time::Span
       @wrecords.reduce(Time::Span.zero) do |acc, wr|
-        acc + wr.on_date d
+        acc + wr.in_range(from_time, to_time)
       end
     end
 
-    private def started_point
-      @wrecords.find { |x| x.started? }
+    private def zero_time : Time
+      location = Time::Location.load("Europe/Moscow")
+      Time.local(1, 1, 1, 0, 0, location: location)
+    end
+
+    def remaining_time(on_time : Time) : Time::Span
+      planned = get_planned zero_time, on_time
+      worked = get_worked zero_time, on_time
+      planned - worked
+    end
+
+    def date_status(date : Time) : Time::Span
+      planned = get_planned date.at_beginning_of_day, date.at_end_of_day
+      worked = get_worked date.at_beginning_of_day, date
+      planned - worked
     end
   end
 end