mirror of
https://github.com/stashapp/stash.git
synced 2025-12-07 17:02:38 +01:00
Add precision field to Date and handle parsing year/month-only dates
This commit is contained in:
parent
7bb437df67
commit
b4b1f4d7a1
2 changed files with 88 additions and 6 deletions
|
|
@ -1,31 +1,63 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DatePrecision int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// default precision is day
|
||||||
|
DatePrecisionDay DatePrecision = iota
|
||||||
|
DatePrecisionMonth
|
||||||
|
DatePrecisionYear
|
||||||
|
)
|
||||||
|
|
||||||
// Date wraps a time.Time with a format of "YYYY-MM-DD"
|
// Date wraps a time.Time with a format of "YYYY-MM-DD"
|
||||||
type Date struct {
|
type Date struct {
|
||||||
time.Time
|
time.Time
|
||||||
|
Precision DatePrecision
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateFormat = "2006-01-02"
|
var dateFormatPrecision []string = []string{
|
||||||
|
"2006-01-02",
|
||||||
|
"2006-01",
|
||||||
|
"2006",
|
||||||
|
}
|
||||||
|
|
||||||
func (d Date) String() string {
|
func (d Date) String() string {
|
||||||
return d.Format(dateFormat)
|
return d.Format(dateFormatPrecision[d.Precision])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Date) After(o Date) bool {
|
func (d Date) After(o Date) bool {
|
||||||
return d.Time.After(o.Time)
|
return d.Time.After(o.Time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseDate uses utils.ParseDateStringAsTime to parse a string into a date.
|
// ParseDate tries to parse the input string into a date using utils.ParseDateStringAsTime.
|
||||||
|
// If that fails, it attempts to parse the string with decreasing precision (month, then year).
|
||||||
|
// It returns a Date struct with the appropriate precision set, or an error if all parsing attempts fail.
|
||||||
func ParseDate(s string) (Date, error) {
|
func ParseDate(s string) (Date, error) {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
// default parse to day precision
|
||||||
ret, err := utils.ParseDateStringAsTime(s)
|
ret, err := utils.ParseDateStringAsTime(s)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return Date{}, err
|
return Date{Time: ret, Precision: DatePrecisionDay}, nil
|
||||||
}
|
}
|
||||||
return Date{Time: ret}, nil
|
|
||||||
|
errs = append(errs, err)
|
||||||
|
|
||||||
|
// try month and year precision
|
||||||
|
for i, format := range dateFormatPrecision[1:] {
|
||||||
|
ret, err := time.Parse(format, s)
|
||||||
|
if err == nil {
|
||||||
|
return Date{Time: ret, Precision: DatePrecision(i + 1)}, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Date{}, fmt.Errorf("failed to parse date %q: %v", s, errs)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
pkg/models/date_test.go
Normal file
50
pkg/models/date_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDateStringAsTime(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
output Date
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
// Full date formats (existing support)
|
||||||
|
{"RFC3339", "2014-01-02T15:04:05Z", Date{Time: time.Date(2014, 1, 2, 15, 4, 5, 0, time.UTC), Precision: DatePrecisionDay}, false},
|
||||||
|
{"Date only", "2014-01-02", Date{Time: time.Date(2014, 1, 2, 0, 0, 0, 0, time.UTC), Precision: DatePrecisionDay}, false},
|
||||||
|
{"Date with time", "2014-01-02 15:04:05", Date{Time: time.Date(2014, 1, 2, 15, 4, 5, 0, time.UTC), Precision: DatePrecisionDay}, false},
|
||||||
|
|
||||||
|
// Partial date formats (new support)
|
||||||
|
{"Year-Month", "2006-08", Date{Time: time.Date(2006, 8, 1, 0, 0, 0, 0, time.UTC), Precision: DatePrecisionMonth}, false},
|
||||||
|
{"Year only", "2014", Date{Time: time.Date(2014, 1, 1, 0, 0, 0, 0, time.UTC), Precision: DatePrecisionYear}, false},
|
||||||
|
|
||||||
|
// Invalid formats
|
||||||
|
{"Invalid format", "not-a-date", Date{}, true},
|
||||||
|
{"Empty string", "", Date{}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := ParseDate(tt.input)
|
||||||
|
|
||||||
|
if tt.expectError {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error for input %q, but got none", tt.input)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error for input %q: %v", tt.input, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !result.Time.Equal(tt.output.Time) || result.Precision != tt.output.Precision {
|
||||||
|
t.Errorf("For input %q, expected output %+v, got %+v", tt.input, tt.output, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue