time
This commit is contained in:
parent
6972f08123
commit
7de4408926
|
@ -66,7 +66,6 @@ func (d *Date) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
func NewDate(year, month, day int) Date {
|
||||
|
||||
if year == 0 || month == 0 || month > 12 || day == 0 {
|
||||
return Date{}
|
||||
}
|
||||
|
|
173
item/event.go
173
item/event.go
|
@ -1,111 +1,110 @@
|
|||
package item
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EventBody struct {
|
||||
Title string `json:"title"`
|
||||
Start time.Time `json:"start"`
|
||||
Date Date `json:"date"`
|
||||
Time Time `json:"time"`
|
||||
Duration time.Duration `json:"duration"`
|
||||
}
|
||||
|
||||
func (e EventBody) MarshalJSON() ([]byte, error) {
|
||||
type Alias EventBody
|
||||
return json.Marshal(&struct {
|
||||
Start string `json:"start"`
|
||||
Duration string `json:"duration"`
|
||||
*Alias
|
||||
}{
|
||||
Start: e.Start.UTC().Format(time.RFC3339),
|
||||
Duration: e.Duration.String(),
|
||||
Alias: (*Alias)(&e),
|
||||
})
|
||||
}
|
||||
// func (e EventBody) MarshalJSON() ([]byte, error) {
|
||||
// type Alias EventBody
|
||||
// return json.Marshal(&struct {
|
||||
// Start string `json:"start"`
|
||||
// Duration string `json:"duration"`
|
||||
// *Alias
|
||||
// }{
|
||||
// Start: e.Start.UTC().Format(time.RFC3339),
|
||||
// Duration: e.Duration.String(),
|
||||
// Alias: (*Alias)(&e),
|
||||
// })
|
||||
// }
|
||||
|
||||
func (e *EventBody) UnmarshalJSON(data []byte) error {
|
||||
type Alias EventBody
|
||||
aux := &struct {
|
||||
Start string `json:"start"`
|
||||
Duration string `json:"duration"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(e),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
// func (e *EventBody) UnmarshalJSON(data []byte) error {
|
||||
// type Alias EventBody
|
||||
// aux := &struct {
|
||||
// Start string `json:"start"`
|
||||
// Duration string `json:"duration"`
|
||||
// *Alias
|
||||
// }{
|
||||
// Alias: (*Alias)(e),
|
||||
// }
|
||||
// if err := json.Unmarshal(data, &aux); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
var err error
|
||||
if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
||||
return err
|
||||
}
|
||||
// var err error
|
||||
// if e.Start, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
|
||||
return err
|
||||
}
|
||||
// if e.Duration, err = time.ParseDuration(aux.Duration); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
type Event struct {
|
||||
ID string `json:"id"`
|
||||
Recurrer Recurrer `json:"recurrer"`
|
||||
RecurNext time.Time `json:"recurNext"`
|
||||
EventBody
|
||||
}
|
||||
// type Event struct {
|
||||
// ID string `json:"id"`
|
||||
// Recurrer Recurrer `json:"recurrer"`
|
||||
// RecurNext time.Time `json:"recurNext"`
|
||||
// EventBody
|
||||
// }
|
||||
|
||||
func NewEvent(i Item) (Event, error) {
|
||||
if i.Kind != KindEvent {
|
||||
return Event{}, fmt.Errorf("item is not an event")
|
||||
}
|
||||
// func NewEvent(i Item) (Event, error) {
|
||||
// if i.Kind != KindEvent {
|
||||
// return Event{}, fmt.Errorf("item is not an event")
|
||||
// }
|
||||
|
||||
var e Event
|
||||
if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
|
||||
return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
|
||||
}
|
||||
// var e Event
|
||||
// if err := json.Unmarshal([]byte(i.Body), &e); err != nil {
|
||||
// return Event{}, fmt.Errorf("could not unmarshal item body: %v", err)
|
||||
// }
|
||||
|
||||
e.ID = i.ID
|
||||
e.Recurrer = i.Recurrer
|
||||
e.RecurNext = i.RecurNext
|
||||
// e.ID = i.ID
|
||||
// e.Recurrer = i.Recurrer
|
||||
// e.RecurNext = i.RecurNext
|
||||
|
||||
return e, nil
|
||||
}
|
||||
// return e, nil
|
||||
// }
|
||||
|
||||
func (e Event) Item() (Item, error) {
|
||||
body, err := json.Marshal(EventBody{
|
||||
Title: e.Title,
|
||||
Start: e.Start,
|
||||
Duration: e.Duration,
|
||||
})
|
||||
if err != nil {
|
||||
return Item{}, fmt.Errorf("could not marshal event to json")
|
||||
}
|
||||
// func (e Event) Item() (Item, error) {
|
||||
// body, err := json.Marshal(EventBody{
|
||||
// Title: e.Title,
|
||||
// Start: e.Start,
|
||||
// Duration: e.Duration,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return Item{}, fmt.Errorf("could not marshal event to json")
|
||||
// }
|
||||
|
||||
return Item{
|
||||
ID: e.ID,
|
||||
Kind: KindEvent,
|
||||
Recurrer: e.Recurrer,
|
||||
RecurNext: e.RecurNext,
|
||||
Body: string(body),
|
||||
}, nil
|
||||
}
|
||||
// return Item{
|
||||
// ID: e.ID,
|
||||
// Kind: KindEvent,
|
||||
// Recurrer: e.Recurrer,
|
||||
// RecurNext: e.RecurNext,
|
||||
// Body: string(body),
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
func (e Event) Valid() bool {
|
||||
if e.Title == "" {
|
||||
return false
|
||||
}
|
||||
if e.Start.IsZero() || e.Start.Year() < 2024 {
|
||||
return false
|
||||
}
|
||||
if e.Duration.Seconds() < 1 {
|
||||
return false
|
||||
}
|
||||
// if e.Recurrer != nil && !e.Recurrer.Valid() {
|
||||
// return false
|
||||
// }
|
||||
// func (e Event) Valid() bool {
|
||||
// if e.Title == "" {
|
||||
// return false
|
||||
// }
|
||||
// if e.Start.IsZero() || e.Start.Year() < 2024 {
|
||||
// return false
|
||||
// }
|
||||
// if e.Duration.Seconds() < 1 {
|
||||
// return false
|
||||
// }
|
||||
// // if e.Recurrer != nil && !e.Recurrer.Valid() {
|
||||
// // return false
|
||||
// // }
|
||||
|
||||
return true
|
||||
}
|
||||
// return true
|
||||
// }
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package item
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TimeFormat = "15:04"
|
||||
)
|
||||
|
||||
type Time struct {
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||
timeString := ""
|
||||
if err := json.Unmarshal(data, &timeString); err != nil {
|
||||
return err
|
||||
}
|
||||
nt := NewTimeFromString(timeString)
|
||||
t.t = nt.Time()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTime(hour, minute int) Time {
|
||||
return Time{
|
||||
t: time.Date(0, 0, 0, hour, minute, 0, 0, time.UTC),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimeFromString(timeStr string) Time {
|
||||
tm, err := time.Parse(TimeFormat, timeStr)
|
||||
if err != nil {
|
||||
return Time{t: time.Time{}}
|
||||
}
|
||||
|
||||
return Time{t: tm}
|
||||
}
|
||||
|
||||
func (t *Time) String() string {
|
||||
return t.t.Format(TimeFormat)
|
||||
}
|
||||
|
||||
func (t *Time) Time() time.Time {
|
||||
return t.t
|
||||
}
|
||||
|
||||
func (t *Time) IsZero() bool {
|
||||
return t.t.IsZero()
|
||||
}
|
||||
|
||||
func (t *Time) Hour() int {
|
||||
return t.t.Hour()
|
||||
}
|
||||
|
||||
func (t *Time) Minute() int {
|
||||
return t.t.Minute()
|
||||
}
|
||||
|
||||
func (t *Time) Add(d time.Duration) Time {
|
||||
return Time{
|
||||
t: t.t.Add(d),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package item_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go-mod.ewintr.nl/planner/item"
|
||||
)
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
h, m := 11, 18
|
||||
tm := item.NewTime(h, m)
|
||||
expStr := "11:18"
|
||||
if expStr != tm.String() {
|
||||
t.Errorf("exp %v, got %v", expStr, tm.String())
|
||||
}
|
||||
actJSON, err := json.Marshal(tm)
|
||||
if err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
expJSON := fmt.Sprintf("%q", expStr)
|
||||
if expJSON != string(actJSON) {
|
||||
t.Errorf("exp %v, got %v", expJSON, string(actJSON))
|
||||
}
|
||||
var actTM item.Time
|
||||
if err := json.Unmarshal(actJSON, &actTM); err != nil {
|
||||
t.Errorf("exp nil, got %v", err)
|
||||
}
|
||||
if expStr != actTM.String() {
|
||||
t.Errorf("ecp %v, got %v", expStr, actTM.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeFromString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
str string
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
exp: "00:00",
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
str: "invalid",
|
||||
exp: "00:00",
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
str: "11:42",
|
||||
exp: "11:42",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
act := item.NewTimeFromString(tc.str)
|
||||
if tc.exp != act.String() {
|
||||
t.Errorf("exp %v, got %v", tc.exp, act.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2,70 +2,66 @@ package storage
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go-mod.ewintr.nl/planner/item"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
type LocalID interface {
|
||||
FindAll() (map[string]int, error)
|
||||
FindOrNext(id string) (int, error)
|
||||
Next() (int, error)
|
||||
Store(id string, localID int) error
|
||||
Delete(id string) error
|
||||
}
|
||||
// type LocalID interface {
|
||||
// FindAll() (map[string]int, error)
|
||||
// FindOrNext(id string) (int, error)
|
||||
// Next() (int, error)
|
||||
// Store(id string, localID int) error
|
||||
// Delete(id string) error
|
||||
// }
|
||||
|
||||
type Sync interface {
|
||||
FindAll() ([]item.Item, error)
|
||||
Store(i item.Item) error
|
||||
DeleteAll() error
|
||||
LastUpdate() (time.Time, error)
|
||||
}
|
||||
// type Sync interface {
|
||||
// FindAll() ([]item.Item, error)
|
||||
// Store(i item.Item) error
|
||||
// DeleteAll() error
|
||||
// LastUpdate() (time.Time, error)
|
||||
// }
|
||||
|
||||
type Event interface {
|
||||
Store(event item.Event) error
|
||||
Find(id string) (item.Event, error)
|
||||
FindAll() ([]item.Event, error)
|
||||
Delete(id string) error
|
||||
}
|
||||
// type Event interface {
|
||||
// Store(event item.Event) error
|
||||
// Find(id string) (item.Event, error)
|
||||
// FindAll() ([]item.Event, error)
|
||||
// Delete(id string) error
|
||||
// }
|
||||
|
||||
func NextLocalID(used []int) int {
|
||||
if len(used) == 0 {
|
||||
return 1
|
||||
}
|
||||
// func NextLocalID(used []int) int {
|
||||
// if len(used) == 0 {
|
||||
// return 1
|
||||
// }
|
||||
|
||||
sort.Ints(used)
|
||||
usedMax := 1
|
||||
for _, u := range used {
|
||||
if u > usedMax {
|
||||
usedMax = u
|
||||
}
|
||||
}
|
||||
// sort.Ints(used)
|
||||
// usedMax := 1
|
||||
// for _, u := range used {
|
||||
// if u > usedMax {
|
||||
// usedMax = u
|
||||
// }
|
||||
// }
|
||||
|
||||
var limit int
|
||||
for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
|
||||
}
|
||||
// var limit int
|
||||
// for limit = 1; limit <= len(used) || limit < usedMax; limit *= 10 {
|
||||
// }
|
||||
|
||||
newId := used[len(used)-1] + 1
|
||||
if newId < limit {
|
||||
return newId
|
||||
}
|
||||
// newId := used[len(used)-1] + 1
|
||||
// if newId < limit {
|
||||
// return newId
|
||||
// }
|
||||
|
||||
usedMap := map[int]bool{}
|
||||
for _, u := range used {
|
||||
usedMap[u] = true
|
||||
}
|
||||
// usedMap := map[int]bool{}
|
||||
// for _, u := range used {
|
||||
// usedMap[u] = true
|
||||
// }
|
||||
|
||||
for i := 1; i < limit; i++ {
|
||||
if _, ok := usedMap[i]; !ok {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// for i := 1; i < limit; i++ {
|
||||
// if _, ok := usedMap[i]; !ok {
|
||||
// return i
|
||||
// }
|
||||
// }
|
||||
|
||||
return limit
|
||||
}
|
||||
// return limit
|
||||
// }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -48,32 +47,33 @@ func (m *Memory) Updated(kinds []item.Kind, timestamp time.Time) ([]item.Item, e
|
|||
}
|
||||
|
||||
func (m *Memory) RecursBefore(date time.Time) ([]item.Item, error) {
|
||||
res := make([]item.Item, 0)
|
||||
for _, i := range m.items {
|
||||
if i.Recurrer == nil {
|
||||
continue
|
||||
}
|
||||
if i.RecurNext.Before(date) {
|
||||
res = append(res, i)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
// res := make([]item.Item, 0)
|
||||
// for _, i := range m.items {
|
||||
// if i.Recurrer == nil {
|
||||
// continue
|
||||
// }
|
||||
// if i.RecurNext.Before(date) {
|
||||
// res = append(res, i)
|
||||
// }
|
||||
// }
|
||||
// return res, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *Memory) RecursNext(id string, date time.Time, ts time.Time) error {
|
||||
i, ok := m.items[id]
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
if i.Recurrer == nil {
|
||||
return ErrNotARecurrer
|
||||
}
|
||||
if !i.Recurrer.On(date) {
|
||||
return fmt.Errorf("item does not recur on %v", date)
|
||||
}
|
||||
i.RecurNext = date
|
||||
i.Updated = ts
|
||||
m.items[id] = i
|
||||
// i, ok := m.items[id]
|
||||
// if !ok {
|
||||
// return ErrNotFound
|
||||
// }
|
||||
// if i.Recurrer == nil {
|
||||
// return ErrNotARecurrer
|
||||
// }
|
||||
// if !i.Recurrer.On(date) {
|
||||
// return fmt.Errorf("item does not recur on %v", date)
|
||||
// }
|
||||
// i.RecurNext = date
|
||||
// i.Updated = ts
|
||||
// m.items[id] = i
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue