diff --git a/NEWS.md b/NEWS.md index dd30d7eb..28dd18fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,5 @@ +**10/16/2026:** **Bug fix:** the time-zone table behind the `*DateTime` columns that `waterdata.get_samples()` and `wqp.get_results()` append, and behind `utils.format_datetime()`, now follows the code lists those services use: the USGS NWIS time-zone list (Samples, and the USGS records that the Water Quality Portal passes through) and the WQX TimeZone domain (Water Quality Portal). USGS records write American Samoa as `ZP-11`, which was missing, so every timed American Samoa row from `get_samples()`, and every timed USGS American Samoa row from `get_results()`, had a `NaT` datetime; it is now UTC-11. 39 other codes from the two lists that used to become `NaT` now resolve, among them the remaining `ZP*` offsets, `IDLW`, `IDLE`, `NZT`, `SWT`, and the retired WQX codes `AHST` and `YST`. **Behavior change:** codes that the table read with a different meaning from the USGS list now take the USGS meaning: `CAST` (+9:30), `CCT` (+8), `EASST` (+11), `EAST` (+10), `NFT` (-3:30), `NZDT` (+13), `WAST` (+7), and `WAT` (-1). `FJST` is now UTC+13. Where the two lists disagree, WQX wins: `SST` stays American Samoa (UTC-11), the meaning the Water Quality Portal returns, and `BST` is UTC-11. + **10/15/2026:** **Bug fix:** the `ActivityStartDateTime` and `Activity_StartDateTime` columns that `wqp.get_results()` and `waterdata.get_samples()` append, and the `datetime` column of `utils.format_datetime()`, read the time-zone code `GST` as Gulf Standard Time (UTC+4). The USGS time-zone codes and the WQX TimeZone domain both define `GST` as Guam Standard Time (UTC+10), so a sample collected on Guam at 10:00 local time was placed at 06:00 UTC instead of 00:00 UTC. `GST` is now UTC+10, and `HDT` (Hawaii-Aleutian Daylight Time, UTC-9), a USGS code that was missing from the table, now resolves instead of becoming `NaT`. **10/13/2026:** Twenty columns that the Water Data OGC collections return are now named, documented parameters of the getter that returns them; before, they could be passed only through `**queryables`. `get_field_measurements()`: `control_condition`, `day`, `field_measurements_series_id`, `measurement_rated`, `month`, `reading_type`, `time_of_day`, `year`. `get_peaks()`: `qualifier`, `time_of_day`, `value`. `get_monitoring_locations()`: `revision_created`, `revision_modified`, `revision_note`. `get_combined_metadata()`: `data_gap_interval`, `reading_type`. `get_time_series_metadata()`: `data_gap_interval`, `parameter_description`. `get_field_measurements_metadata()`: `reading_type`. `get_channel()`: `channel_location_direction`. They are keyword-only, so every existing call, including one that passes arguments by position, sends the same request as before; `sublocation_identifier` (10/07) is keyword-only too. **Behavior change:** `statistics_begin` in `get_time_series_metadata()` and `method_category` in `get_continuous()`, added in 1.4.0, are now keyword-only too, so a call that passed either by position, or passed a later argument by position (from `thresholds` in `get_time_series_metadata()` and from `value` in `get_continuous()`), must pass it by name. diff --git a/dataretrieval/codes/timezones.py b/dataretrieval/codes/timezones.py index 3a7e02aa..97542692 100644 --- a/dataretrieval/codes/timezones.py +++ b/dataretrieval/codes/timezones.py @@ -1,50 +1,61 @@ """Time zone information. Maps the time-zone abbreviations that services put beside a local date and -time to UTC offsets. Several abbreviations name more than one zone worldwide. -``GST`` follows the USGS time-zone codes and the WQX TimeZone domain, which -both define it as Guam Standard Time (UTC+10), not Gulf Standard Time. Other -entries that differ from the USGS list (``CCT``, ``EAST``, ``NFT``, ``NZDT``, -``SST``, ``WAT``) have not been reconciled, and the two lists disagree with -each other on ``SST``. +time to UTC offsets. Several abbreviations name more than one zone worldwide, +so the table follows the code lists the services draw from rather than common +usage: + +* the WQX TimeZone domain, used by the Water Quality Portal + (``tests/data/wqx_timezones.csv``), and +* the USGS NWIS time-zone list, used by the USGS Samples database and by the + USGS records the Water Quality Portal passes through + (``tests/data/usgs_timezones.xml``). + +Where the two lists disagree -- ``SST`` (American Samoa in WQX, Swedish Summer +Time in USGS) and ``BST`` (retired Bering Standard Time in WQX, British Summer +Time in USGS) -- WQX wins: the Water Quality Portal returns ``SST`` for American +Samoa, and USGS records write that zone as ``ZP-11``. Abbreviations in +neither list keep their common meaning. ``tests/timezones_test.py`` checks the +table against both lists. """ -tz_str = """-1200 Y --1100 X NUT SST --1000 W CKT HAST HST TAHT TKT --0900 V AKST GAMT GIT HADT HDT HNY +tz_str = """-1200 Y IDLW +-1100 X BST NUT SST ZP-11 +-1000 W AHST CKT HAST HST TAHT TKT +-0900 V AKST GAMT GIT HADT HDT HNY YST -0800 U AKDT CIST HAY HNP PST PT -0700 T HAP HNR MST PDT --0600 S CST EAST GALT HAR HNC MDT --0500 R CDT COT EASST ECT EST ET HAC HNE PET +-0600 S CST GALT HAR HNC MDT +-0500 R CDT COT ECT EST ET HAC HNE PET -0400 Q AST BOT CLT COST EDT FKT GYT HAE HNA PYT --0300 P ADT ART BRT CLST FKST GFT HAA PMST PYST SRT UYT WGT --0200 O BRST FNT PMDT UYST WGST --0100 N AZOT CVT EGT +-0300 P ADT ART BRT CLST FKST GFT HAA PMST PYST SRT UYT WGT ZP-3 +-0200 O BRST FNT PMDT UYST WGST ZP-2 +-0100 N AZOT CVT EGT WAT +0000 Z EGST GMT UTC WET WT -+0100 A CET DFT WAT WEDT WEST -+0200 B CAT CEDT CEST EET SAST WAST -+0300 C EAT EEDT EEST IDT MSK -+0400 D AMT AZT GET KUYT MSD MUT RET SAMT SCT -+0500 E AMST AQTT AZST HMT MAWT MVT PKT TFT TJT TMT UZT YEKT -+0600 F ALMT BIOT BTT IOT KGT NOVT OMST YEKST -+0700 G CXT DAVT HOVT ICT KRAT NOVST OMSST THA WIB -+0800 H ACT AWST BDT BNT CAST HKT IRKT KRAST MYT PHT SGT ULAT WITA WST -+0900 I AWDT IRKST JST KST PWT TLT WDT WIT YAKT -+1000 K AEST ChST GST PGT VLAT YAKST YAPT -+1100 L AEDT LHDT MAGT NCT PONT SBT VLAST VUT -+1200 M ANAST ANAT FJT GILT MAGST MHT NZST PETST PETT TVT WFT -+1330 FJST NZDT -+1130 NFT -+1030 ACDT LHST -+0930 ACST -+0630 CCT MMT ++0100 A CET DFT DNT DST FST MET MEWT MEZ NOR SET SWT WEDT WEST WETDST ++0200 B CAT CEDT CEST CETDST EET FWT IST MEST METDST SAST ++0300 C BT EAT EEDT EEST EETDST IDT MSK ++0400 D AMT AZT GET KUYT MSD MUT RET SAMT SCT ZP4 ++0500 E AMST AQTT AZST HMT MAWT MVT PKT TFT TJT TMT UZT YEKT ZP5 ++0600 F ALMT BIOT BTT IOT KGT NOVT OMST YEKST ZP6 ++0700 G CXT DAVT HOVT ICT KRAT NOVST OMSST THA WAST WIB ++0800 H ACT AWST BDT BNT CCT HKT IRKT KRAST MYT PHT SGT ULAT WADT WITA WST ++0900 I AWDT AWSST IRKST JST KST PWT TLT WDT WIT YAKT ++1000 K AEST ChST EAST GST LIGT PGT VLAT YAKST YAPT ++1100 L AEDT AESST EASST LHDT MAGT NCT PONT SBT VLAST VUT ZP11 ++1200 M ANAST ANAT FJT GILT IDLE MAGST MHT NZST NZT PETST PETT TVT WFT ++1300 FJST NZDT ++1030 ACDT ACSST CADT LHST SADT ++0930 ACST CAST SAT ++0830 MT ++0730 JT ++0630 MMT +0545 NPT +0530 SLT +0430 AFT IRDT -+0330 IRST ++0330 IRST IT -0230 HAT NDT --0330 HNT NST NT +-0330 HNT NFT NST NT -0430 HLV VET -0930 MART MIT""" diff --git a/tests/contracts/README.md b/tests/contracts/README.md index c8d24e1d..28d29a61 100644 --- a/tests/contracts/README.md +++ b/tests/contracts/README.md @@ -10,7 +10,8 @@ The suite uses four dependency-oriented layers without moving established tests: response parsing, and documented protocol behavior. - **Component** (`transport_test.py`, `waterdata_chunking_test.py`, `waterdata_queryables_test.py`, `waterdata_endpoints_test.py`, - `waterdata_properties_test.py`, `rdb_test.py`, `_csv_test.py`): one internal + `waterdata_properties_test.py`, `rdb_test.py`, `_csv_test.py`, + `timezones_test.py`): one internal responsibility in isolation. - **Cross-component** (`architecture_test.py`, `headers_host_scoping_test.py`, `waterdata_progress_test.py`): dependency fitness functions and behavior that diff --git a/tests/data/usgs_timezones.xml b/tests/data/usgs_timezones.xml new file mode 100644 index 00000000..6cc666f8 --- /dev/null +++ b/tests/data/usgs_timezones.xml @@ -0,0 +1,548 @@ + + + Time Zone + Time zone offset. An ANSI SQL/92 time zone offset string. Some examples are '-07:00' (Eastern), '+02:00' (Eastern Europe), and '+03:30' (India). + United States Geological Survey, National Water Information System + + + UTC + 1 + 00:00 + null + null + null + Universal Coordinated Time + Universal Coordinated Time + + + NST + 2 + -03:30 + NDT + Newfoundland Daylight Time + -02:30 + Newfoundland Standard Time + Newfoundland + + + AST + 3 + -04:00 + ADT + Atlantic Daylight Time + -03:00 + Atlantic Standard Time (Canada) + Atlantic (Canada) + + + EST + 4 + -05:00 + EDT + Eastern Daylight Time + -04:00 + Eastern Standard Time + Eastern North America + + + CST + 5 + -06:00 + CDT + Central Daylight Time + -05:00 + Central Standard Time + Central North America + + + MST + 6 + -07:00 + MDT + Mountain Daylight Time + -06:00 + Mountain Standard Time + Mountain North America + + + PST + 7 + -08:00 + PDT + Pacific Daylight Time + -07:00 + Pacific Standard Time + Pacific North America + + + AKST + 8 + -09:00 + AKDT + Alaska Daylight Time + -08:00 + Alaska Standard Time + Alaska + + + HST + 9 + -10:00 + HDT + Hawaii Daylight Time + -09:00 + Hawaii Standard Time + Hawaii + + + ZP-11 + 10 + -11:00 + null + null + null + GMT -11 hours + GMT -11 hours + + + IDLW + 11 + -12:00 + null + null + null + International Date Line, West + International Date Line, West + + + IDLE + 12 + +12:00 + null + null + null + International Date Line, East + International Date Line, East + + + NZST + 13 + +12:00 + NZDT + New Zealand Daylight Time + +13:00 + New Zealand Standard Time + New Zealand + + + NZT + 14 + +12:00 + NZDT + New Zealand Daylight Time + +13:00 + New Zealand Time + New Zealand + + + ZP11 + 15 + +11:00 + null + null + null + GMT +11 hours + Zone GMT +11 Hours + + + AEST + 16 + +10:00 + AESST + Australia Eastern Summer Time + +11:00 + Australia Eastern Standard Time + Eastern Australia + + + EAST + 17 + +10:00 + EASST + East Australian Summer Time + +11:00 + East Australian Standard Time + East Australia + + + GST + 18 + +10:00 + null + null + null + Guam Standard Time + Guam Standard Time, Russia Zone 9 + + + LIGT + 19 + +10:00 + null + null + null + Melbourne, Australia + Melbourne + + + ACST + 20 + +09:30 + ACSST + Central Australia Summer Time + +10:30 + Central Australia Standard Time + Central Australia + + + CAST + 21 + +09:30 + CADT + Central Australia Daylight Time + +10:30 + Central Australia Standard Time + Central Australia + + + SAT + 22 + +09:30 + SADT + South Australian Daylight Time + +10:30 + South Australian Standard Time + South Australia + + + JST + 23 + +09:00 + null + null + null + Japan Standard Time + Japan Standard Time, Russia Zone 8 + + + KST + 24 + +09:00 + null + null + null + Korea Standard Time + Korea + + + MT + 25 + +08:30 + null + null + null + Moluccas Time + Moluccas + + + AWST + 26 + +08:00 + AWSST + Australia Western Summer Time + +09:00 + Australia Western Standard Time + Western Australia + + + CCT + 27 + +08:00 + null + null + null + China Coastal Time + China Coastal + + + WST + 28 + +08:00 + WDT + West Australian Daylight Time + +09:00 + West Australian Standard Time + West Australian + + + JT + 29 + +07:30 + null + null + null + Java Time + Java + + + WAST + 30 + +07:00 + WADT + West Australian Daylight Time + +08:00 + West Australian Standard Time + West Australia + + + ZP6 + 31 + +06:00 + null + null + null + GMT +6 hours + Zone GMT +6 Hours + + + ZP5 + 32 + +05:00 + null + null + null + GMT +5 hours + Zone GMT +5 Hours + + + ZP4 + 33 + +04:00 + null + null + null + GMT +4 hours + Zone GMT +4 Hours + + + IT + 34 + +03:30 + null + null + null + Iran Time + Iran + + + BT + 35 + +03:00 + null + null + null + Baghdad Time + Baghdad + + + EET + 36 + +02:00 + EETDST + Eastern Europe Daylight Time + +03:00 + Eastern Europe Standard Time + Eastern Europe, Russia Zone 1 + + + IST + 37 + +02:00 + null + null + null + Israel Standard Time + Israel + + + CET + 38 + +01:00 + CETDST + Central European Daylight Time + +02:00 + Central European Time + Central Europe + + + DNT + 39 + +01:00 + null + null + null + Dansk Normal Time + Dansk + + + DST + 40 + +01:00 + null + null + null + Dansk Summer Time + Dansk + + + FST + 41 + +01:00 + FWT + French Winter Time + +02:00 + French Summer Time + French + + + MET + 42 + +01:00 + METDST + Middle Europe Daylight Time + +02:00 + Middle Europe Time + Middle Europe + + + MEWT + 43 + +01:00 + MEST + Middle Europe Summer Time + +02:00 + Middle Europe Winter Time + Middle Europe + + + MEZ + 44 + +01:00 + null + null + null + Middle Europe Zone + Middle Europe + + + NOR + 45 + +01:00 + null + null + null + Norway Standard Time + Norway + + + SET + 46 + +01:00 + null + null + null + Seychelles Time + Seychelles + + + SWT + 47 + +01:00 + SST + Swedish Summer Time + +02:00 + Swedish Winter Time + Swedish + + + GMT + 48 + 00:00 + BST + British Summer Time + +01:00 + Greenwich Mean Time + Great Britain + + + WET + 49 + 00:00 + WETDST + Western Europe Daylight Time + +01:00 + Western Europe + Western Europe + + + WAT + 50 + -01:00 + null + null + null + West Africa Time + West Africa + + + ZP-2 + 51 + -02:00 + null + null + null + GMT -2 hours + Zone GMT -2 Hours + + + ZP-3 + 52 + -03:00 + null + null + null + GMT -3 hours + Zone GMT -3 Hours + + + NFT + 53 + -03:30 + NDT + Newfoundland Daylight Time + -02:30 + Newfoundland Standard Time + Newfoundland + + + AFT + 54 + +04:30 + null + null + null + Afghanistan Time + Afghanistan + + + diff --git a/tests/data/wqx_timezones.csv b/tests/data/wqx_timezones.csv new file mode 100644 index 00000000..7d8644e2 --- /dev/null +++ b/tests/data/wqx_timezones.csv @@ -0,0 +1,28 @@ +Domain,Unique Identifier,Code,Name,Offset,Last Change Date +Time Zone(TimeZoneCode),5,ADT,Atlantic Daylight Time,-3,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),3,AHST,Alaska-Hawaii Standard Time (*retired: >1983 use AKST),-10,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),1,AKDT,Alaska Daylight Time,-8,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),2,AKST,Alaska Standard Time,-9,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),6,AST,Atlantic Standard Time,-4,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),7,BST,Bering Standard Time (*retired: >1983 use HAST),-11,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),8,CDT,Central Daylight Time,-5,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),25,CEST,Sweden Daylight Time or Central European Standard Time,2,7/12/2016 9:39:33 AM +Time Zone(TimeZoneCode),24,CET,Stockholm Sweden Time or Central European Time,1,7/12/2016 9:39:33 AM +Time Zone(TimeZoneCode),9,CST,Central Standard Time,-6,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),10,EDT,Eastern Daylight Time,-4,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),11,EST,Eastern Standard Time,-5,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),23,GMT,Greenwich Mean Time ,0,6/19/2009 11:19:08 AM +Time Zone(TimeZoneCode),12,GST,Guam Standard Time Zone (also Chamorro Standard Time),10,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),13,HADT,Hawaii-Aleutian Daylight Time,-9,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),14,HAST,Hawaii-Aleutian Standard Time,-10,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),26,KST,Korea Standard Time,9,7/12/2016 9:39:33 AM +Time Zone(TimeZoneCode),15,MDT,Mountain Daylight Time,-6,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),16,MST,Mountain Standard Time,-7,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),17,NDT,Newfoundland Daylight Time,-2.5,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),18,NST,Newfoundland Standard Time,-3.5,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),19,PDT,Pacific Daylight Time,-7,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),20,PST,Pacific Standard Time,-8,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),4,SST,American Samoa Standard Time,-11,8/11/2006 10:57:50 AM +Time Zone(TimeZoneCode),22,UTC,Coordinated Universal Time,0,1/15/2010 1:10:00 PM +Time Zone(TimeZoneCode),21,YST,Yukon Standard Time: U.S.-Yukatat (*retired: >1983 use AKST),-9,8/11/2006 10:57:50 AM + diff --git a/tests/timezones_test.py b/tests/timezones_test.py new file mode 100644 index 00000000..1180ed28 --- /dev/null +++ b/tests/timezones_test.py @@ -0,0 +1,127 @@ +"""Component tests for the time-zone table, ``dataretrieval.codes.timezones``. + +The table turns the abbreviation a service writes beside a local time into a UTC +offset, so an entry is right when it agrees with the code list that service +draws from: the WQX TimeZone domain (Water Quality Portal) and the USGS NWIS +time-zone list (Samples). Snapshots of both are in ``tests/data``; the offline +tests check the table against them, and the live tests check the snapshots +against the published lists. +""" + +from __future__ import annotations + +import csv +import io +import xml.etree.ElementTree as ET +from pathlib import Path + +import httpx +import pytest + +from dataretrieval import waterdata, wqp +from dataretrieval.codes import tz + +DATA = Path(__file__).parent / "data" +USGS_URL = "https://water.usgs.gov/XML/NWIS/5.0/ReferenceLists/TimeZoneList.xml" +WQX_URL = "https://cdx.epa.gov/wqx/download/DomainValues/TimeZone.CSV" + +# Codes the two lists define differently. The table follows WQX for these: the +# Water Quality Portal returns ``SST`` for American Samoa, while USGS records +# (in Samples, and passed through by WQP) write that zone as ``ZP-11``. +WQX_WINS = {"SST", "BST"} + + +def _usgs_offset(text: str) -> str: + # "-11:00" -> "-1100"; the list writes zero offsets as " 00:00". + text = text.strip().replace(":", "") + return text if text[0] in "+-" else "+" + text + + +def parse_usgs(content: bytes) -> dict[str, str]: + """Map each standard and daylight code in the USGS list to its offset.""" + codes: dict[str, str] = {} + for item in ET.fromstring(content).iter("ListItem"): + codes[item.findtext("Code", "")] = _usgs_offset(item.findtext("UTCOffset", "")) + dst = item.findtext("DSTCode", "null") + if dst != "null": + codes[dst] = _usgs_offset(item.findtext("DSTUTCOffset", "")) + return codes + + +def parse_wqx(content: bytes) -> dict[str, str]: + """Map each code in the WQX TimeZone domain to its offset.""" + codes: dict[str, str] = {} + for row in csv.DictReader(io.StringIO(content.decode("utf-8"))): + hours = float(row["Offset"]) + sign = "-" if hours < 0 else "+" + minutes = round(abs(hours) * 60) + codes[row["Code"]] = f"{sign}{minutes // 60:02d}{minutes % 60:02d}" + return codes + + +USGS = parse_usgs((DATA / "usgs_timezones.xml").read_bytes()) +WQX = parse_wqx((DATA / "wqx_timezones.csv").read_bytes()) + + +@pytest.mark.parametrize("code", sorted(WQX)) +def test_table_matches_the_wqx_domain(code): + assert tz.get(code) == WQX[code] + + +@pytest.mark.parametrize("code", sorted(set(USGS) - WQX_WINS)) +def test_table_matches_the_usgs_list(code): + assert tz.get(code) == USGS[code] + + +def test_the_lists_disagree_only_where_the_table_says_so(): + """``WQX_WINS`` names every conflict, so a new one has to be decided.""" + conflicts = {c for c in USGS.keys() & WQX.keys() if USGS[c] != WQX[c]} + assert conflicts == WQX_WINS + + +# --- live upstream monitors -------------------------------------------------- + + +@pytest.mark.live +@pytest.mark.parametrize( + ("url", "parse", "snapshot"), + [(USGS_URL, parse_usgs, USGS), (WQX_URL, parse_wqx, WQX)], + ids=["usgs", "wqx"], +) +def test_published_list_matches_the_snapshot(url, parse, snapshot): + """If this fails, the published list changed: refresh its snapshot in + ``tests/data`` from ``url`` and reconcile the table with the new entries.""" + resp = httpx.get(url, timeout=60, follow_redirects=True) + resp.raise_for_status() + assert parse(resp.content) == snapshot + + +def _samples(location): + df, _ = waterdata.get_samples( + service="results", profile="fullphyschem", monitoring_location_id=location + ) + return df + + +def _wqp(location): + df, _ = wqp.get_results(siteid=location, legacy=False, dataProfile="fullPhysChem") + return df + + +@pytest.mark.live +@pytest.mark.parametrize( + ("fetch", "location", "zone"), + [ + (_samples, "USGS-141037169391201", "ZP-11"), # American Samoa + (_samples, "USGS-16854500", "GST"), # Guam + (_wqp, "11NPSWRD_WQX-NPSA_F_07TUT_FW", "SST"), # American Samoa + (_wqp, "USGS-141037169391201", "ZP-11"), # USGS code passed through + (_wqp, "USGS-16854500", "GST"), # Guam + ], +) +def test_pacific_times_resolve(fetch, location, zone): + """Every timed row for these Pacific monitoring locations has a UTC time.""" + df = fetch(location) + timed = df[df["Activity_StartTime"].notna()] + assert zone in set(timed["Activity_StartTimeZone"]) + assert timed["Activity_StartDateTime"].notna().all() diff --git a/tests/utils_test.py b/tests/utils_test.py index fc1745e4..4a089f5e 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -442,6 +442,8 @@ def test_legacy_wqp_triplet_resolves_to_utc(self): ("GST", "2024-06-01 00:00:00"), # Hawaii-Aleutian Daylight Time, UTC-9, from the USGS codes. ("HDT", "2024-06-01 19:00:00"), + # American Samoa as the Samples database writes it (USGS codes). + ("ZP-11", "2024-06-01 21:00:00"), ], ) def test_zone_resolves_as_the_services_define_it(self, zone, utc):