Skip to content

fix(search): honor UTC+0 timezone offset instead of collapsing to default -7 - #55

Merged
tylermenezes merged 1 commit into
mainfrom
detail/bug-fix/fix-search-honor-utc-0-timezone-offset-instead-of-388448
Sep 18, 2026
Merged

tylermenezes merged 1 commit into
mainfrom
detail/bug-fix/fix-search-honor-utc-0-timezone-offset-instead-of-388448

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Bug

TIMEZONE_MATCH (15×) is the single largest weight in the getProjectMatches ranking query. The timezone offset wrapper in src/search/ElasticEntry.ts and its query-side mirror in src/search/getProjectMatches.ts both passed the inner getTimezoneOffset result through a JS falsy check (if (basicLookup) and || -7).

The inner util (src/utils/getTimezoneOffset.ts) returns null for unrecognized zones but a legitimate 0 (or -0, depending on ICU) for UTC+0 zones — Atlantic/Reykjavik, Africa/Accra, GMT, UTC, etc. Since 0 and -0 are falsy, UTC+0 mentors and students were silently collapsed to the -7 default — the same index bucket as Asia/Bangkok (UTC+7). This shifted the ±4h timezone boost window eastward and awarded the 15× multiplier to mentors 7–11h east of the student while denying it to mentors genuinely within ±4h, producing a systematic eastward skew in match ranking for any UTC+0 user.

Fix

Distinguish null (unrecognized) from a valid numeric zero on both paths — both must change together, or a UTC+0 mentor (indexed at 0) would stop matching a UTC+0 student (still queried at -7):

  • src/search/ElasticEntry.ts: if (basicLookup) return basicLookup;if (basicLookup !== null) return basicLookup;, and the legacy-string-dict fallback [...] || -7[...] ?? -7.
  • src/search/getProjectMatches.ts: getTimezoneOffset(student.timezone) || -7getTimezoneOffset(student.timezone) ?? -7.

This is the idiomatic fix because it matches the inner util's null-as-"unrecognized" sentinel contract and honors both signs of zero (-0 !== null and +0 !== null), so it's correct regardless of which ICU version produces which sign.

Testing

  • Added an offline regression suite, src/search/searchTimezone.test.ts (following the repo's existing hand-rolled assert/assertEqual style), covering: the inner util's 0-vs-null contract; the indexing path via projectToElasticEntry (UTC+0 → 0, real non-zero offsets unchanged, missing/unrecognized → -7, legacy SGT dict fallback → 8); the query-side boost window read out of the generated Elasticsearch query JSON via typedi-injected fakes (UTC+0 student → [-4..4], real UTC+7 student → [-11..-3], unrecognized/missing → -7); and an end-to-end ranking test that scores two otherwise-equal mentors against the real generated function-score. All assertions pass; a pre-fix run of the same suite reproduces the bug (UTC+0 → -7; window [-11..-3]).
  • Typecheck (tsc --skipLibCheck --noEmit) and the pre-existing offline suite (syncAlumniInteractions.test.ts) both still pass.
  • Verified end-to-end against live Elasticsearch 7.17.10 and Postgres 15.19, each started in a Docker container: seeded two mentors (real UTC+1 Africa/Lagos, real UTC+7 Asia/Bangkok), bulk-indexed via the real elasticSync task, and confirmed a UTC+0 student ranks the UTC+1 mentor above the UTC+7 mentor (scores 75 vs 5, the 15× timezone multiplier landing on the correct mentor), while a UTC+7 student still ranks the UTC+7 mentor first.
  • Seasonal check attempted for winter-UTC+0 zones (Europe/London, Europe/Lisbon, etc.) but could not confirm the === 0 assertion at this date — they're currently on summer time (offset -1). The fix honors both +0 and -0, the two falsy signs of zero, so the winter case is covered by the same mechanism verified for the year-round UTC+0 zones.
  • Lint could not be run: the repo pins @typescript-eslint/parser@3.x (via @codeday/eslint-typescript-config@2.1.5), which is incompatible with the installed typescript@5.2.2 and fails to parse every file in the tree with DeprecationError: 'originalKeywordKind'. This is a pre-existing toolchain issue unrelated to this change.

Automatic Fixes PRs can be configured here.

@detail-app
detail-app Bot requested a review from tylermenezes September 18, 2026 02:53
@tylermenezes
tylermenezes merged commit 9895310 into main Sep 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant