fix: anchor growth forecasts to real Tehran date
This commit is contained in:
@@ -47,10 +47,13 @@ type Builder interface {
|
||||
Build(ctx context.Context, merchantID int64, terminalID string) (Analysis, error)
|
||||
}
|
||||
|
||||
type PostgreSQLBuilder struct{ pool *pgxpool.Pool }
|
||||
type PostgreSQLBuilder struct {
|
||||
pool *pgxpool.Pool
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func NewPostgreSQLBuilder(pool *pgxpool.Pool) *PostgreSQLBuilder {
|
||||
return &PostgreSQLBuilder{pool: pool}
|
||||
return &PostgreSQLBuilder{pool: pool, now: time.Now}
|
||||
}
|
||||
|
||||
func (b *PostgreSQLBuilder) Build(ctx context.Context, merchantID int64, terminalID string) (Analysis, error) {
|
||||
@@ -88,7 +91,7 @@ func (b *PostgreSQLBuilder) Build(ctx context.Context, merchantID int64, termina
|
||||
if err := b.pool.QueryRow(ctx, `SELECT max(created_at AT TIME ZONE 'Asia/Tehran')::date FROM payment_sessions`).Scan(&latest); err != nil {
|
||||
return Analysis{}, fmt.Errorf("loading dataset watermark date: %w", err)
|
||||
}
|
||||
forecastStart := latest.AddDate(0, 0, 1)
|
||||
forecastStart := tehranToday(b.now())
|
||||
peer, err := b.loadPeer(ctx, merchantID, terminalID)
|
||||
if err != nil {
|
||||
return Analysis{}, err
|
||||
|
||||
@@ -46,10 +46,11 @@ func (s *Service) Create(ctx context.Context, userID, merchantID int64, request
|
||||
return Record{}, false, err
|
||||
}
|
||||
}
|
||||
watermark, start, err := s.repository.DatasetMetadata(ctx)
|
||||
watermark, _, err := s.repository.DatasetMetadata(ctx)
|
||||
if err != nil {
|
||||
return Record{}, false, err
|
||||
}
|
||||
start := tehranToday(now)
|
||||
fingerprint := s.fingerprint(merchantID, request.TerminalID, watermark, start)
|
||||
var refreshDay *time.Time
|
||||
if request.Refresh {
|
||||
@@ -91,10 +92,11 @@ func (s *Service) Latest(ctx context.Context, merchantID int64, terminalID strin
|
||||
record, err := s.repository.Latest(ctx, merchantID, terminalID, growth.AnalyticsVersion)
|
||||
if err == nil {
|
||||
record.Cached = true
|
||||
watermark, start, metadataErr := s.repository.DatasetMetadata(ctx)
|
||||
watermark, _, metadataErr := s.repository.DatasetMetadata(ctx)
|
||||
if metadataErr != nil {
|
||||
record.Stale = true
|
||||
} else {
|
||||
start := tehranToday(s.now())
|
||||
record.Stale = record.Fingerprint != s.fingerprint(merchantID, terminalID, watermark, start)
|
||||
}
|
||||
record.NextRefreshAt, _ = s.repository.NextRefreshAt(ctx, merchantID, terminalID, growth.AnalyticsVersion, s.now())
|
||||
|
||||
@@ -295,7 +295,7 @@ func (r *Repository) DatasetMetadata(ctx context.Context) (string, time.Time, er
|
||||
if err != nil {
|
||||
return "", time.Time{}, fmt.Errorf("loading growth dataset metadata: %w", err)
|
||||
}
|
||||
return sha + ":" + latest.Format(time.DateOnly), latest.AddDate(0, 0, 1), nil
|
||||
return sha + ":" + latest.Format(time.DateOnly), latest, nil
|
||||
}
|
||||
|
||||
func (r *Repository) CleanupSuperseded(ctx context.Context, cutoff time.Time) (int64, error) {
|
||||
|
||||
@@ -27,6 +27,16 @@ func TestNextTehranRefreshUsesCalendarBoundary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTehranTodayUsesClockInsteadOfDatasetWatermark(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, time.August, 21, 8, 30, 0, 0, time.UTC)
|
||||
got := tehranToday(now)
|
||||
if got.Format(time.DateOnly) != "2026-08-21" {
|
||||
t.Fatalf("tehranToday() = %s", got.Format(time.DateOnly))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEstimatedTokenReservationCoversEveryRetryAndTurn(t *testing.T) {
|
||||
t.Parallel()
|
||||
small := EstimatedTokenReservation([]byte("{}"))
|
||||
|
||||
@@ -2,6 +2,15 @@ package growthreport
|
||||
|
||||
import "time"
|
||||
|
||||
func tehranToday(now time.Time) time.Time {
|
||||
location, err := time.LoadLocation("Asia/Tehran")
|
||||
if err != nil {
|
||||
location = time.FixedZone("Asia/Tehran", 12600)
|
||||
}
|
||||
local := now.In(location)
|
||||
return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, location)
|
||||
}
|
||||
|
||||
func NextTehranRefresh(now time.Time) time.Time {
|
||||
location, err := time.LoadLocation("Asia/Tehran")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user