Add more complete documentation
This commit is contained in:
parent
2fad09fac6
commit
f982c49db7
24 changed files with 460 additions and 10 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -12,3 +12,5 @@ __pycache__
|
||||||
*,cover
|
*,cover
|
||||||
/.reuse
|
/.reuse
|
||||||
__version__.py
|
__version__.py
|
||||||
|
/docs/_build
|
||||||
|
/.idea
|
||||||
|
|
|
||||||
42
docs/_ext/custom_autodoc.py
Normal file
42
docs/_ext/custom_autodoc.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
"""Sphinx extension to remove the first line from module docstrings."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
|
def remove_first_line_in_module_docstring(
|
||||||
|
_app: Sphinx,
|
||||||
|
what: str,
|
||||||
|
_name: str,
|
||||||
|
_obj: Any,
|
||||||
|
_options: Any,
|
||||||
|
lines: list[str],
|
||||||
|
) -> None:
|
||||||
|
"""Remove the first line from the docstring.
|
||||||
|
|
||||||
|
This is because the first line of the docstring is summed up in the
|
||||||
|
document title, before the module autodoc.
|
||||||
|
"""
|
||||||
|
if what != "module" or not lines:
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in range(1, len(lines)):
|
||||||
|
if not lines[i]:
|
||||||
|
lines[: i + 1] = []
|
||||||
|
return
|
||||||
|
|
||||||
|
lines[:] = []
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app: Sphinx) -> None:
|
||||||
|
"""Set up the extension."""
|
||||||
|
app.connect(
|
||||||
|
"autodoc-process-docstring",
|
||||||
|
remove_first_line_in_module_docstring,
|
||||||
|
)
|
||||||
13
docs/code.rst
Normal file
13
docs/code.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
Code reference
|
||||||
|
==============
|
||||||
|
|
||||||
|
If you are looking for information on a specific function, class or method,
|
||||||
|
this part of the documentation is for you.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
code/leapseconddata
|
||||||
7
docs/code/leapseconddata.rst
Normal file
7
docs/code/leapseconddata.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
``leapseconddata`` -- main namespace for the project
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
.. automodule:: leapseconddata
|
||||||
25
docs/conf.py
25
docs/conf.py
|
|
@ -17,6 +17,7 @@ import sys
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
|
sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
|
||||||
|
sys.path.append(str(pathlib.Path(__file__).parent / "_ext"))
|
||||||
|
|
||||||
# Define the canonical URL if you are using a custom domain on Read the Docs
|
# Define the canonical URL if you are using a custom domain on Read the Docs
|
||||||
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
|
html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
|
||||||
|
|
@ -29,7 +30,7 @@ if os.environ.get("READTHEDOCS", "") == "True":
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'leapseconddata'
|
project = 'leapseconddata'
|
||||||
copyright = '2021, Jeff Epler'
|
copyright = '2021-2024, Jeff Epler'
|
||||||
author = 'Jeff Epler'
|
author = 'Jeff Epler'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
|
|
@ -43,6 +44,9 @@ release = '1.1.0'
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.todo',
|
||||||
|
'custom_autodoc',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
|
@ -51,7 +55,10 @@ templates_path = ['_templates']
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This pattern also affects html_static_path and html_extra_path.
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_env']
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_env', '.license']
|
||||||
|
|
||||||
|
# Show the contents of todo directives.
|
||||||
|
todo_include_todos = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
@ -66,8 +73,22 @@ html_theme = 'sphinx_rtd_theme'
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
intersphinx_mapping = {
|
||||||
|
"python": ("https://docs.python.org/3", None),
|
||||||
|
}
|
||||||
|
|
||||||
autodoc_typehints = "description"
|
autodoc_typehints = "description"
|
||||||
|
autodoc_typehints_format = "short"
|
||||||
autodoc_class_signature = "separated"
|
autodoc_class_signature = "separated"
|
||||||
|
autodoc_default_options = {
|
||||||
|
"members": True,
|
||||||
|
"undoc-members": True,
|
||||||
|
"inherited-members": False,
|
||||||
|
"special-members": False,
|
||||||
|
"exclude-members": "__init__",
|
||||||
|
"show-inheritance": True,
|
||||||
|
}
|
||||||
|
autodoc_member_order = "bysource"
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
||||||
#
|
#
|
||||||
|
|
|
||||||
16
docs/developer-guides.rst
Normal file
16
docs/developer-guides.rst
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
Developer guides
|
||||||
|
================
|
||||||
|
|
||||||
|
This section consists of multiple guides for solving specific problems,
|
||||||
|
targeted towards developers using the component.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
developer-guides/obtaining-leap-seconds
|
||||||
|
developer-guides/converting-tai-to-utc
|
||||||
|
developer-guides/converting-utc-to-tai
|
||||||
|
developer-guides/checking-if-date-is-leap.rst
|
||||||
14
docs/developer-guides/check-date-leap.py
Normal file
14
docs/developer-guides/check-date-leap.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
my_date = date(2015, 12, 31)
|
||||||
|
data = LeapSecondData.from_standard_source()
|
||||||
|
|
||||||
|
for leap in data.leap_seconds:
|
||||||
|
time = leap.start - timedelta(seconds=1)
|
||||||
|
if my_date.year == time.year and my_date.month == time.month and my_date.day == time.day:
|
||||||
|
print(f"{my_date} has a leap second!")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"{my_date} does not have a leap second.")
|
||||||
3
docs/developer-guides/check-date-leap.py.license
Normal file
3
docs/developer-guides/check-date-leap.py.license
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
# Placed in a separate file so that it does not appear in the produced docs.
|
||||||
21
docs/developer-guides/checking-if-date-is-leap.rst
Normal file
21
docs/developer-guides/checking-if-date-is-leap.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
Checking if a date has a leap second
|
||||||
|
====================================
|
||||||
|
|
||||||
|
In order to check if a date has a leap second, you must first
|
||||||
|
obtain the leap second data by using one of the methods described
|
||||||
|
in :ref:`devguide-obtaining-leaps`. Then, you can iterate over
|
||||||
|
the fetched leap seconds to check for the date.
|
||||||
|
|
||||||
|
For example, in order to check if December 31st, 2016 has a leap
|
||||||
|
second, you can use the following code:
|
||||||
|
|
||||||
|
.. literalinclude:: check-date-leap.py
|
||||||
|
|
||||||
|
The output of this program is the following:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
2016-12-31 has a leap second!
|
||||||
9
docs/developer-guides/convert-tai-to-utc.py
Normal file
9
docs/developer-guides/convert-tai-to-utc.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData, tai
|
||||||
|
|
||||||
|
my_date = datetime(2024, 7, 18, 22, 0, 37, tzinfo=tai)
|
||||||
|
data = LeapSecondData.from_standard_source()
|
||||||
|
|
||||||
|
my_tai_date = data.tai_to_utc(my_date)
|
||||||
|
print(my_tai_date.isoformat())
|
||||||
3
docs/developer-guides/convert-tai-to-utc.py.license
Normal file
3
docs/developer-guides/convert-tai-to-utc.py.license
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
# Placed in a separate file so that it does not appear in the produced docs.
|
||||||
9
docs/developer-guides/convert-utc-to-tai.py
Normal file
9
docs/developer-guides/convert-utc-to-tai.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
my_date = datetime(2024, 7, 18, 22, 0, 0, tzinfo=UTC)
|
||||||
|
data = LeapSecondData.from_standard_source()
|
||||||
|
|
||||||
|
my_tai_date = data.to_tai(my_date)
|
||||||
|
print(my_tai_date.isoformat())
|
||||||
3
docs/developer-guides/convert-utc-to-tai.py.license
Normal file
3
docs/developer-guides/convert-utc-to-tai.py.license
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
# Placed in a separate file so that it does not appear in the produced docs.
|
||||||
22
docs/developer-guides/converting-tai-to-utc.rst
Normal file
22
docs/developer-guides/converting-tai-to-utc.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
Converting a TAI date and time to UTC
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. py:currentmodule:: leapseconddata
|
||||||
|
|
||||||
|
In order to convert a TAI date and time to UTC, you must first
|
||||||
|
obtain the leap second data by using one of the methods described
|
||||||
|
in :ref:`devguide-obtaining-leaps`. Then, you can use the
|
||||||
|
:py:meth:`LeapSecondData.tai_to_utc` method to convert the date and time.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. literalinclude:: convert-tai-to-utc.py
|
||||||
|
|
||||||
|
This program will provide you with the following output:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
2024-07-18T22:00:00g+00:00
|
||||||
22
docs/developer-guides/converting-utc-to-tai.rst
Normal file
22
docs/developer-guides/converting-utc-to-tai.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
Converting a UTC date and time to TAI
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. py:currentmodule:: leapseconddata
|
||||||
|
|
||||||
|
In order to convert a UTC date and time to TAI, you must first
|
||||||
|
obtain the leap second data by using one of the methods described
|
||||||
|
in :ref:`devguide-obtaining-leaps`. Then, you can use the
|
||||||
|
:py:meth:`LeapSecondData.to_tai` method to convert the date and time.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. literalinclude:: convert-utc-to-tai.py
|
||||||
|
|
||||||
|
This program will provide you with the following output:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
2024-07-18T22:00:37+00:00
|
||||||
64
docs/developer-guides/obtaining-leap-seconds.rst
Normal file
64
docs/developer-guides/obtaining-leap-seconds.rst
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
.. _devguide-obtaining-leaps:
|
||||||
|
|
||||||
|
Obtaining a list of leap seconds
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. py:currentmodule:: leapseconddata
|
||||||
|
|
||||||
|
In order to obtain the current leap second list, you must use one of
|
||||||
|
:py:class:`LeapSecondData` ``from_*`` class methods.
|
||||||
|
|
||||||
|
Using the first available standard source
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
If you do not have any particular restrictions on your Internet access,
|
||||||
|
you can try the "magic" method :py:meth:`LeapSecondData.from_standard_source`,
|
||||||
|
which will try known local then network sources:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
data = LeapSecondData.from_standard_source()
|
||||||
|
...
|
||||||
|
|
||||||
|
Using a custom file source
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
If you have a custom path for the ``leap-seconds.list`` the module can use,
|
||||||
|
you can use the :py:meth:`LeapSecondData.from_file` method. For example,
|
||||||
|
if your file is located at ``/etc/my-program/leap-seconds.list``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
data = LeapSecondData.from_file("/etc/my-program/leap-seconds.list")
|
||||||
|
|
||||||
|
Using a custom URL
|
||||||
|
------------------
|
||||||
|
|
||||||
|
If you have restrictions on your Internet access and can only access the
|
||||||
|
file from a specific URL available to your machine, you can use
|
||||||
|
:py:meth:`LeapSecondData.from_url`:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
data = LeapSecondData.from_url("https://tz.example/leap-seconds.list")
|
||||||
|
|
||||||
|
You can also still try local sources before your custom URL, by using
|
||||||
|
:py:meth:`LeapSecondData.from_standard_source` with the ``custom_sources``
|
||||||
|
keyword parameter set:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from leapseconddata import LeapSecondData
|
||||||
|
|
||||||
|
data = LeapSecondData.from_standard_source(
|
||||||
|
custom_sources=["https://tz.example/leap-seconds.list"],
|
||||||
|
)
|
||||||
12
docs/guides.rst
Normal file
12
docs/guides.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
General guides
|
||||||
|
==============
|
||||||
|
|
||||||
|
This section consists of multiple guides for solving specific problems.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
guides/install
|
||||||
17
docs/guides/install.rst
Normal file
17
docs/guides/install.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
.. _guide-install:
|
||||||
|
|
||||||
|
Installing leapseconddata
|
||||||
|
=========================
|
||||||
|
|
||||||
|
In order to install leapseconddata, the instructions may depend on the system
|
||||||
|
you want to install it on.
|
||||||
|
|
||||||
|
pip (generic)
|
||||||
|
-------------
|
||||||
|
|
||||||
|
leapseconddata can be installed via ``pip``::
|
||||||
|
|
||||||
|
pip install leapseconddata
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
.. SPDX-FileCopyrightText: 2021 Jeff Epler
|
.. SPDX-FileCopyrightText: 2021 Jeff Epler
|
||||||
..
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
.. SPDX-License-Identifier: GPL-3.0-only
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
leapseconddata
|
leapseconddata |version|
|
||||||
==============
|
========================
|
||||||
|
|
||||||
.. image:: https://github.com/jepler/leapseconddata/actions/workflows/test.yml/badge.svg
|
.. image:: https://github.com/jepler/leapseconddata/actions/workflows/test.yml/badge.svg
|
||||||
:target: https://github.com/jepler/leapseconddata/actions/workflows/test.yml
|
:target: https://github.com/jepler/leapseconddata/actions/workflows/test.yml
|
||||||
|
|
@ -13,10 +13,54 @@ leapseconddata
|
||||||
:target: https://pypi.org/project/leapseconddata
|
:target: https://pypi.org/project/leapseconddata
|
||||||
:alt: PyPI
|
:alt: PyPI
|
||||||
|
|
||||||
|
This module allows you to download and extract leap second data from
|
||||||
|
various trusted sources, both offline and online, in order to:
|
||||||
|
|
||||||
|
* Convert dates and times between TAI and UTC;
|
||||||
|
* Determine if a date has an extra second at the end in UTC.
|
||||||
|
|
||||||
|
You can also find the project in the following locations:
|
||||||
|
|
||||||
|
* `jelper/leapseconddata repository on Github
|
||||||
|
<https://github.com/jepler/leapseconddata>`_;
|
||||||
|
* `leapseconddata project on PyPI
|
||||||
|
<https://pypi.org/project/leapseconddata/>`_.
|
||||||
|
|
||||||
|
The project’s code and documentation contents are licensed under GNU General
|
||||||
|
Public License version 3.
|
||||||
|
|
||||||
|
How-to guides
|
||||||
|
-------------
|
||||||
|
|
||||||
|
These sections provide guides, i.e. recipes, targeted towards various actors.
|
||||||
|
They guide you through the steps involved in addressing key problems
|
||||||
|
and use-cases.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 3
|
||||||
:caption: Contents:
|
|
||||||
|
|
||||||
.. automodule:: leapseconddata
|
guides
|
||||||
:members:
|
developer-guides
|
||||||
|
|
||||||
|
Discussion topics
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
These sections discuss key topics and concepts at a fairly high level, and
|
||||||
|
provide useful background information and explanation.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 3
|
||||||
|
|
||||||
|
topics
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
|
||||||
|
These sections provide technical reference for APIs and other aspects of
|
||||||
|
the project's machinery. They go into detail, and therefore, assume you
|
||||||
|
have a basic understanding of key concepts.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
code
|
||||||
|
|
|
||||||
13
docs/topics.rst
Normal file
13
docs/topics.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
General topics
|
||||||
|
==============
|
||||||
|
|
||||||
|
These topics explore general concepts behind the project.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
topics/leap-seconds
|
||||||
|
topics/leap-second-distribution
|
||||||
50
docs/topics/leap-second-distribution.rst
Normal file
50
docs/topics/leap-second-distribution.rst
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
How is leap second data distributed?
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Leap seconds are announced **every six months** by the IERS in the
|
||||||
|
`Bulletin C publications`_:
|
||||||
|
|
||||||
|
* On beginning of January if they are introduced on June 30th (UTC);
|
||||||
|
* On beginning of July if they are introduced in December 31st (UTC).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
* `Bulletin C 52`_, published on July 6th, 2016, announces a leap
|
||||||
|
second will be introduced on December 31st, 2016 (UTC).
|
||||||
|
* `Bulletin C 67`_, published on July 4th, 2024, announces **no**
|
||||||
|
leap second will be introduced on June 30th, 2024 (UTC).
|
||||||
|
|
||||||
|
The IERS also distributes a file named ``leap-seconds.list``, at the
|
||||||
|
following URL::
|
||||||
|
|
||||||
|
https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
|
||||||
|
|
||||||
|
From here, there are multiple approaches to how systems can receive
|
||||||
|
the information, in order to display the time correctly:
|
||||||
|
|
||||||
|
* NTP supports informing clients of minutes with a leap second.
|
||||||
|
See `The NTP Timescale and Leap Seconds`_ for more information;
|
||||||
|
* Debian and derivatives distribute the file provided by the IERS, as
|
||||||
|
well as some commodities, through the tzdata_ package.
|
||||||
|
This file is available at ``/usr/share/zoneinfo/leap-seconds.list``;
|
||||||
|
* FreeBSD's ntpd has a ntpleapfetch_ command that fetches ``leap-seconds.list``
|
||||||
|
file, and stores it in ``/var/db/ntpd.leap-seconds.list``.
|
||||||
|
* Programs can fetch the file directly from network sources, if the network
|
||||||
|
is not restricted.
|
||||||
|
|
||||||
|
If using :py:meth:`LeapSecondData.from_standard_source`, ``leapseconddata``
|
||||||
|
will use local sources if available, and official network sources if
|
||||||
|
not found.
|
||||||
|
|
||||||
|
.. _Bulletin C publications:
|
||||||
|
https://datacenter.iers.org/availableVersions.php?id=16
|
||||||
|
.. _Bulletin C 52:
|
||||||
|
https://datacenter.iers.org/data/16/bulletinc-052.txt
|
||||||
|
.. _Bulletin C 67:
|
||||||
|
https://datacenter.iers.org/data/16/bulletinc-067.txt
|
||||||
|
.. _The NTP Timescale and Leap Seconds: https://www.ntp.org/reflib/leap/
|
||||||
|
.. _tzdata: https://salsa.debian.org/glibc-team/tzdata
|
||||||
|
.. _ntpleapfetch: https://docs.ntpsec.org/latest/ntpleapfetch.html
|
||||||
43
docs/topics/leap-seconds.rst
Normal file
43
docs/topics/leap-seconds.rst
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
.. SPDX-FileCopyrightText: 2024 Thomas Touhey
|
||||||
|
.. SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
What are leap seconds?
|
||||||
|
======================
|
||||||
|
|
||||||
|
`Coordinated Universal Time (UTC) <UTC_>`_ is a time standard based on two
|
||||||
|
other standards, `International Atomic Time (TAI) <TAI_>`_ and
|
||||||
|
`Universal Time (UT1) <UT1_>`_. It aims at being at a whole second offset
|
||||||
|
from TAI, while keeping UTC and UT1 within 0.9 seconds of each other.
|
||||||
|
|
||||||
|
In order to accomplish that, UTC bases itself on TAI, and gets `leap seconds`_
|
||||||
|
added to it when considered necessary by the `International Earth Rotation
|
||||||
|
Service (IERS) <IERS_>`_, in a semi-annually published bulletin called
|
||||||
|
`Bulletin C`_ which announces whether or not a leap second is inserted
|
||||||
|
in June 30th and/or December 31st, meaning the UTC clock may reach ``23:59:60``
|
||||||
|
on these dates.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
With timezones, the leap second may not be inserted at ``23:59``, but
|
||||||
|
at another time. For example:
|
||||||
|
|
||||||
|
* In France, using Central European Time (CET, UTC+01:00), the leap second
|
||||||
|
was inserted on January 1st, 2017, at ``00:59:60``.
|
||||||
|
* In Australia, using Australian Western Central Standard Time (AWCST,
|
||||||
|
UTC+08:45), the leap second was inserted on January 1st, 2017,
|
||||||
|
at ``08:44:60``.
|
||||||
|
* In the United States, using Mountain Time Zone (UTC-07:00), the leap
|
||||||
|
second was inserted on December 31st, 2016, at ``16:59:60``.
|
||||||
|
|
||||||
|
For more information, you can read `The Unix leap second mess (madore.org)
|
||||||
|
<http://www.madore.org/%7Edavid/computers/unix-leap-seconds.html>`_, as
|
||||||
|
well as the Wikipedia pages linked above.
|
||||||
|
|
||||||
|
.. _UTC: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
|
||||||
|
.. _TAI: https://en.wikipedia.org/wiki/International_Atomic_Time
|
||||||
|
.. _UT1: https://en.wikipedia.org/wiki/Universal_Time
|
||||||
|
.. _leap seconds: https://en.wikipedia.org/wiki/Leap_second
|
||||||
|
.. _IERS:
|
||||||
|
https://en.wikipedia.org/wiki/
|
||||||
|
International_Earth_Rotation_and_Reference_Systems_Service
|
||||||
|
.. _Bulletin C: https://datacenter.iers.org/productMetadata.php?id=16
|
||||||
|
|
@ -17,7 +17,6 @@ For example, to retrieve the UTC-TAI offset on January 1, 2011:
|
||||||
>>> when = datetime.datetime(2011, 1, 1, tzinfo=datetime.timezone.utc)
|
>>> when = datetime.datetime(2011, 1, 1, tzinfo=datetime.timezone.utc)
|
||||||
>>> ls.tai_offset(when).total_seconds()
|
>>> ls.tai_offset(when).total_seconds()
|
||||||
34.0
|
34.0
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ line-length=120
|
||||||
[tool.ruff.lint]
|
[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"]
|
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", "COM812"]
|
ignore = ["D203", "D213", "D400", "D415", "ISC001", "COM812"]
|
||||||
|
exclude = ["docs/**/*.py"]
|
||||||
[project]
|
[project]
|
||||||
name = "leapseconddata"
|
name = "leapseconddata"
|
||||||
authors = [{name = "Jeff Epler", email = "jepler@gmail.com"}]
|
authors = [{name = "Jeff Epler", email = "jepler@gmail.com"}]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue