Passed
Push — v3.0.0 ( c0d1f3...99da91 )
by Serhii
01:17
created

timeago.defaultConfig   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
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 = defaultConfig()
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 without
58
// overriding the previous configuration. It will only override the
59
// provided configuration. If you want to override the previous
60
// configurations, use Reconfigure function instead.
61
func Configure(c Config) {
62
	if c.Language != "" {
63
		conf.Language = c.Language
64
	}
65
66
	if c.Location != "" {
67
		conf.Location = c.Location
68
	}
69
70
	if len(c.Translations) > 0 {
71
		conf.Translations = c.Translations
72
	}
73
}
74
75
// Reconfigure reconfigures the timeago with the provided configuration.
76
// It will override the previous configuration with the new one.
77
func Reconfigure(c Config) {
78
	conf = defaultConfig()
79
	cachedJsonRes = map[string]*LangSet{}
80
81
	Configure(c)
82
}
83
84
func defaultConfig() *Config {
85
	return NewConfig("en", "", []LangSet{})
86
}
87
88
func parseStrIntoTime(datetime string) (time.Time, error) {
89
	if !conf.isLocationProvided() {
90
		parsedTime, _ := time.Parse("2006-01-02 15:04:05", datetime)
91
		return parsedTime, nil
92
	}
93
94
	loc, err := location()
95
96
	if err != nil {
97
		return time.Time{}, err
98
	}
99
100
	parsedTime, err := time.ParseInLocation("2006-01-02 15:04:05", datetime, loc)
101
102
	if err != nil {
103
		return time.Time{}, createError("%v", err)
104
	}
105
106
	return parsedTime, nil
107
}
108
109
func location() (*time.Location, error) {
110
	if !conf.isLocationProvided() {
111
		return time.Now().Location(), nil
112
	}
113
114
	loc, err := time.LoadLocation(conf.Location)
115
116
	if err != nil {
117
		return nil, createError("%v", err)
118
	}
119
120
	return loc, nil
121
}
122
123
func calculateTimeAgo(t time.Time) (string, error) {
124
	now := time.Now()
125
126
	if conf.isLocationProvided() {
127
		loc, err := location()
128
129
		if err != nil {
130
			return "", err
131
		}
132
133
		t = t.In(loc)
134
		now = now.In(loc)
135
	}
136
137
	seconds := int(now.Sub(t).Seconds())
138
139
	if seconds < 0 {
140
		enableOption(Upcoming)
141
		seconds = -seconds
142
	}
143
144
	set, err := newLangSet()
145
146
	if err != nil {
147
		return "", err
148
	}
149
150
	langSet = set
151
152
	return generateTimeUnit(seconds)
153
}
154
155
func generateTimeUnit(seconds int) (string, error) {
156
	minutes, hours, days, weeks, months, years := getTimeCalculations(float64(seconds))
157
158
	switch {
159
	case optionIsEnabled("online") && seconds < 60:
160
		return langSet.Online, nil
161
	case optionIsEnabled("justNow") && seconds < 60:
162
		return langSet.JustNow, nil
163
	case seconds < 60:
164
		return getWords(langSet.Second, seconds)
165
	case minutes < 60:
166
		return getWords(langSet.Minute, minutes)
167
	case hours < 24:
168
		return getWords(langSet.Hour, hours)
169
	case days < 7:
170
		return getWords(langSet.Day, days)
171
	case weeks < 4:
172
		return getWords(langSet.Week, weeks)
173
	case months < 12:
174
		if months == 0 {
175
			months = 1
176
		}
177
178
		return getWords(langSet.Month, months)
179
	}
180
181
	return getWords(langSet.Year, years)
182
}
183
184
// getWords decides rather the word must be singular or plural,
185
// and depending on the result it adds the correct word after
186
// the time number.
187
func getWords(langForm LangForms, num int) (string, error) {
188
	timeUnit, err := finalTimeUnit(langForm, num)
189
190
	if err != nil {
191
		return "", err
192
	}
193
194
	res := langSet.Format
195
	res = strings.Replace(res, "{timeUnit}", timeUnit, -1)
196
	res = strings.Replace(res, "{num}", strconv.Itoa(num), -1)
197
198
	if optionIsEnabled("noSuffix") || optionIsEnabled("upcoming") {
199
		res = strings.Replace(res, "{ago}", "", -1)
200
		return strings.Trim(res, " "), nil
201
	}
202
203
	return strings.Replace(res, "{ago}", langSet.Ago, -1), nil
204
}
205
206
func finalTimeUnit(langForm LangForms, num int) (string, error) {
207
	form, err := identifyTimeUnitForm(num)
208
209
	if err != nil {
210
		return "", err
211
	}
212
213
	if unit, ok := langForm[form]; ok {
214
		return unit, nil
215
	}
216
217
	return langForm["other"], nil
218
}
219
220
func identifyTimeUnitForm(num int) (string, error) {
221
	rule, err := identifyGrammarRules(num, conf.Language)
222
223
	if err != nil {
224
		return "", err
225
	}
226
227
	switch {
228
	case rule.Zero:
229
		return "zero", nil
230
	case rule.One:
231
		return "one", nil
232
	case rule.Few:
233
		return "few", nil
234
	case rule.Two:
235
		return "two", nil
236
	case rule.Many:
237
		return "many", nil
238
	}
239
240
	return "other", nil
241
}
242
243
func getTimeCalculations(seconds float64) (int, int, int, int, int, int) {
244
	minutes := math.Round(seconds / 60)
245
	hours := math.Round(seconds / 3600)
246
	days := math.Round(seconds / 86400)
247
	weeks := math.Round(seconds / 604800)
248
	months := math.Round(seconds / 2629440)
249
	years := math.Round(seconds / 31553280)
250
251
	return int(minutes), int(hours), int(days), int(weeks), int(months), int(years)
252
}
253