Passed
Push — v3.0.0 ( 2ee17b...4b7c3c )
by Serhii
01:15
created

timeago.finalTimeUnit   A

Complexity

Conditions 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
dl 0
loc 12
rs 10
c 0
b 0
f 0
nop 2
1
package timeago
2
3
import (
4
	"math"
5
	"strconv"
6
	"strings"
7
	"time"
8
)
9
10
var (
11
	cachedJsonRes = map[string]*LangSet{}
12
	options       = []Option{}
13
	langSet       *LangSet
14
	conf          = NewConfig("en", "", []Translation{})
15
)
16
17
// Parse coverts given datetime into `x time ago` format.
18
// First argument can have 3 types:
19
// 1. int (Unix timestamp)
20
// 2. time.Time (Type from Go time package)
21
// 3. string (Datetime string in format 'YYYY-MM-DD HH:MM:SS')
22
func Parse(datetime interface{}, opts ...Option) (string, error) {
23
	options = []Option{}
24
	var input time.Time
25
	var err error
26
27
	switch date := datetime.(type) {
28
	case int:
29
		input = parseTimestampIntoTime(date)
30
	case string:
31
		input, err = parseStrIntoTime(date)
32
	default:
33
		input = datetime.(time.Time)
34
	}
35
36
	if err != nil {
37
		return "", err
38
	}
39
40
	enableOptions(opts)
41
42
	return calculateTimeAgo(input)
43
}
44
45
func Configure(c *Config) {
46
	if c.Language != "" {
47
		conf.Language = c.Language
48
	}
49
50
	if c.Location != "" {
51
		conf.Location = c.Location
52
	}
53
54
	if len(c.Translations) > 0 {
55
		conf.Translations = c.Translations
56
	}
57
}
58
59
func parseStrIntoTime(datetime string) (time.Time, error) {
60
	if !conf.LocationIsSet() {
61
		parsedTime, _ := time.Parse("2006-01-02 15:04:05", datetime)
62
		return parsedTime, nil
63
	}
64
65
	loc, err := location()
66
67
	if err != nil {
68
		return time.Time{}, err
69
	}
70
71
	parsedTime, err := time.ParseInLocation("2006-01-02 15:04:05", datetime, loc)
72
73
	if err != nil {
74
		return time.Time{}, createError("%v", err)
75
	}
76
77
	return parsedTime, nil
78
}
79
80
func location() (*time.Location, error) {
81
	if !conf.LocationIsSet() {
82
		return time.Now().Location(), nil
83
	}
84
85
	loc, err := time.LoadLocation(conf.Location)
86
87
	if err != nil {
88
		return nil, createError("%v", err)
89
	}
90
91
	return loc, nil
92
}
93
94
func calculateTimeAgo(t time.Time) (string, error) {
95
	now := time.Now()
96
97
	if conf.LocationIsSet() {
98
		loc, err := location()
99
100
		if err != nil {
101
			return "", err
102
		}
103
104
		t = t.In(loc)
105
		now = now.In(loc)
106
	}
107
108
	seconds := int(now.Sub(t).Seconds())
109
110
	if seconds < 0 {
111
		enableOption(Upcoming)
112
		seconds = -seconds
113
	}
114
115
	set, err := NewLangSet()
116
117
	if err != nil {
118
		return "", err
119
	}
120
121
	langSet = set
122
123
	return generateTimeUnit(seconds)
124
}
125
126
func generateTimeUnit(seconds int) (string, error) {
127
	minutes, hours, days, weeks, months, years := getTimeCalculations(float64(seconds))
128
129
	switch {
130
	case optionIsEnabled("online") && seconds < 60:
131
		return langSet.Online, nil
132
	case optionIsEnabled("justNow") && seconds < 60:
133
		return langSet.JustNow, nil
134
	case seconds < 60:
135
		return getWords(langSet.Second, seconds)
136
	case minutes < 60:
137
		return getWords(langSet.Minute, minutes)
138
	case hours < 24:
139
		return getWords(langSet.Hour, hours)
140
	case days < 7:
141
		return getWords(langSet.Day, days)
142
	case weeks < 4:
143
		return getWords(langSet.Week, weeks)
144
	case months < 12:
145
		if months == 0 {
146
			months = 1
147
		}
148
149
		return getWords(langSet.Month, months)
150
	}
151
152
	return getWords(langSet.Year, years)
153
}
154
155
// getWords decides rather the word must be singular or plural,
156
// and depending on the result it adds the correct word after
157
// the time number
158
func getWords(langForm LangForms, num int) (string, error) {
159
	timeUnit, err := finalTimeUnit(langForm, num)
160
161
	if err != nil {
162
		return "", err
163
	}
164
165
	res := langSet.Format
166
	res = strings.Replace(res, "{timeUnit}", timeUnit, -1)
167
	res = strings.Replace(res, "{num}", strconv.Itoa(num), -1)
168
169
	if optionIsEnabled("noSuffix") || optionIsEnabled("upcoming") {
170
		res = strings.Replace(res, "{ago}", "", -1)
171
		return strings.Trim(res, " "), nil
172
	}
173
174
	return strings.Replace(res, "{ago}", langSet.Ago, -1), nil
175
}
176
177
func finalTimeUnit(langForm LangForms, num int) (string, error) {
178
	form, err := identifyTimeUnitForm(num)
179
180
	if err != nil {
181
		return "", err
182
	}
183
184
	if unit, ok := langForm[form]; ok {
185
		return unit, nil
186
	}
187
188
	return langForm["other"], nil
189
}
190
191
func identifyTimeUnitForm(num int) (string, error) {
192
	rule, err := identifyGrammarRules(num, conf.Language)
193
194
	if err != nil {
195
		return "", err
196
	}
197
198
	switch {
199
	case rule.Zero:
200
		return "zero", nil
201
	case rule.One:
202
		return "one", nil
203
	case rule.Few:
204
		return "few", nil
205
	case rule.Two:
206
		return "two", nil
207
	case rule.Many:
208
		return "many", nil
209
	}
210
211
	return "other", nil
212
}
213
214
func getTimeCalculations(seconds float64) (int, int, int, int, int, int) {
215
	minutes := math.Round(seconds / 60)
216
	hours := math.Round(seconds / 3600)
217
	days := math.Round(seconds / 86400)
218
	weeks := math.Round(seconds / 604800)
219
	months := math.Round(seconds / 2629440)
220
	years := math.Round(seconds / 31553280)
221
222
	return int(minutes), int(hours), int(days), int(weeks), int(months), int(years)
223
}
224