diff --git a/logs/periods.go b/logs/periods.go new file mode 100644 index 0000000000000000000000000000000000000000..536727df91ebfff8deb69a21826ad00f17ded2cf --- /dev/null +++ b/logs/periods.go @@ -0,0 +1,103 @@ +package logs + +import ( + "time" +) + +type Period struct { + Start time.Time `json:"start_time"` + End time.Time `json:"end_time"` +} + +func (p Period) Duration() time.Duration { + return p.End.Sub(p.Start) +} + +type Periods []Period + +func NewPeriods(start time.Time, end time.Time) Periods { + if end.Before(start) { + return []Period{} + } + return []Period{{Start: start, End: end}} +} + +func (ps Periods) Without(p Period) Periods { + if len(ps) == 0 { + return ps //nothing left to take from + } + if p.End.Before(ps[0].Start) { + return ps //before first period + } + if p.Start.After(ps[len(ps)-1].End) { + return ps //after last period + } + + //logger.Debugf("Start: %+v", ps) + nextIndex := 0 + for nextIndex < len(ps) && ps[nextIndex].End.Before(p.Start) { + //logger.Debugf("skip[%d]: %s > %s", nextIndex, p.Start, ps[nextIndex].End) + nextIndex++ + } + toDelete := []int{} + for nextIndex < len(ps) && ps[nextIndex].End.Before(p.End) { + if ps[nextIndex].Start.Before(p.Start) { + //trim tail + //logger.Debugf("tail[%d] %s->%s", nextIndex, ps[nextIndex].End, p.Start) + ps[nextIndex].End = p.Start + } else { + //delete this period completely and move to next + toDelete = append(toDelete, nextIndex) + //logger.Debugf("delete[%d] %s..%s", nextIndex, ps[nextIndex].Start, ps[nextIndex].End) + } + nextIndex++ + } + if nextIndex < len(ps) && ps[nextIndex].End.After(p.End) { + if ps[nextIndex].Start.Before(p.Start) { + //remove part of this period + ps = append(ps, Period{Start: p.End, End: ps[nextIndex].End}) + ps[nextIndex].End = p.Start + //logger.Debugf("split[%d]", nextIndex) + } else { + if ps[nextIndex].Start.Before(p.End) { + //trim head of period to start after removed peroid, then stop + //logger.Debugf("head[%d] %s->%s", nextIndex, ps[nextIndex].Start, p.End) + ps[nextIndex].Start = p.End + } + } + } + + //delete selected periods completely + newPS := []Period{} + for i, p := range ps { + if len(toDelete) > 0 && i == toDelete[0] { + toDelete = toDelete[1:] + } else { + newPS = append(newPS, p) + } + } + //logger.Debugf("final: %+v", newPS) + return newPS +} + +//Span is (last.end - first.start) +func (ps Periods) Span() time.Duration { + if len(ps) > 0 { + return ps[len(ps)-1].End.Sub(ps[0].Start) + } + return time.Duration(0) +} + +//Duration is sum of all period durations +func (ps Periods) Duration() time.Duration { + dur := time.Duration(0) + for _, p := range ps { + dur += p.Duration() + } + return dur +} + +//Gaps is (Span - Duration), i.e. time between periods +func (ps Periods) Gaps() time.Duration { + return ps.Span() - ps.Duration() +} diff --git a/logs/periods_test.go b/logs/periods_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e8804248aa15cd348d13b3077c259b35d330709b --- /dev/null +++ b/logs/periods_test.go @@ -0,0 +1,59 @@ +package logs_test + +import ( + "testing" + "time" + + "gitlab.com/uafrica/go-utils/logger" + "gitlab.com/uafrica/go-utils/logs" +) + +func TestPeriods(t *testing.T) { + logger.SetGlobalFormat(logger.NewConsole()) + logger.SetGlobalLevel(logger.LevelDebug) + t0 := time.Date(2021, 01, 01, 0, 0, 0, 0, time.Now().Location()) + ps := logs.NewPeriods(t0, t0.Add(time.Hour)) + t.Log(ps) + //ps: 0..60 + + //split[0] + ps1 := ps.Without(logs.Period{Start: t0.Add(time.Minute * 5), End: t0.Add(time.Minute * 10)}) + t.Log(ps1) + //-(5..10) -> ps1: 0..5, 10..60 + + //split[1] + ps2 := ps1.Without(logs.Period{Start: t0.Add(time.Minute * 15), End: t0.Add(time.Minute * 20)}) + t.Log(ps2) + //-(15..20) -> ps1: 0..5, 10..15, 20..60 + + //trim head[2] + ps3 := ps2.Without(logs.Period{Start: t0.Add(time.Minute * 18), End: t0.Add(time.Minute * 21)}) + t.Log(ps3) + //-(18..21) -> ps1: 0..5, 10..15, 21..60 + + //trim tail[1] + ps4 := ps3.Without(logs.Period{Start: t0.Add(time.Minute * 14), End: t0.Add(time.Minute * 19)}) + t.Log(ps4) + //-(14..19) -> ps1: 0..5, 10..14, 21..60 + + //tail, delete, head + ps5 := ps4.Without(logs.Period{Start: t0.Add(time.Minute * 4), End: t0.Add(time.Minute * 22)}) + t.Log(ps5) + //-(4..22) -> ps1: 0..4, 22..60 + + //over start + ps6 := ps5.Without(logs.Period{Start: t0.Add(-time.Minute * 1), End: t0.Add(time.Minute * 2)}) + t.Log(ps6) + //-(-1..2) -> ps1: 2..4, 22..60 + + //over end + ps7 := ps6.Without(logs.Period{Start: t0.Add(time.Minute * 50), End: t0.Add(time.Minute * 120)}) + t.Log(ps7) + //-(50..120) -> ps1: 2..4, 22..50 + + //all + ps8 := ps7.Without(logs.Period{Start: t0.Add(time.Minute * 0), End: t0.Add(time.Minute * 120)}) + t.Log(ps8) + //-(0..120) -> ps1: nil + +}