github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/coveragedb/time_period.go (about)

     1  // Copyright 2024 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package coveragedb
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"slices"
    10  	"sort"
    11  
    12  	"cloud.google.com/go/civil"
    13  )
    14  
    15  type TimePeriod struct {
    16  	DateTo civil.Date
    17  	Days   int
    18  	Type   string // DayPeriod, MonthPeriod, QuarterPeriod.
    19  }
    20  
    21  // DatesFromTo returns the closed range [fromDate, toDate].
    22  func (tp *TimePeriod) DatesFromTo() (civil.Date, civil.Date) {
    23  	return tp.DateTo.AddDays(-tp.Days + 1), tp.DateTo
    24  }
    25  
    26  func MakeTimePeriod(targetDate civil.Date, periodType string) (TimePeriod, error) {
    27  	pOps, err := PeriodOps(periodType)
    28  	if err != nil {
    29  		return TimePeriod{}, err
    30  	}
    31  	tp := TimePeriod{DateTo: targetDate, Days: pOps.pointedPeriodDays(targetDate), Type: periodType}
    32  	if !pOps.IsValidPeriod(tp) {
    33  		return TimePeriod{}, fmt.Errorf("date %s doesn't point the period(%s) end", targetDate.String(), periodType)
    34  	}
    35  	return tp, nil
    36  }
    37  
    38  const (
    39  	DayPeriod     = "day"
    40  	MonthPeriod   = "month"
    41  	QuarterPeriod = "quarter"
    42  )
    43  
    44  var AllPeriods = []string{DayPeriod, MonthPeriod, QuarterPeriod}
    45  
    46  var errUnknownTimePeriodType = errors.New("unknown time period type")
    47  
    48  func MinMaxDays(periodType string) (int, int, error) {
    49  	switch periodType {
    50  	case DayPeriod:
    51  		return 1, 1, nil
    52  	case MonthPeriod:
    53  		return 28, 31, nil
    54  	case QuarterPeriod:
    55  		return 31 + 28 + 31, 31 + 30 + 31, nil
    56  	default:
    57  		return 0, 0, errUnknownTimePeriodType
    58  	}
    59  }
    60  
    61  func PeriodOps(periodType string) (periodOps, error) {
    62  	switch periodType {
    63  	case DayPeriod:
    64  		return &DayPeriodOps{}, nil
    65  	case MonthPeriod:
    66  		return &MonthPeriodOps{}, nil
    67  	case QuarterPeriod:
    68  		return &QuarterPeriodOps{}, nil
    69  	default:
    70  		return nil, errUnknownTimePeriodType
    71  	}
    72  }
    73  
    74  type periodOps interface {
    75  	IsValidPeriod(p TimePeriod) bool
    76  	lastPeriodDate(d civil.Date) civil.Date
    77  	pointedPeriodDays(d civil.Date) int
    78  }
    79  
    80  func GenNPeriodsTill(n int, d civil.Date, periodType string) ([]TimePeriod, error) {
    81  	pOps, err := PeriodOps(periodType)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	var res []TimePeriod
    86  	for i := 0; i < n; i++ {
    87  		d = pOps.lastPeriodDate(d)
    88  		res = append(res, TimePeriod{DateTo: d, Days: pOps.pointedPeriodDays(d), Type: periodType})
    89  		d = d.AddDays(-pOps.pointedPeriodDays(d))
    90  	}
    91  	slices.Reverse(res)
    92  	return res, nil
    93  }
    94  
    95  type DayPeriodOps struct{}
    96  
    97  func (dpo *DayPeriodOps) lastPeriodDate(d civil.Date) civil.Date {
    98  	return d
    99  }
   100  
   101  func (dpo *DayPeriodOps) IsValidPeriod(p TimePeriod) bool {
   102  	return p.Days == 1
   103  }
   104  
   105  func (dpo *DayPeriodOps) pointedPeriodDays(d civil.Date) int {
   106  	return 1
   107  }
   108  
   109  type MonthPeriodOps struct{}
   110  
   111  func (m *MonthPeriodOps) lastPeriodDate(d civil.Date) civil.Date {
   112  	d.Day = 1
   113  	d = d.AddDays(32)
   114  	d.Day = 1
   115  	return d.AddDays(-1)
   116  }
   117  
   118  func (m *MonthPeriodOps) IsValidPeriod(p TimePeriod) bool {
   119  	lmd := m.lastPeriodDate(p.DateTo)
   120  	return lmd == p.DateTo && p.Days == lmd.Day
   121  }
   122  
   123  func (m *MonthPeriodOps) pointedPeriodDays(d civil.Date) int {
   124  	return m.lastPeriodDate(d).Day
   125  }
   126  
   127  type QuarterPeriodOps struct{}
   128  
   129  func (q *QuarterPeriodOps) IsValidPeriod(p TimePeriod) bool {
   130  	lmd := q.lastPeriodDate(p.DateTo)
   131  	return lmd == p.DateTo && p.Days == q.pointedPeriodDays(lmd)
   132  }
   133  
   134  func (q *QuarterPeriodOps) lastPeriodDate(d civil.Date) civil.Date {
   135  	d.Month = ((d.Month-1)/3)*3 + 3
   136  	d.Day = 1
   137  	return (&MonthPeriodOps{}).lastPeriodDate(d)
   138  }
   139  
   140  func (q *QuarterPeriodOps) pointedPeriodDays(d civil.Date) int {
   141  	d = q.lastPeriodDate(d)
   142  	d.Day = 1
   143  	res := 0
   144  	for i := 0; i < 3; i++ {
   145  		res += (&MonthPeriodOps{}).pointedPeriodDays(d)
   146  		d.Month--
   147  	}
   148  	return res
   149  }
   150  
   151  func PeriodsToMerge(srcDates, mergedPeriods []TimePeriod, srcRows, mergedRows []int64, ops periodOps) []TimePeriod {
   152  	periodRows := map[civil.Date]int64{}
   153  	for i, srcDate := range srcDates {
   154  		periodID := ops.lastPeriodDate(srcDate.DateTo)
   155  		periodRows[periodID] += srcRows[i]
   156  	}
   157  	for i, period := range mergedPeriods {
   158  		if !ops.IsValidPeriod(period) {
   159  			continue
   160  		}
   161  		mergerPeriodID := period.DateTo
   162  		if rowsAvailable, ok := periodRows[mergerPeriodID]; ok && rowsAvailable == mergedRows[i] {
   163  			delete(periodRows, mergerPeriodID)
   164  		}
   165  	}
   166  	periods := []TimePeriod{}
   167  	for periodEndDate := range periodRows {
   168  		periods = append(periods,
   169  			TimePeriod{DateTo: periodEndDate, Days: ops.pointedPeriodDays(periodEndDate)})
   170  	}
   171  	sort.Slice(periods, func(i, j int) bool {
   172  		return periods[i].DateTo.After(periods[j].DateTo)
   173  	})
   174  	return periods
   175  }
   176  
   177  func AtMostNLatestPeriods(periods []TimePeriod, n int) []TimePeriod {
   178  	sort.Slice(periods, func(i, j int) bool {
   179  		return periods[i].DateTo.After(periods[j].DateTo)
   180  	})
   181  	if len(periods) <= n {
   182  		return periods
   183  	}
   184  	return periods[:n]
   185  }