1package ztime
2
3import (
4	"fmt"
5	"strings"
6	"testing"
7	"time"
8
9	"zgo.at/zstd/ztest"
10)
11
12func TestNew(t *testing.T) {
13	tz, err := time.LoadLocation("Asia/Makassar")
14	_ = tz
15	if err != nil {
16		t.Fatal(err)
17	}
18	tests := []struct {
19		in   string
20		want time.Time
21	}{
22		{"2020-06-18 14:15:16.999999999", time.Date(2020, 6, 18, 14, 15, 16, 999999999, time.UTC)},
23		{"2020-06-18 14:15:16.0", time.Date(2020, 6, 18, 14, 15, 16, 0, time.UTC)},
24		{"2020-06-18 14:15:16", time.Date(2020, 6, 18, 14, 15, 16, 0, time.UTC)},
25		{"2020-06-18 14:15", time.Date(2020, 6, 18, 14, 15, 0, 0, time.UTC)},
26		{"2020-06-18 14", time.Date(2020, 6, 18, 14, 0, 0, 0, time.UTC)},
27		{"2020-06-18", time.Date(2020, 6, 18, 0, 0, 0, 0, time.UTC)},
28		{"2020-06", time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC)},
29		{"2020", time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)},
30
31		// {"2020-06-18 14:15:16.999999999 WITA", time.Date(2020, 6, 18, 14, 15, 16, 999999999, tz)},
32		// {"2020-06-18 14:15:16.0 WITA", time.Date(2020, 6, 18, 14, 15, 16, 0, tz)},
33		// {"2020-06-18 14:15:16 WITA", time.Date(2020, 6, 18, 14, 15, 16, 0, tz)},
34		// {"2020-06-18 14:15 WITA", time.Date(2020, 6, 18, 14, 15, 0, 0, tz)},
35		// {"2020-06-18 14 WITA", time.Date(2020, 6, 18, 14, 0, 0, 0, tz)},
36		// {"2020-06-18 WITA", time.Date(2020, 6, 18, 0, 0, 0, 0, tz)},
37		// {"2020-06 WITA", time.Date(2020, 6, 1, 0, 0, 0, 0, tz)},
38		// {"2020 WITA", time.Date(2020, 1, 1, 0, 0, 0, 0, tz)},
39	}
40
41	for _, tt := range tests {
42		t.Run("", func(t *testing.T) {
43			have := New(tt.in)
44			if !have.Equal(tt.want) {
45				t.Errorf("\nhave: %s\nwant: %s", have, tt.want)
46			}
47		})
48	}
49}
50
51func TestStartOf(t *testing.T) {
52	var (
53		periods = []Period{Second, Minute, QuarterHour, HalfHour, Hour, Day, WeekMonday, WeekSunday, Month, Quarter, HalfYear, Year}
54		f       = "2006-01-02 15:04:05.999999999"
55		tt      = Time{time.Date(2020, 6, 18, 14, 49, 20, 666, time.UTC)}
56		h       = new(strings.Builder)
57	)
58	h.WriteString("       StartOf: " + tt.Format(f) + "\n")
59	for _, p := range periods {
60		pad := strings.Repeat(" ", 14-len(p.String()))
61		fmt.Fprintln(h, p.String(), pad,
62			tt.StartOf(p).Format(f),
63			"", tt.StartOf(p).AddTime(-1).StartOf(p).Format(f),
64			"", tt.StartOf(p).AddTime(-1).StartOf(p).AddTime(-1).StartOf(p).Format(f),
65			"", tt.StartOf(p).AddTime(-1).StartOf(p).AddTime(-1).StartOf(p).AddTime(-1).StartOf(p).Format(f))
66	}
67
68	have := h.String()
69	want := `
70		       StartOf: 2020-06-18 14:49:20.000000666
71		second          2020-06-18 14:49:20  2020-06-18 14:49:19  2020-06-18 14:49:18  2020-06-18 14:49:17
72		minute          2020-06-18 14:49:00  2020-06-18 14:48:00  2020-06-18 14:47:00  2020-06-18 14:46:00
73		quarter hour    2020-06-18 14:45:00  2020-06-18 14:30:00  2020-06-18 14:15:00  2020-06-18 14:00:00
74		half hour       2020-06-18 14:30:00  2020-06-18 14:00:00  2020-06-18 13:30:00  2020-06-18 13:00:00
75		hour            2020-06-18 14:00:00  2020-06-18 13:00:00  2020-06-18 12:00:00  2020-06-18 11:00:00
76		day             2020-06-18 00:00:00  2020-06-17 00:00:00  2020-06-16 00:00:00  2020-06-15 00:00:00
77		week (Monday)   2020-06-15 00:00:00  2020-06-08 00:00:00  2020-06-01 00:00:00  2020-05-25 00:00:00
78		week (Sunday)   2020-06-14 00:00:00  2020-06-07 00:00:00  2020-05-31 00:00:00  2020-05-24 00:00:00
79		month           2020-06-01 00:00:00  2020-05-01 00:00:00  2020-04-01 00:00:00  2020-03-01 00:00:00
80		quarter         2020-04-01 00:00:00  2020-01-01 00:00:00  2019-10-01 00:00:00  2019-07-01 00:00:00
81		half year       2020-01-01 00:00:00  2019-07-01 00:00:00  2019-01-01 00:00:00  2018-07-01 00:00:00
82		year            2020-01-01 00:00:00  2019-01-01 00:00:00  2018-01-01 00:00:00  2017-01-01 00:00:00`
83	if d := ztest.Diff(have, want, ztest.DiffNormalizeWhitespace); d != "" {
84		t.Error(d)
85	}
86}
87
88func TestEndOf(t *testing.T) {
89	var (
90		periods = []Period{Second, Minute, QuarterHour, HalfHour, Hour, Day, WeekMonday, WeekSunday, Month, Quarter, HalfYear, Year}
91		f       = "2006-01-02 15:04:05.999999999"
92		tt      = Time{time.Date(2020, 6, 18, 14, 49, 20, 666, time.UTC)}
93		h       = new(strings.Builder)
94	)
95	h.WriteString("\n         EndOf: " + tt.Format(f) + "\n")
96	for _, p := range periods {
97		pad := strings.Repeat(" ", 14-len(p.String()))
98		fmt.Fprintln(h, p.String(), pad,
99			tt.EndOf(p).Format(f),
100			"", tt.EndOf(p).AddTime(1).EndOf(p).Format(f),
101			"", tt.EndOf(p).AddTime(1).EndOf(p).AddTime(1).EndOf(p).Format(f),
102			"", tt.EndOf(p).AddTime(1).EndOf(p).AddTime(1).EndOf(p).AddTime(1).EndOf(p).Format(f))
103	}
104
105	have := h.String()
106	want := `
107		         EndOf: 2020-06-18 14:49:20.000000666
108		second          2020-06-18 14:49:20.999999999  2020-06-18 14:49:21.999999999  2020-06-18 14:49:22.999999999  2020-06-18 14:49:23.999999999
109		minute          2020-06-18 14:49:59.999999999  2020-06-18 14:50:59.999999999  2020-06-18 14:51:59.999999999  2020-06-18 14:52:59.999999999
110		quarter hour    2020-06-18 14:59:59.999999999  2020-06-18 15:14:59.999999999  2020-06-18 15:29:59.999999999  2020-06-18 15:44:59.999999999
111		half hour       2020-06-18 14:59:59.999999999  2020-06-18 15:29:59.999999999  2020-06-18 15:59:59.999999999  2020-06-18 16:29:59.999999999
112		hour            2020-06-18 14:59:59.999999999  2020-06-18 15:59:59.999999999  2020-06-18 16:59:59.999999999  2020-06-18 17:59:59.999999999
113		day             2020-06-18 23:59:59.999999999  2020-06-19 23:59:59.999999999  2020-06-20 23:59:59.999999999  2020-06-21 23:59:59.999999999
114		week (Monday)   2020-06-21 23:59:59.999999999  2020-06-28 23:59:59.999999999  2020-07-05 23:59:59.999999999  2020-07-12 23:59:59.999999999
115		week (Sunday)   2020-06-20 23:59:59.999999999  2020-06-27 23:59:59.999999999  2020-07-04 23:59:59.999999999  2020-07-11 23:59:59.999999999
116		month           2020-06-30 23:59:59.999999999  2020-07-31 23:59:59.999999999  2020-08-31 23:59:59.999999999  2020-09-30 23:59:59.999999999
117		quarter         2020-06-30 23:59:59.999999999  2020-09-30 23:59:59.999999999  2020-12-31 23:59:59.999999999  2021-03-31 23:59:59.999999999
118		half year       2020-06-30 23:59:59.999999999  2020-12-31 23:59:59.999999999  2021-06-30 23:59:59.999999999  2021-12-31 23:59:59.999999999
119		year            2020-12-31 23:59:59.999999999  2021-12-31 23:59:59.999999999  2022-12-31 23:59:59.999999999  2023-12-31 23:59:59.999999999`
120	if d := ztest.Diff(have, want, ztest.DiffNormalizeWhitespace); d != "" {
121		t.Error(d)
122	}
123}
124
125func TestWeek(t *testing.T) {
126	var (
127		mon = Time{time.Date(2021, 4, 5, 14, 49, 20, 666, time.UTC)}
128		sun = Time{time.Date(2021, 4, 4, 14, 49, 20, 666, time.UTC)}
129		f   = "Mon Jan _2"
130		h   = new(strings.Builder)
131	)
132
133	h.WriteString("Monday:\n")
134	for i := 0; i < 7; i++ {
135		fmt.Fprintf(h, "%d  %s → %s %s\n", i, mon.Add(i, Day).Format(f),
136			mon.Add(i, Day).StartOf(Week(false)).Format(f),
137			mon.Add(i, Day).EndOf(Week(false)).Format(f))
138	}
139	h.WriteString("\nSunday:\n")
140	for i := 0; i < 7; i++ {
141		fmt.Fprintf(h, "%d  %s → %s %s\n", i, sun.Add(i, Day).Format(f),
142			sun.Add(i, Day).StartOf(Week(true)).Format(f),
143			sun.Add(i, Day).EndOf(Week(true)).Format(f))
144	}
145
146	have := h.String()
147	want := `
148		Monday:
149		0  Mon Apr  5Mon Apr  5 Sun Apr 11
150		1  Tue Apr  6Mon Apr  5 Sun Apr 11
151		2  Wed Apr  7Mon Apr  5 Sun Apr 11
152		3  Thu Apr  8Mon Apr  5 Sun Apr 11
153		4  Fri Apr  9Mon Apr  5 Sun Apr 11
154		5  Sat Apr 10Mon Apr  5 Sun Apr 11
155		6  Sun Apr 11Mon Apr  5 Sun Apr 11
156
157		Sunday:
158		0  Sun Apr  4Sun Apr  4 Sat Apr 10
159		1  Mon Apr  5Sun Apr  4 Sat Apr 10
160		2  Tue Apr  6Sun Apr  4 Sat Apr 10
161		3  Wed Apr  7Sun Apr  4 Sat Apr 10
162		4  Thu Apr  8Sun Apr  4 Sat Apr 10
163		5  Fri Apr  9Sun Apr  4 Sat Apr 10
164		6  Sat Apr 10Sun Apr  4 Sat Apr 10`
165	if d := ztest.Diff(have, want, ztest.DiffNormalizeWhitespace); d != "" {
166		t.Error(d)
167	}
168}
169
170func TestAdd(t *testing.T) {
171	tests := []struct {
172		in   string
173		n    int
174		p    Period
175		want string
176	}{
177		{"2021-06-18 12:12:12", 1, Second, "2021-06-18 12:12:13"},
178		{"2021-06-18 12:12:12", -1, Second, "2021-06-18 12:12:11"},
179		{"2021-06-18 12:12:12", 61, Second, "2021-06-18 12:13:13"},
180
181		{"2021-06-18 12:12:12", 1, Minute, "2021-06-18 12:13:12"},
182		{"2021-06-18 12:12:12", -1, Minute, "2021-06-18 12:11:12"},
183
184		{"2021-06-18 12:12:12", 1, QuarterHour, "2021-06-18 12:27:12"},
185		{"2021-06-18 12:12:12", -1, QuarterHour, "2021-06-18 11:57:12"},
186
187		{"2021-06-18 12:12:12", 1, HalfHour, "2021-06-18 12:42:12"},
188		{"2021-06-18 12:12:12", -1, HalfHour, "2021-06-18 11:42:12"},
189
190		{"2021-06-18 12:12:12", 1, Hour, "2021-06-18 13:12:12"},
191		{"2021-06-18 12:12:12", -1, Hour, "2021-06-18 11:12:12"},
192
193		{"2021-06-18", 1, Day, "2021-06-19"},
194		{"2021-06-18", -1, Day, "2021-06-17"},
195		{"2021-01-31", 1, Day, "2021-02-01"},
196
197		{"2021-06-18", 1, WeekMonday, "2021-06-25"},
198		{"2021-06-18", 1, WeekSunday, "2021-06-25"},
199		{"2021-06-18", -1, WeekMonday, "2021-06-11"},
200		{"2021-06-18", -1, WeekSunday, "2021-06-11"},
201
202		{"2021-06-18", 1, Month, "2021-07-18"},
203		{"2021-06-18", -1, Month, "2021-05-18"},
204		{"2021-03-31", -1, Month, "2021-02-28"}, // Correct if number of days is less.
205		{"2021-03-31", 1, Month, "2021-04-30"},
206		{"2021-01-31", 10, Month, "2021-11-30"},
207		{"2020-03-31", -1, Month, "2020-02-29"}, // Leap year fun.
208		{"2020-02-29", 1, Month, "2020-03-29"},
209		{"2020-02-29", -1, Month, "2020-01-29"},
210
211		{"2021-06-18", 1, Quarter, "2021-09-18"},
212		{"2021-06-18", -1, Quarter, "2021-03-18"},
213		{"2021-01-31", 1, Quarter, "2021-04-30"}, // Fewer days.
214		{"2021-12-31", -1, Quarter, "2021-09-30"},
215		{"2020-05-31", -1, Quarter, "2020-02-29"}, // Leap year
216
217		{"2021-06-18", 1, HalfYear, "2021-12-18"},
218		{"2021-06-18", -1, HalfYear, "2020-12-18"},
219
220		{"2021-06-18", 1, Year, "2022-06-18"},
221		{"2021-06-18", -1, Year, "2020-06-18"},
222		{"2020-02-29", 1, Year, "2021-02-28"}, // Leap year
223		{"2020-02-29", -1, Year, "2019-02-28"},
224	}
225
226	for _, tt := range tests {
227		t.Run(fmt.Sprintf("%+d %s", tt.n, tt.p), func(t *testing.T) {
228			have := Add(New(tt.in), tt.n, tt.p)
229			want := New(tt.want)
230			if !have.Equal(want) {
231				t.Errorf("\nhave: %s\nwant: %s", have, want)
232			}
233		})
234	}
235}
236