Skip to content

Commit 8d00e18

Browse files
aVolpelucassabreu
andauthored
feat: Add flag to set the timezone on reports (#253)
* feat: Add flag to set the timezone on reports By default all the dates are in UTC, this commit adds a new flag: '--time-zone' or '-z' that allows the user to pass a timezone. This is a breaking change, now, by default the used timezone is 'local', meaning the value returned by 'time.local'. Examples: - Madrid Timezone: -z Europe/Madrid - Current timezone: -z local A full list of timezones can be found here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones This can fix some issues reported in #135. Signed-off-by: Arturo Volpe <[email protected]> * chore: Apply suggestions from code review Co-authored-by: Lucas dos Santos Abreu <[email protected]> * chore: test * feat: timezone as config * feat: link flag and config * fix: redundant * feat: changelog * fix: time-zone => tz --------- Signed-off-by: Arturo Volpe <[email protected]> Co-authored-by: Lucas dos Santos Abreu <[email protected]>
1 parent 3e454e5 commit 8d00e18

File tree

13 files changed

+421
-63
lines changed

13 files changed

+421
-63
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- new config `lang` to allow setting the number format to be used when printing
1717
- support to using client's name or id for autocompletion on bash
18+
- new config `timezone` to allow setting which timezone to use when reporting the time of a time entry
1819

1920
## [v0.52.0] - 2024-06-02
2021

cmd/clockify-cli/main.go

+7
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ func bindViper(rootCmd *cobra.Command) error {
157157
}
158158
}
159159

160+
if flag := cmd.Flags().Lookup("tz"); flag != nil {
161+
if err := bind(flag, cmdutil.CONF_TIMEZONE,
162+
"TIMEZONE"); err != nil {
163+
return err
164+
}
165+
}
166+
160167
return nil
161168
}
162169

internal/mocks/mock_Config.go

+82
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/mocks/simple_config.go

+29-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package mocks
22

33
import (
4+
"time"
5+
46
"github.com/lucassabreu/clockify-cli/pkg/cmdutil"
57
"golang.org/x/text/language"
68
)
@@ -24,18 +26,7 @@ type SimpleConfig struct {
2426
AllowArchivedTags bool
2527
SearchProjectWithClientsName bool
2628
LanguageTag language.Tag
27-
}
28-
29-
// IsSearchProjectWithClientsName defines if the project name for ID should
30-
// include the client's name
31-
func (s *SimpleConfig) IsSearchProjectWithClientsName() bool {
32-
return s.SearchProjectWithClientsName
33-
}
34-
35-
// InteractivePageSize sets how many items are shown when prompting
36-
// projects
37-
func (s *SimpleConfig) InteractivePageSize() int {
38-
return s.InteractivePageSizeNumber
29+
TimeZoneLoc *time.Location
3930
}
4031

