diff --git a/finders/dbfinder/finder.go b/finders/dbfinder/finder.go index e59316d9..9b6e37db 100644 --- a/finders/dbfinder/finder.go +++ b/finders/dbfinder/finder.go @@ -152,8 +152,8 @@ func (f *Finder) RouteStopBuffer(ctx context.Context, param *model.RouteStopBuff func (f *Finder) FindFeedVersionServiceWindow(ctx context.Context, fvid int) (*model.ServiceWindow, error) { a, _, err := f.fvslCache.Get(ctx, fvid) - if err != nil { - return nil, err + if err != nil || a == nil || a.Location == nil { + return nil, errors.New("no service window found") } // Get local time nowLocal := time.Now().In(a.Location) @@ -166,7 +166,6 @@ func (f *Finder) FindFeedVersionServiceWindow(ctx context.Context, fvid int) (*m StartDate: a.StartDate, EndDate: a.EndDate, FallbackWeek: a.FallbackWeek, - Location: a.Location, } return ret, err } diff --git a/finders/dbfinder/stop_time_select.go b/finders/dbfinder/stop_time_select.go index 497bf4c9..be917379 100644 --- a/finders/dbfinder/stop_time_select.go +++ b/finders/dbfinder/stop_time_select.go @@ -236,12 +236,11 @@ func StopTimeFilterExpand(where *model.StopTimeFilter, fvsw *model.ServiceWindow // Further processing of the StopTimeFilter if where != nil { - var loc *time.Location var nowLocal time.Time if fvsw != nil { - loc = fvsw.Location nowLocal = fvsw.NowLocal } + loc := nowLocal.Location() // Set ServiceDate to local timezone // ServiceDate is a strict GTFS calendar date diff --git a/finders/dbfinder/trip_select.go b/finders/dbfinder/trip_select.go index 5e74a97c..ec3b452d 100644 --- a/finders/dbfinder/trip_select.go +++ b/finders/dbfinder/trip_select.go @@ -50,7 +50,7 @@ func TripSelect(limit *int, after *model.Cursor, ids []int, active bool, permFil if dow < 0 { dow = 6 } - where.ServiceDate = tzTruncate(fvsw.FallbackWeek.AddDate(0, 0, dow), fvsw.Location) + where.ServiceDate = tzTruncate(fvsw.FallbackWeek.AddDate(0, 0, dow), fvsw.NowLocal.Location()) } } } diff --git a/finders/dbfinder/util.go b/finders/dbfinder/util.go index e92d92fc..3be75951 100644 --- a/finders/dbfinder/util.go +++ b/finders/dbfinder/util.go @@ -3,12 +3,12 @@ package dbfinder import ( "fmt" "regexp" - "strconv" "strings" "time" "unicode" sq "github.com/Masterminds/squirrel" + "github.com/interline-io/log" "github.com/interline-io/transitland-lib/tt" "github.com/interline-io/transitland-server/model" ) @@ -35,6 +35,10 @@ func kebabize(a string) string { } func tzTruncate(s time.Time, loc *time.Location) *tt.Date { + if loc == nil { + log.Error().Msg("tzTruncate: loc is nil, set to UTC") + loc = time.UTC + } return ptr(tt.NewDate(time.Date(s.Year(), s.Month(), s.Day(), 0, 0, 0, 0, loc))) } @@ -62,11 +66,6 @@ func checkFloat(v *float64, min float64, max float64) float64 { return *v } -func atoi(v string) int { - a, _ := strconv.Atoi(v) - return a -} - // unicode aware remove all non-alphanumeric characters // this is not for escaping sql; just for preparing to_tsquery func alphanumeric(v string) string { diff --git a/finders/rtfinder/lookup.go b/finders/rtfinder/lookup.go index c81bd5ea..3b3656a3 100644 --- a/finders/rtfinder/lookup.go +++ b/finders/rtfinder/lookup.go @@ -105,19 +105,27 @@ func (f *lookupCache) StopTimezone(ctx context.Context, id int, known string) (* // If a timezone is provided, save it and return immediately if known != "" { - log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Msg("tz: using known timezone") + log.TraceCheck(func() { + log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Msg("tz: using known timezone") + }) return f.tzCache.Add(id, known) } // Check the cache if loc, ok := f.tzCache.Get(id); ok { - log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: using cached timezone") + log.TraceCheck(func() { + log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: using cached timezone") + }) return loc, ok } else { - log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: timezone not in cache") + log.TraceCheck(func() { + log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: timezone not in cache") + }) } if id == 0 { - log.For(ctx).Trace().Int("stop_id", id).Msg("tz: lookup failed, cant find timezone for stops with id=0 unless speciifed explicitly") + log.TraceCheck(func() { + log.For(ctx).Trace().Int("stop_id", id).Msg("tz: lookup failed, cant find timezone for stops with id=0 unless speciifed explicitly") + }) return nil, false } // Otherwise lookup the timezone @@ -139,7 +147,9 @@ func (f *lookupCache) StopTimezone(ctx context.Context, id int, known string) (* return nil, false } loc, ok := f.tzCache.Add(id, tz) - log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: lookup successful") + log.TraceCheck(func() { + log.For(ctx).Trace().Int("stop_id", id).Str("known", known).Str("loc", loc.String()).Msg("tz: lookup successful") + }) return loc, ok } diff --git a/internal/clock/clock.go b/internal/clock/clock.go index aea4d928..ef71e6c4 100644 --- a/internal/clock/clock.go +++ b/internal/clock/clock.go @@ -1,6 +1,10 @@ package clock -import "time" +import ( + "time" + + "github.com/interline-io/log" +) // Allow for time mocking type Clock interface { @@ -26,5 +30,9 @@ func (dc *Mock) Now() time.Time { // Helpers func tzTruncate(s time.Time, loc *time.Location) time.Time { + if loc == nil { + log.Error().Msg("tzTruncate: loc is nil, set to UTC") + loc = time.UTC + } return time.Date(s.Year(), s.Month(), s.Day(), 0, 0, 0, 0, loc) } diff --git a/internal/clock/swcache.go b/internal/clock/swcache.go index c74d2250..37627dbd 100644 --- a/internal/clock/swcache.go +++ b/internal/clock/swcache.go @@ -23,19 +23,19 @@ type ServiceWindow struct { type ServiceWindowCache struct { db sqlx.Ext lock sync.Mutex - fvslWindows map[int]ServiceWindow + fvslWindows map[int]*ServiceWindow tzCache *tzcache.Cache[int] } func NewServiceWindowCache(db sqlx.Ext) *ServiceWindowCache { return &ServiceWindowCache{ db: db, - fvslWindows: map[int]ServiceWindow{}, + fvslWindows: map[int]*ServiceWindow{}, tzCache: tzcache.NewCache[int](), } } -func (f *ServiceWindowCache) Get(ctx context.Context, fvid int) (ServiceWindow, bool, error) { +func (f *ServiceWindowCache) Get(ctx context.Context, fvid int) (*ServiceWindow, bool, error) { f.lock.Lock() defer f.lock.Unlock() a, ok := f.fvslWindows[fvid] @@ -51,6 +51,8 @@ func (f *ServiceWindowCache) Get(ctx context.Context, fvid int) (ServiceWindow, if fvData.Location == nil { return a, false, fmt.Errorf("unable to get cached default timezone for feed version %d", fvid) } + + a = &ServiceWindow{} a.Location = fvData.Location // Get fallback week from FVSL data diff --git a/model/params.go b/model/params.go index ef1046eb..2dfde940 100644 --- a/model/params.go +++ b/model/params.go @@ -14,7 +14,6 @@ type ServiceWindow struct { StartDate time.Time EndDate time.Time FallbackWeek time.Time - Location *time.Location } type StopPlaceParam struct { diff --git a/server/gql/stop_time_resolver.go b/server/gql/stop_time_resolver.go index d05ed839..f56fe50c 100644 --- a/server/gql/stop_time_resolver.go +++ b/server/gql/stop_time_resolver.go @@ -52,7 +52,7 @@ func (r *stopTimeResolver) Trip(ctx context.Context, obj *model.StopTime) (*mode func (r *stopTimeResolver) Arrival(ctx context.Context, obj *model.StopTime) (*model.StopTimeEvent, error) { // Lookup timezone loc, ok := model.ForContext(ctx).RTFinder.StopTimezone(ctx, obj.StopID.Int(), "") - if !ok { + if loc == nil || !ok { return nil, errors.New("timezone not available for stop") } // Create arrival; fallback to RT departure if arrival is not present @@ -73,7 +73,7 @@ func (r *stopTimeResolver) Arrival(ctx context.Context, obj *model.StopTime) (*m func (r *stopTimeResolver) Departure(ctx context.Context, obj *model.StopTime) (*model.StopTimeEvent, error) { // Lookup timezone loc, ok := model.ForContext(ctx).RTFinder.StopTimezone(ctx, obj.StopID.Int(), "") - if !ok { + if loc == nil || !ok { return nil, errors.New("timezone not available for stop") } // Create departure; fallback to RT arrival if departure is not present