Passed
Push — v3.0.0 ( c3fef3...7240dc )
by Serhii
01:16
created

timeago.calculateTimeAgo   A

Complexity

Conditions 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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