8000 Clock repair and military-time testing by jnd-au · Pull Request #61 · oz/tz · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Clock repair and military-time testing #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,37 @@ import "time"
// Clock keeps track of the current time.
type Clock struct {
t time.Time
isRealTime bool
}

// Make a new Clock.
//
// When "sec" is not t0, the returned Clock is set to this time,
// assuming "sec" is a UNIX timestamp (cf. time.Unix).
func NewClock(sec int64) *Clock {
// A new Clock in the local timezone at the current time now.
func NewClockNow() *Clock {
clock := new(Clock)
clock.t = time.Now()
clock.isRealTime = true
return clock
}

if sec != 0 {
clock.t = time.Unix(sec, 0)
} else {
clock.t = time.Now()
}
// A new Clock with the given time and timezone.
func NewClockTime(t time.Time) *Clock {
clock := new(Clock)
clock.t = t
clock.isRealTime = false
return clock
}

// A new Clock in the local timezone at the `time.Unix` timestamp.
func NewClockUnixTimestamp(sec int64) *Clock {
clock := new(Clock)
clock.t = time.Unix(sec, 0)
clock.isRealTime = false
return clock
}

// AddDays adds n days to the current date.
func (c *Clock) AddDays(n int) {
c.t = c.t.AddDate(0, 0, n)
c.isRealTime = false
}

// AddDays adds n days to the current date and clears the minutes
Expand All @@ -40,6 +51,7 @@ func (c *Clock) AddHours(n int) {
c.t.Location(),
)
c.t = c.t.Add(time.Hour * time.Duration(n))
c.isRealTime = false
}

// Get the wrapped time.Time struct
Expand Down
61 changes: 61 additions & 0 deletions clock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* This file is part of tz.
*
* tz is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* tz is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with tz. If not, see <https://www.gnu.org/licenses/>.
**/
package main

import (
"testing"
"time"
)

func TestNewClockNow(t *testing.T) {
start := time.Now()
clock := NewClockNow()
if clock.t.Compare(start) < 0 {
t.Errorf("Unexpected old time from NewClockNow(): %v", clock.t.Format(time.RFC3339))
}
if !clock.isRealTime {
t.Error("NewClockNow() wasn’t real time")
}
}

func TestClockTime(t *testing.T) {
epoch, err := time.Parse(time.RFC3339, "1970-01-01T01:03:05Z")
if err != nil {
t.Fatalf("Could not parse test epoch")
}
clock := NewClockTime(epoch)
if clock.t.Compare(epoch) != 0 {
t.Errorf("Unexpected time from NewClockTime(epoch): %v", clock.t.Format(time.RFC3339))
}
if clock.isRealTime {
t.Error("NewClockTime() shouldn’t be real time")
}
}

func TestClockUnixTimestamp(t *testing.T) {
epoch, err := time.Parse(time.RFC3339, "1970-01-01T00:00:01Z")
if err != nil {
t.Fatalf("Could not parse test epoch")
}
clock := NewClockUnixTimestamp(1)
if clock.t.Compare(epoch) != 0 {
t.Errorf("Unexpected time from NewClockUnixTimestamp(1): %v", clock.t.Format(time.RFC3339))
}
if clock.isRealTime {
t.Error("NewClockUnixTimestamp() shouldn’t be real time")
}
}
12 changes: 6 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
openInTimeAndDateDotCom(m.clock.Time())

case match(key, m.keymaps.Now):
m.clock = *NewClock(0)
m.clock = *NewClockNow()

case match(key, m.keymaps.ToggleDate):
m.showDates = !m.showDates
Expand All @@ -139,8 +139,8 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

