Skip to content

Commit

Permalink
Merge pull request #21 from jepler/document-data-sources
Browse files Browse the repository at this point in the history
Update & document data sources
  • Loading branch information
jepler authored Jul 18, 2024
2 parents 80de9bd + b29f68d commit 9817b72
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
run: make mypy

- name: Test
run: python -mcoverage run --branch -m unittest testleapseconddata.py && python -mcoverage report --fail-under=100 && python -mcoverage xml
run: python -X tracemalloc=3 -mcoverage run --branch -m unittest testleapseconddata.py && python -mcoverage report --fail-under=100 && python -mcoverage xml

pre-commit:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_env']


# -- Options for HTML output -------------------------------------------------
Expand Down
57 changes: 47 additions & 10 deletions leapseconddata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import re
import urllib.request
from dataclasses import dataclass, field
from typing import BinaryIO
from typing import BinaryIO, ClassVar

tai = datetime.timezone(datetime.timedelta(0), "TAI")

Expand Down Expand Up @@ -76,6 +76,49 @@ class LeapSecondData:
:param Optional[datetime.datetime] updated: The last update time of the data
"""

standard_file_sources: ClassVar[list[str]] = [
"file:///usr/share/zoneinfo/leap-seconds.list", # Debian Linux
"file:///var/db/ntpd.leap-seconds.list", # FreeBSD
]
"""When using `LeapSecondData.from_standard_source`, these local sources are checked first.
Locations for Debian Linux & FreeBSD are supported."""

standard_network_sources: ClassVar[list[str]] = [
"https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list",
"https://data.iana.org/time-zones/tzdb/leap-seconds.list",
"https://raw.githubusercontent.com/eggert/tz/main/leap-seconds.list",
"ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list",
"https://www.meinberg.de/download/ntp/leap-seconds.list",
]
"""When using `LeapSecondData.from_standard_source`, these network sources are checked second.
Remote sources are checked in the following order until a suitable file is found:
* The `International Earth Rotation Service (IERS)
<https://www.iers.org/IERS/EN/Home/home_node.html>`_ is the international
body charged with various duties including scheduling leap seconds.
* The `Internet Assigned Numbers Authority (IANA)
<https://www.iana.org/>`_ publishes the IANA timezone database, used by
many major operating sytsems for handling the world's time zones. As part
of this activity they publish a version of the leap second list.
* `eggert/tz <https://github.com/eggert/tz>`_ is the canonical github home
of the IANA timezone database, and updated versions of the leap second
list can appear here before they are part of an official IANA timezone
database release.
* `The National Institute of Standards and Technology (NIST)'s Time
Realization and Distribution Group
<https://www.nist.gov/pml/time-and-frequency-division/time-distribution/internet-time-service-its>`_
is a US federal organization that publishes a version of the leap second
database.
* `Meinberg Funkuhren GmbH & Co. KG
<https://www.meinbergglobal.com/english/company/>`_ is a Germany-based
company that published a `helpful article in its knowledge base
<https://kb.meinbergglobal.com/kb/time_sync/ntp/configuration/ntp_leap_second_file>`_
including URLs of sites that disseminate the leap second list. They state
that the version they distribute is frequently more up to date than other
sources, including IANA, NIST, and tzdb."""

leap_seconds: list[LeapSecondInfo]
"""All known and scheduled leap seconds"""

Expand Down Expand Up @@ -204,12 +247,7 @@ def from_standard_source(
leap-second.list data valid for the given timestamp, or the current
time (if unspecified)
"""
for location in [ # pragma no branch
"file:///usr/share/zoneinfo/leap-seconds.list", # Debian Linux
"file:///var/db/ntpd.leap-seconds.list", # FreeBSD
"https://raw.githubusercontent.com/eggert/tz/main/leap-seconds.list",
"https://www.meinberg.de/download/ntp/leap-seconds.list",
]:
for location in cls.standard_file_sources + cls.standard_network_sources:
logging.debug("Trying leap second data from %s", location)
try:
candidate = cls.from_url(location, check_hash=check_hash)
Expand Down Expand Up @@ -244,14 +282,13 @@ def from_file(
@classmethod
def from_url(
cls,
url: str = "https://raw.githubusercontent.com/eggert/tz/main/leap-seconds.list",
url: str,
*,
check_hash: bool = True,
) -> LeapSecondData | None:
"""Retrieve the leap second list from a local file
:param filename: URL to read leap second data from. The
default is maintained by the tzdata authors
:param filename: URL to read leap second data from
:param check_hash: Whether to check the embedded hash
"""
try:
Expand Down
26 changes: 25 additions & 1 deletion leapseconddata/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import click

from . import LeapSecondData, tai
from . import InvalidHashError, LeapSecondData, tai

utc = datetime.timezone.utc

Expand Down Expand Up @@ -165,5 +165,29 @@ def table(ctx: click.Context, *, start: datetime.datetime, end: datetime.datetim
print(f"{leap_second.start:%Y-%m-%d}: {leap_second.tai_offset.total_seconds():.0f}")


@cli.command
def sources() -> None:
"""Print information about leap-second.list data sources"""
first = True
for location in LeapSecondData.standard_file_sources + LeapSecondData.standard_network_sources:
if not first:
print()
first = False
try:
leap_second_data = LeapSecondData.from_url(location, check_hash=True)
except InvalidHashError: # pragma no coverage
print(f"{location}: Invalid hash")
leap_second_data = LeapSecondData.from_url(location, check_hash=False)
except Exception as e: # pragma no coverage # noqa: BLE001
print(f"{location}: {e}")
leap_second_data = None

if leap_second_data is not None:
print(f"{location}: Last updated {leap_second_data.last_updated}")
print(f"{location}: Valid until {leap_second_data.valid_until}")
else:
print(f"{location}: Could not be read")


if __name__ == "__main__": # pragma no cover
cli()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ write_to = "leapseconddata/__version__.py"
line-length=120
[tool.ruff.lint]
select = ["E", "F", "D", "I", "N", "UP", "YTT", "BLE", "B", "FBT", "A", "COM", "C4", "DTZ", "FA", "ISC", "ICN", "PIE", "PYI", "Q", "RET", "SIM", "TID", "TCH", "ARG", "PTH", "C", "R", "W", "FLY", "RUF", "PL"]
ignore = ["D203", "D213", "D400", "D415", "ISC001"]
ignore = ["D203", "D213", "D400", "D415", "ISC001", "COM812"]
[project]
name = "leapseconddata"
authors = [{name = "Jeff Epler", email = "jepler@gmail.com"}]
Expand Down
1 change: 1 addition & 0 deletions testleapseconddata.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def test_main(self) -> None:
self.run_main("next-leapsecond", "2100-2-2")
self.run_main("previous-leapsecond", "2009-2-2")
self.run_main("previous-leapsecond", "1960-2-2")
self.run_main("sources")

def test_corrupt(self) -> None:
self.assertRaises(
Expand Down

0 comments on commit 9817b72

Please sign in to comment.