|
| 1 | +// Copyright 2024 Gitea. All rights reserved. |
| 2 | +// SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +package util |
| 5 | + |
| 6 | +import ( |
| 7 | + "fmt" |
| 8 | + "regexp" |
| 9 | + "strconv" |
| 10 | + "strings" |
| 11 | + "sync" |
| 12 | +) |
| 13 | + |
| 14 | +type timeStrGlobalVarsType struct { |
| 15 | + units []struct { |
| 16 | + name string |
| 17 | + num int64 |
| 18 | + } |
| 19 | + re *regexp.Regexp |
| 20 | +} |
| 21 | + |
| 22 | +// When tracking working time, only hour/minute/second units are accurate and could be used. |
| 23 | +// For other units like "day", it depends on "how many working hours in a day": 6 or 7 or 8? |
| 24 | +// So at the moment, we only support hour/minute/second units. |
| 25 | +// In the future, it could be some configurable options to help users |
| 26 | +// to convert the working time to different units. |
| 27 | + |
| 28 | +var timeStrGlobalVars = sync.OnceValue[*timeStrGlobalVarsType](func() *timeStrGlobalVarsType { |
| 29 | + v := &timeStrGlobalVarsType{} |
| 30 | + v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`) |
| 31 | + v.units = []struct { |
| 32 | + name string |
| 33 | + num int64 |
| 34 | + }{ |
| 35 | + {"h", 60 * 60}, |
| 36 | + {"m", 60}, |
| 37 | + {"s", 1}, |
| 38 | + } |
| 39 | + return v |
| 40 | +}) |
| 41 | + |
| 42 | +func TimeEstimateParse(timeStr string) (int64, error) { |
| 43 | + if timeStr == "" { |
| 44 | + return 0, nil |
| 45 | + } |
| 46 | + var total int64 |
| 47 | + matches := timeStrGlobalVars().re.FindAllStringSubmatchIndex(timeStr, -1) |
| 48 | + if len(matches) == 0 { |
| 49 | + return 0, fmt.Errorf("invalid time string: %s", timeStr) |
| 50 | + } |
| 51 | + if matches[0][0] != 0 || matches[len(matches)-1][1] != len(timeStr) { |
| 52 | + return 0, fmt.Errorf("invalid time string: %s", timeStr) |
| 53 | + } |
| 54 | + for _, match := range matches { |
| 55 | + amount, err := strconv.ParseInt(timeStr[match[2]:match[3]], 10, 64) |
| 56 | + if err != nil { |
| 57 | + return 0, fmt.Errorf("invalid time string: %v", err) |
| 58 | + } |
| 59 | + unit := timeStr[match[4]:match[5]] |
| 60 | + found := false |
| 61 | + for _, u := range timeStrGlobalVars().units { |
| 62 | + if strings.ToLower(unit) == u.name { |
| 63 | + total += amount * u.num |
| 64 | + found = true |
| 65 | + break |
| 66 | + } |
| 67 | + } |
| 68 | + if !found { |
| 69 | + return 0, fmt.Errorf("invalid time unit: %s", unit) |
| 70 | + } |
| 71 | + } |
| 72 | + return total, nil |
| 73 | +} |
| 74 | + |
| 75 | +func TimeEstimateString(amount int64) string { |
| 76 | + var timeParts []string |
| 77 | + for _, u := range timeStrGlobalVars().units { |
| 78 | + if amount >= u.num { |
| 79 | + num := amount / u.num |
| 80 | + amount %= u.num |
| 81 | + timeParts = append(timeParts, fmt.Sprintf("%d%s", num, u.name)) |
| 82 | + } |
| 83 | + } |
| 84 | + return strings.Join(timeParts, " ") |
| 85 | +} |
0 commit comments