case tickMsg:
if m.watch {
m.clock = *NewClock(0)
if m.watch && m.clock.isRealTime {
m.clock = *NewClockNow()
}
return m, tick()
}
Expand All @@ -153,7 +153,7 @@ func main() {

exitQuick := flag.Bool("q", false, "exit immediately")
showVersion := flag.Bool("v", false, "show version")
when := flag.Int64("when", 0, "time in seconds since unix epoch")
when := flag.Int64("when", 0, "time in seconds since unix epoch (disables -w)")
doSearch := flag.Bool("list", false, "list zones by name")
military := flag.Bool("m", false, "use 24-hour time")
watch := flag.Bool("w", false, "watch live, set time to now every minute")
Expand Down Expand Up @@ -183,15 +183,15 @@ func main() {
var initialModel = model{
zones: config.Zones,
keymaps: config.Keymaps,
clock: *NewClock(0),
clock: *NewClockNow(),
showDates: false,
isMilitary: *military,
watch: *watch,
showHelp: false,
}

if *when != 0 {
initialModel.clock = *NewClock(*when)
initialModel.clock = *NewClockUnixTimestamp(*when)
}

initialModel.interactive = !*exitQuick && isatty.IsTerminal(os.Stdout.Fd())
Expand Down
62 changes: 41 additions & 21 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
package main

import (
"os"
"regexp"
"strings"
"testing"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/muesli/termenv"
"golang.org/x/tools/txtar"
)

var (
Expand All @@ -40,16 +42,13 @@ var (
)
utcMinuteAfterMidnightModel = model{
zones: DefaultZones[len(DefaultZones) - 1:],
clock: *NewClock(utcMinuteAfterMidnightTime.Unix()),
clock: *NewClockTime(utcMinuteAfterMidnightTime),
isMilitary: true,
showDates: true,
}
)

func getTimestampWithHour(hour int) int64 {
if hour == -1 {
hour = time.Now().Hour()
}
func getTimestampWithHour(hour int) time.Time {
return time.Date(
time.Now().Year(),
time.Now().Month(),
Expand All @@ -59,7 +58,7 @@ func getTimestampWithHour(hour int) int64 {
0, // Seconds set to 0
0, // Nanoseconds set to 0
time.Now().Location(),
).Unix()
)
}

func stripAnsiControlSequences(s string) string {
Expand Down Expand Up @@ -94,18 +93,18 @@ func TestUpdateIncHour(t *testing.T) {
m := model{
zones: DefaultZones,
keymaps: NewDefaultConfig().Keymaps,
clock: *NewClock(getTimestampWithHour(test.startHour)),
clock: *NewClockTime(getTimestampWithHour(test.startHour)),
}

db := m.clock.Time().Day()
nextState, cmd := m.Update(msg)
_, cmd := m.Update(msg)
da := m.clock.Time().Day()

if cmd != nil {
t.Errorf("Expected nil Cmd, but got %v", cmd)
return
}
h := nextState.(*model).clock.t.Hour()
h := m.clock.t.Hour()
if h != test.nextHour {
t.Errorf("Expected %d, but got %d", test.nextHour, h)
}
Expand Down Expand Up @@ -137,14 +136,14 @@ func TestUpdateDecHour(t *testing.T) {
m := model{
zones: DefaultZones,
keymaps: NewDefaultConfig().Keymaps,
clock: *NewClock(getTimestampWithHour(test.startHour)),
clock: *NewClockTime(getTimestampWithHour(test.startHour)),
}
nextState, cmd := m.Update(msg)
_, cmd := m.Update(msg)
if cmd != nil {
t.Errorf("Expected nil Cmd, but got %v", cmd)
return
}
h := nextState.(*model).clock.t.Hour()
h := m.clock.t.Hour()
if h != test.nextHour {
t.Errorf("Expected %d, but got %d", test.nextHour, h)
}
Expand All @@ -162,7 +161,7 @@ func TestUpdateQuitMsg(t *testing.T) {
m := model{
zones: DefaultZones,
keymaps: NewDefaultConfig().Keymaps,
clock: *NewClock(getTimestampWithHour(-1)),
clock: *NewClockTime(getTimestampWithHour(10)),
}
_, cmd := m.Update(msg)
if cmd == nil {
Expand All @@ -174,14 +173,35 @@ func TestUpdateQuitMsg(t *testing.T) {
}

func TestMilitaryTime(t *testing.T) {
m := model{
zones: DefaultZones,
clock: *NewClock(getTimestampWithHour(-1)),
isMilitary: true,
showDates: true,
testDataFile := "testdata/main/test-military-time.txt"
testData, err := txtar.ParseFile(testDataFile)
if err != nil {
t.Fatal(err)
}

formatted := utcMinuteAfterMidnightTime.Format(" 15:04, Mon Jan 02, 2006")
expected := stripAnsiControlSequencesAndNewline(testData.Files[0].Data)
observed := stripAnsiControlSequences(utcMinuteAfterMidnightModel.View())

archive := txtar.Archive{
Comment: testData.Comment,
Files: []txtar.File{
{
Name: "expected",
Data: []byte(expected),
},
{
Name: "observed",
Data: []byte(observed),
},
},
}
os.WriteFile(testDataFile, txtar.Format(&archive), 0666)

if formatted != expected {
t.Errorf("Expected military time of %s, but got %s", expected, formatted)
}
s := m.View()
if !strings.Contains(s, m.clock.t.Format("15:04")) {
t.Errorf("Expected military time of %s, but got %s", m.clock.t.Format("15:04"), s)
if !strings.Contains(observed, expected) {
t.Errorf("Expected military time of %s, but got %s", expected, observed)
}
}
12 changes: 12 additions & 0 deletions testdata/main/test-military-time.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Military time is (a) 24-hour time (b) padded with leading zeros (c) without an AM/PM suffix.
For example, one minute past midnight is “0001” (neither unpadded 24-hour “0:01” nor 12-hour suffixed “12:01AM”).
However for clarity, we include a colon delimiter between hours and minutes anyway:
-- expected --
00:01, Sun Nov 05, 2017
-- observed --

What time is it?

🕛 UTC 00:01, Sun Nov 05, 2017
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
📆 Sun 05
0