4132
func (d *SimpleConfig) GetBool(n string) bool {
@@ -147,6 +138,32 @@ func (d *SimpleConfig) LogLevel() string {
147138
return d.LogLevelValue
148139
}
149140

141+
// TimeZone which time zone to use for showing date & time
142+
func (s *SimpleConfig) TimeZone() *time.Location {
143+
if s.TimeZoneLoc == nil {
144+
s.TimeZoneLoc = time.UTC
145+
}
146+
147+
return s.TimeZoneLoc
148+
}
149+
150+
// SetTimeZone changes the timezone used for dates
151+
func (s *SimpleConfig) SetTimeZone(loc *time.Location) {
152+
s.TimeZoneLoc = loc
153+
}
154+
155+
// IsSearchProjectWithClientsName defines if the project name for ID should
156+
// include the client's name
157+
func (s *SimpleConfig) IsSearchProjectWithClientsName() bool {
158+
return s.SearchProjectWithClientsName
159+
}
160+
161+
// InteractivePageSize sets how many items are shown when prompting
162+
// projects
163+
func (s *SimpleConfig) InteractivePageSize() int {
164+
return s.InteractivePageSizeNumber
165+
}
166+
150167
func (*SimpleConfig) Save() error {
151168
panic("should not call")
152169
}

pkg/cmd/config/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ var validParameters = cmdcompl.ValidArgsMap{
3636
cmdutil.CONF_LOG_LEVEL: "how much logs should be shown values: " +
3737
"none , error , info and debug",
3838
cmdutil.CONF_ALLOW_ARCHIVED_TAGS: "should allow and suggest archived tags",
39+
cmdutil.CONF_LANGUAGE: "which language to use for number " +
40+
"formatting",
41+
cmdutil.CONF_TIMEZONE: "which timezone to use to input/output time",
3942
}
4043

4144
// NewCmdConfig represents the config command

pkg/cmd/config/init/init.go

+55-27
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package init
33
import (
44
"fmt"
55
"strings"
6+
"time"
67

78
"github.com/lucassabreu/clockify-cli/api"
89
"github.com/lucassabreu/clockify-cli/pkg/cmdutil"
@@ -110,7 +111,8 @@ func NewCmdInit(f cmdutil.Factory) *cobra.Command {
110111
"Should suggest and allow creating time entries "+
111112
"with archived tags?",
112113
),
113-
func() error { return setLanguage(i, config) },
114+
setLanguage(i, config),
115+
setTimezone(i, config),
114116
); err != nil {
115117
return err
116118
}
@@ -122,36 +124,62 @@ func NewCmdInit(f cmdutil.Factory) *cobra.Command {
122124
return cmd
123125
}
124126

125-
func setLanguage(i ui.UI, config cmdutil.Config) error {
126-
suggestLanguages := []string{
127-
language.English.String(),
128-
language.German.String(),
129-
language.Afrikaans.String(),
130-
language.Chinese.String(),
131-
language.Portuguese.String(),
127+
func setTimezone(i ui.UI, config cmdutil.Config) func() error {
128+
return func() error {
129+
tzname, err := i.AskForValidText("What is your preferred timezone:",
130+
func(s string) error {
131+
_, err := time.LoadLocation(s)
132+
return err
133+
},
134+
ui.WithHelp("Should be 'Local' to use the systems timezone, UTC "+
135+
"or valid TZ identifier from the IANA TZ database "+
136+
"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"),
137+
ui.WithDefault(config.TimeZone().String()),
138+
)
139+
if err != nil {
140+
return err
141+
}
142+
143+
tz, _ := time.LoadLocation(tzname)
144+
145+
config.SetTimeZone(tz)
146+
147+
return nil
132148
}
149+
}
133150

134-
lang, err := i.AskForValidText("What is your preferred language:",
135-
func(s string) error {
136-
_, err := language.Parse(s)
151+
func setLanguage(i ui.UI, config cmdutil.Config) func() error {
152+
return func() error {
153+
suggestLanguages := []string{
154+
language.English.String(),
155+
language.German.String(),
156+
language.Afrikaans.String(),
157+
language.Chinese.String(),
158+
language.Portuguese.String(),
159+
}
160+
161+
lang, err := i.AskForValidText("What is your preferred language:",
162+
func(s string) error {
163+
_, err := language.Parse(s)
164+
return err
165+
},
166+
ui.WithHelp("Accepts any IETF language tag "+
167+
"https://en.wikipedia.org/wiki/IETF_language_tag"),
168+
ui.WithSuggestion(func(toComplete string) []string {
169+
return strhlp.Filter(
170+
strhlp.IsSimilar(toComplete),
171+
suggestLanguages,
172+
)
173+
}),
174+
ui.WithDefault(config.Language().String()),
175+
)
176+
if err != nil {
137177
return err
138-
},
139-
ui.WithHelp("Accepts any IETF language tag "+
140-
"https://en.wikipedia.org/wiki/IETF_language_tag"),
141-
ui.WithSuggestion(func(toComplete string) []string {
142-
return strhlp.Filter(
143-
strhlp.IsSimilar(toComplete),
144-
suggestLanguages,
145-
)
146-
}),
147-
ui.WithDefault(config.Language().String()),
148-
)
149-
if err != nil {
150-
return err
151-
}
178+
}
152179

153-
config.SetLanguage(language.MustParse(lang))
154-
return nil
180+
config.SetLanguage(language.MustParse(lang))
181+
return nil
182+
}
155183
}
156184

157185
func setWeekdays(config cmdutil.Config, i ui.UI) (err error) {

pkg/cmd/config/init/init_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ func TestInitCmd(t *testing.T) {
111111
config.EXPECT().Language().Return(language.English)
112112
config.EXPECT().SetLanguage(language.German)
113113

114+
config.EXPECT().TimeZone().Return(time.Local)
115+
config.EXPECT().SetTimeZone(mock.Anything).
116+
Run(func(tz *time.Location) {
117+
assert.Equal(t, tz.String(), "America/Bahia")
118+
})
119+
114120
config.EXPECT().Save().Once().Return(nil)
115121

116122
f.EXPECT().UI().Return(ui.NewUI(in, out, out))
@@ -209,6 +215,10 @@ func TestInitCmd(t *testing.T) {
209215
c.Send(string(terminal.KeyTab))
210216
c.SendLine(string(terminal.KeyTab))
211217

218+
c.ExpectString("preferred timezone:")
219+
c.SendLine("America/Bahia")
220+
c.ExpectString("America/Bahia")
221+
212222
c.ExpectEOF()
213223
})
214224
}

pkg/cmd/config/set/set.go

+19
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package set
22

33
import (
4+
"fmt"
45
"strings"
6+
"time"
57

68
"github.com/MakeNowJust/heredoc"
79
"github.com/lucassabreu/clockify-cli/pkg/cmdcompl"
810
"github.com/lucassabreu/clockify-cli/pkg/cmdutil"
911
"github.com/lucassabreu/clockify-cli/strhlp"
1012
"github.com/spf13/cobra"
13+
"golang.org/x/text/language"
1114
)
1215

1316
// NewCmdSet will update the value of one parameter
@@ -44,6 +47,22 @@ func NewCmdSet(
4447
ws,
4548
)
4649
config.SetStringSlice(param, ws)
50+
case cmdutil.CONF_LANGUAGE:
51+
lang, err := language.Parse(value)
52+
if err != nil {
53+
return fmt.Errorf(
54+
"%s is not a valid language: %w", value, err)
55+
}
56+
57+
config.SetLanguage(lang)
58+
case cmdutil.CONF_TIMEZONE:
59+
tz, err := time.LoadLocation(value)
60+
if err != nil {
61+
return fmt.Errorf(
62+
"%s is not a valid timezone: %w", value, err)
63+
}
64+
65+
config.SetTimeZone(tz)
4766
default:
4867
config.SetString(param, value)
4968
}

0 commit comments

Comments
 (0)