aboutsummaryrefslogtreecommitdiffstats
path: root/modules/util/time_str.go
blob: 0fccfe82cc1cb46f264cc84d27ede2accecd55b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright 2024 Gitea. All rights reserved.
// SPDX-License-Identifier: MIT

package util

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

type timeStrGlobalVarsType struct {
	units []struct {
		name string
		num  int64
	}
	re *regexp.Regexp
}

// When tracking working time, only hour/minute/second units are accurate and could be used.
// For other units like "day", it depends on "how many working hours in a day": 6 or 7 or 8?
// So at the moment, we only support hour/minute/second units.
// In the future, it could be some configurable options to help users
// to convert the working time to different units.

var timeStrGlobalVars = sync.OnceValue(func() *timeStrGlobalVarsType {
	v := &timeStrGlobalVarsType{}
	v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`)
	v.units = []struct {
		name string
		num  int64
	}{
		{"h", 60 * 60},
		{"m", 60},
		{"s", 1},
	}
	return v
})

func TimeEstimateParse(timeStr string) (int64, error) {
	if timeStr == "" {
		return 0, nil
	}
	var total int64
	matches := timeStrGlobalVars().re.FindAllStringSubmatchIndex(timeStr, -1)
	if len(matches) == 0 {
		return 0, fmt.Errorf("invalid time string: %s", timeStr)
	}
	if matches[0][0] != 0 || matches[len(matches)-1][1] != len(timeStr) {
		return 0, fmt.Errorf("invalid time string: %s", timeStr)
	}
	for _, match := range matches {
		amount, err := strconv.ParseInt(timeStr[match[2]:match[3]], 10, 64)
		if err != nil {
			return 0, fmt.Errorf("invalid time string: %v", err)
		}
		unit := timeStr[match[4]:match[5]]
		found := false
		for _, u := range timeStrGlobalVars().units {
			if strings.ToLower(unit) == u.name {
				total += amount * u.num
				found = true
				break
			}
		}
		if !found {
			return 0, fmt.Errorf("invalid time unit: %s", unit)
		}
	}
	return total, nil
}

func TimeEstimateString(amount int64) string {
	var timeParts []string
	for _, u := range timeStrGlobalVars().units {
		if amount >= u.num {
			num := amount / u.num
			amount %= u.num
			timeParts = append(timeParts, fmt.Sprintf("%d%s", num, u.name))
		}
	}
	return strings.Join(timeParts, " ")
}