Passed
Push — v3.0.0 ( d631d6...c0d1f3 )
by Serhii
01:16
created

timeago.ClearCache   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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