Repository navigation
refactor: read the US/Eastern zone from one constant - #154
Merged
Merged
Conversation
DataFrames from both handlers, INTERNAL dates, the rate-limit reset time and exception timestamps all render in US/Eastern, but the zone was built from a literal in four places. DEFAULT_TIMEZONE moves from utils to internal_settings, which exceptions can import (utils imports exceptions), and every site reads it; the polars handler takes its name with .zone. pytz returns one cached object per zone name, so each site gets the same object it built before, and polars the same string.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #154 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 62 62
Lines 3196 3196
=========================================
Hits 3196 3196
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The fixture's docstring said it pointed every module that renders a timestamp at Asia/Tokyo. It patches the exception module and the two DataFrame handlers, the sites the test checks; utils and types also read the constant and keep it.
This was referenced Sep 25, 2026
✅ Ready — Code reviewVersion impact: none. This internal refactor centralizes the US/Eastern timezone object while preserving Rating patch 6/6 (exemplary) · proof 6/6 (comprehensive) · overall 6/6 Since last review
Reviewed |
…h the client The shared zone becomes internal_settings._DEFAULT_TIMEZONE, and utils assigns DEFAULT_TIMEZONE from it rather than importing it. An imported name is not part of a module's public surface, so the surface stays the one on main and nothing changes for a caller. The AST scan for the "US/Eastern" literal and the render test that built the handlers and an exception by hand give way to respx tests through client.stocks.quotes. With the zone set to Asia/Tokyo in the five modules that read it, the INTERNAL dates, the credit reset, the pandas and polars DataFrames and the timestamp of a 400 answer all come out in Asia/Tokyo. Another test pins utils.DEFAULT_TIMEZONE to US/Eastern. The pandas docstring of _convert_timestamp_columns takes the text the same docstring has on fix/null-date-float-overflow, so the two branches merge cleanly.
MarketDataDev01
approved these changes
Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #115
Summary
Every timestamp the SDK renders is US/Eastern, and the zone was built from a literal in four places:
utils.DEFAULT_TIMEZONE, the pandas handler, the polars handler (.zone) and the exception timestamp. The zone is now built once, ininternal_settings._DEFAULT_TIMEZONE, and every site reads it, so a DataFrame and an exception from the same call cannot end up in different zones after a change that misses one copy.marketdata.utils.DEFAULT_TIMEZONEstays public, assigned from the shared value. The change is specific to Python; no sibling SDK is involved.Version impact
None. The public surface is the one on main:
marketdata.utils.DEFAULT_TIMEZONEis still a name thatutilsdefines (an assignment, not an import), and the new shared value is private. No output changes: every site gets the same object it built before (see Proof). No CHANGELOG entry.For reference,
pyproject.tomlis at 1.3.0, and the CHANGELOG's[Unreleased]section, which already has BREAKING entries, is the 2.0.0 in progress.The change
internal_settings.py:_DEFAULT_TIMEZONE = pytz.timezone("US/Eastern"). It sits here and not inutilsbecauseutilsimportsexceptions, which needs the zone too.utils.py:DEFAULT_TIMEZONE = _DEFAULT_TIMEZONE, assigned so the name stays part of the module's surface. Its functions readDEFAULT_TIMEZONEas on main, andutilsno longer importspytz.types.pyimports_DEFAULT_TIMEZONEfrominternal_settingsinstead ofDEFAULT_TIMEZONEfromutils.exceptions.py:_coerce_timestampstampsdatetime.now(_DEFAULT_TIMEZONE). Thepytzimport is gone.output_handlers/pandas.pyandoutput_handlers/polars.py:_convert_timestamp_columnsreads_DEFAULT_TIMEZONE, and polars takes its name with.zone. Neither importspytzany more.fix/null-date-float-overflow(fix(dataframe): read null dates without pandas 2.x overflowing #158), so the two branches merge cleanly.Proof
Same object at every site. pytz keeps one object per zone name, so
_DEFAULT_TIMEZONEis the very object each site built withpytz.timezone("US/Eastern"), and its.zoneis the same string polars got. With pytz 2026.3.post1 fromuv.lock:Suite.
uv run pytest -n 4 --cov=marketdata, Python 3.10. mainbc0619c: 1954 passed. This branch at0e0f1c2: 1959 passed, the same 1954 plus the 5 intest_default_timezone.py, coverage 100% (3196 statements, 0 missed). No existing test changed, so the ones that pinpytz.timezone("US/Eastern")andpl.Datetime("us", "US/Eastern")still check the public contract on their own.The tests catch every site that stops reading the constant. Mutation on
0e0f1c2, one site at a time, a literal put back while the module keeps its import. Each run: 1 failed, 4 passed, and the failure is an assertion, not the setup.utils.format_timestampfromtimestamp(number / scale, tz=__import__("pytz").timezone("US/Eastern"))test_internal_dates_and_the_credit_reset_follow_the_constantstr(quotes[0].updated.tzinfo) == "Asia/Tokyo":'US/Eastern' == 'Asia/Tokyo'UserRateLimits.__post_init__fromtimestamp(float(reset_time), tz=__import__("pytz").timezone("US/Eastern"))test_internal_dates_and_the_credit_reset_follow_the_constantstr(reset_time.tzinfo) == "Asia/Tokyo":'US/Eastern' == 'Asia/Tokyo'BaseMarketdataException._coerce_timestampdatetime.now(__import__("pytz").timezone("US/Eastern"))test_an_error_timestamp_follows_the_constant'2026-09-28 23:31:47' <= '2026-09-28 10:31:47'_convert_timestamp_columnsdefault_tz = __import__("pytz").timezone("US/Eastern")test_dataframe_dates_follow_the_constant[pandas]'US/Eastern' == 'Asia/Tokyo'_convert_timestamp_columnsdefault_tz = __import__("pytz").timezone("US/Eastern").zonetest_dataframe_dates_follow_the_constant[polars]'US/Eastern' == 'Asia/Tokyo'Restored: 5 passed.
Against main's package code (
git checkout origin/main -- src/marketdata): the four client tests error at setup withAttributeError: <module 'marketdata.types' ...> has no attribute '_DEFAULT_TIMEZONE', because main has no shared constant;test_utils_default_timezone_is_us_easternpasses, because the public name is the same on main.Tests
src/tests/test_default_timezone.py, 5 tests. Thetokyofixture sets the zone to Asia/Tokyo (no daylight saving time, 13 or 14 hours from US/Eastern) in the five modules that read it:DEFAULT_TIMEZONEinutils, and_DEFAULT_TIMEZONEintypes,exceptionsand both handlers. Each module binds the name when it is imported, so the fixture replaces it where it is read, and a site that built its own zone would stay in US/Eastern. Every call goes throughclient.stocks.quoteswithrespx:test_internal_dates_and_the_credit_reset_follow_the_constant: the INTERNALupdateddate (read byutils.format_timestamp) andget_meta(result).rate_limits.reset_time, read from a realx-api-ratelimit-resetheader byUserRateLimits, are in Asia/Tokyo.test_dataframe_dates_follow_the_constant[pandas]and[polars]: theupdatedcolumn of the DataFrame each library builds is in Asia/Tokyo.test_an_error_timestamp_follows_the_constant: a 400 answer raisesBadRequestErrorstamped with the time in Asia/Tokyo.test_utils_default_timezone_is_us_eastern:marketdata.utils.DEFAULT_TIMEZONE is pytz.timezone("US/Eastern").Review round 1
2026-09-28, on
0e0f1c2.3bf2e7d3marketdata.utils.DEFAULT_TIMEZONEbecame an imported nameutilsassigns it from the shared value, and the shared value is private. The surface is the one on main, so the version impact stays none.dfa4f13dclient.stocks.quotesreplace both, and they also cover the two readers the render test did not patch,utilsandtypes(see the mutation table).Public surface, main against this branch:
marketdata.utils.DEFAULT_TIMEZONEutils:pytz.timezone("US/Eastern")utils:DEFAULT_TIMEZONE = _DEFAULT_TIMEZONE, the same objectmarketdata.internal_settings._DEFAULT_TIMEZONEgit merge-treeof0e0f1c2against main and against the open #157, #158 and #159 is clean../lint.shpasses.