wwvb 3.0.8__py3-none-any.whl → 4.0.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wwvb/__version__.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.0.8'
16
- __version_tuple__ = version_tuple = (3, 0, 8)
15
+ __version__ = version = '4.0.0a0'
16
+ __version_tuple__ = version_tuple = (4, 0, 0)
wwvb/decode.py CHANGED
@@ -3,8 +3,10 @@
3
3
  # SPDX-License-Identifier: GPL-3.0-only
4
4
  """A stateful decoder of WWVB signals"""
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  import sys
7
- from typing import Generator, List, Optional
9
+ from typing import Generator
8
10
 
9
11
  import wwvb
10
12
 
@@ -20,12 +22,12 @@ import wwvb
20
22
  # State 4: Decoding a minute, starting in second 1
21
23
  # Second
22
24
 
23
- always_zero = set((4, 10, 11, 14, 20, 21, 34, 35, 44, 54))
25
+ always_zero = {4, 10, 11, 14, 20, 21, 34, 35, 44, 54}
24
26
 
25
27
 
26
- def wwvbreceive() -> Generator[Optional[wwvb.WWVBTimecode], wwvb.AmplitudeModulation, None]:
27
- """A stateful decoder of WWVB signals"""
28
- minute: List[wwvb.AmplitudeModulation] = []
28
+ def wwvbreceive() -> Generator[wwvb.WWVBTimecode | None, wwvb.AmplitudeModulation, None]:
29
+ """Decode WWVB signals statefully."""
30
+ minute: list[wwvb.AmplitudeModulation] = []
29
31
  state = 1
30
32
 
31
33
  value = yield None
@@ -38,10 +40,7 @@ def wwvbreceive() -> Generator[Optional[wwvb.WWVBTimecode], wwvb.AmplitudeModula
38
40
  value = yield None
39
41
 
40
42
  elif state == 2:
41
- if value == wwvb.AmplitudeModulation.MARK:
42
- state = 3
43
- else:
44
- state = 1
43
+ state = 3 if value == wwvb.AmplitudeModulation.MARK else 1
45
44
  value = yield None
46
45
 
47
46
  elif state == 3:
wwvb/gen.py CHANGED
@@ -6,9 +6,11 @@
6
6
  #
7
7
  # SPDX-License-Identifier: GPL-3.0-only
8
8
 
9
+ from __future__ import annotations
10
+
9
11
  import datetime
10
12
  import sys
11
- from typing import Any, List, Type
13
+ from typing import Any
12
14
 
13
15
  import click
14
16
  import dateutil.parser
@@ -16,15 +18,17 @@ import dateutil.parser
16
18
  from . import WWVBMinute, WWVBMinuteIERS, print_timecodes, print_timecodes_json, styles
17
19
 
18
20
 
19
- def parse_timespec(ctx: Any, param: Any, value: List[str]) -> datetime.datetime:
21
+ def parse_timespec(ctx: Any, param: Any, value: list[str]) -> datetime.datetime: # noqa: ARG001
20
22
  """Parse a time specifier from the commandline"""
21
23
  try:
22
24
  if len(value) == 5:
23
25
  year, month, day, hour, minute = map(int, value)
24
- return datetime.datetime(year, month, day, hour, minute)
26
+ return datetime.datetime(year, month, day, hour, minute, tzinfo=datetime.timezone.utc)
25
27
  if len(value) == 4:
26
28
  year, yday, hour, minute = map(int, value)
27
- return datetime.datetime(year, 1, 1, hour, minute) + datetime.timedelta(days=yday - 1)
29
+ return datetime.datetime(year, 1, 1, hour, minute, tzinfo=datetime.timezone.utc) + datetime.timedelta(
30
+ days=yday - 1,
31
+ )
28
32
  if len(value) == 1:
29
33
  return dateutil.parser.parse(value[0])
30
34
  if len(value) == 0:
@@ -68,7 +72,7 @@ def parse_timespec(ctx: Any, param: Any, value: List[str]) -> datetime.datetime:
68
72
  @click.option(
69
73
  "--style",
70
74
  default="default",
71
- type=click.Choice(sorted(["json"] + list(styles.keys()))),
75
+ type=click.Choice(sorted(["json", *list(styles.keys())])),
72
76
  help="Style of output",
73
77
  )
74
78
  @click.option(
@@ -86,6 +90,7 @@ def parse_timespec(ctx: Any, param: Any, value: List[str]) -> datetime.datetime:
86
90
  )
87
91
  @click.argument("timespec", type=str, nargs=-1, callback=parse_timespec)
88
92
  def main(
93
+ *,
89
94
  iers: bool,
90
95
  leap_second: bool,
91
96
  dut1: int,
@@ -99,7 +104,6 @@ def main(
99
104
 
100
105
  TIMESPEC: one of "year yday hour minute" or "year month day hour minute", or else the current minute
101
106
  """
102
-
103
107
  if (leap_second is not None) or (dut1 is not None):
104
108
  iers = False
105
109
 
@@ -107,16 +111,13 @@ def main(
107
111
  newls = None
108
112
 
109
113
  if iers:
110
- Constructor: Type[WWVBMinute] = WWVBMinuteIERS
114
+ constructor: type[WWVBMinute] = WWVBMinuteIERS
111
115
  else:
112
- Constructor = WWVBMinute
113
- if dut1 is None:
114
- newut1 = -500 * (leap_second or 0)
115
- else:
116
- newut1 = dut1
116
+ constructor = WWVBMinute
117
+ newut1 = -500 * (leap_second or 0) if dut1 is None else dut1
117
118
  newls = bool(leap_second)
118
119
 
119
- w = Constructor.from_datetime(timespec, newls=newls, newut1=newut1)
120
+ w = constructor.from_datetime(timespec, newls=newls, newut1=newut1)
120
121
  if style == "json":
121
122
  print_timecodes_json(w, minutes, channel, file=sys.stdout)
122
123
  else:
wwvb/iersdata.py CHANGED
@@ -7,7 +7,7 @@
7
7
  # SPDX-License-Identifier: GPL-3.0-only
8
8
 
9
9
  import datetime
10
- import os
10
+ import pathlib
11
11
 
12
12
  import platformdirs
13
13
 
@@ -18,10 +18,9 @@ for location in [
18
18
  platformdirs.user_data_dir("wwvbpy", "unpythonic.net"),
19
19
  platformdirs.site_data_dir("wwvbpy", "unpythonic.net"),
20
20
  ]: # pragma no cover
21
- filename = os.path.join(location, "wwvbpy_iersdata.py")
22
- if os.path.exists(filename):
23
- with open(filename, encoding="utf-8") as f:
24
- exec(f.read(), globals(), globals())
21
+ path = pathlib.Path(location) / "wwvbpy_iersdata.py"
22
+ if path.exists():
23
+ exec(path.read_text(encoding="utf-8"), globals(), globals())
25
24
  break
26
25
 
27
26
  start = datetime.datetime.combine(DUT1_DATA_START, datetime.time()).replace(tzinfo=datetime.timezone.utc)
wwvb/iersdata_dist.py CHANGED
@@ -5,9 +5,9 @@
5
5
  # SPDX-License-Identifier: CC0-1.0
6
6
  # isort: skip_file
7
7
  import datetime
8
- __all__ = ['DUT1_DATA_START', 'DUT1_OFFSETS']
8
+ __all__ = ["DUT1_DATA_START", "DUT1_OFFSETS"]
9
9
  DUT1_DATA_START = datetime.date(1972, 1, 1)
10
- d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s = tuple('defghijklmnopqrs')
10
+ d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s = tuple("defghijklmnopqrs")
11
11
  DUT1_OFFSETS = str( # 19720101
12
12
  i*182+s*123+k*30+i*31+s*19+r*31+q*29+p*28+o*30+n*36+m*40 # 19730909
13
13
  +l*39+k*33+j*31+i*31+h*18+r*19+q*38+p*32+o*31+n*33+m*48+l*45 # 19741010
@@ -34,5 +34,5 @@ DUT1_OFFSETS = str( # 19720101
34
34
  +i*126+h*176+g*97+f*91+e*52+o*116+n*98+m*70+l*133+k*91+j*91 # 20140507
35
35
  +i*77+h*140+g*91+f*84+e*70+d*34+n*72+m*76+l*66+k*53+j*56 # 20160831
36
36
  +i*105+h*77+g*45+q*25+p*63+o*91+n*154+m*105+l*190+k*118 # 20190501
37
- +j*105+i*807+j*376+k*775+l*67+k*2+l*6+k*133 # 20250405
37
+ +j*105+i*807+j*376+k*768+l*47+k*3+l*4+k*252, # 20250705
38
38
  )
wwvb/testcli.py CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/python3
2
2
  """Test most wwvblib commandline programs"""
3
3
 
4
+ # ruff: noqa: N802 D102
4
5
  # Copyright (C) 2011-2020 Jeff Epler <jepler@gmail.com>
5
6
  # SPDX-FileCopyrightText: 2021 Jeff Epler
6
7
  #
@@ -25,7 +26,7 @@ class CLITestCase(unittest.TestCase):
25
26
  return subprocess.check_output(args, stdin=subprocess.DEVNULL, encoding="utf-8", env=env)
26
27
 
27
28
  def moduleArgs(self, *args: str) -> Sequence[str]:
28
- return tuple((sys.executable, *coverage_add, "-m", *args))
29
+ return (sys.executable, *coverage_add, "-m", *args)
29
30
 
30
31
  def moduleOutput(self, *args: str) -> str:
31
32
  return self.programOutput(sys.executable, *coverage_add, "-m", *args)
@@ -64,7 +65,11 @@ class CLITestCase(unittest.TestCase):
64
65
  env["PYTHONIOENCODING"] = "utf-8"
65
66
  with self.assertRaises(subprocess.SubprocessError):
66
67
  subprocess.check_output(
67
- args, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, encoding="utf-8", env=env
68
+ args,
69
+ stdin=subprocess.DEVNULL,
70
+ stderr=subprocess.DEVNULL,
71
+ encoding="utf-8",
72
+ env=env,
68
73
  )
69
74
 
70
75
  def assertModuleError(self, *args: str) -> None:
@@ -72,7 +77,7 @@ class CLITestCase(unittest.TestCase):
72
77
  self.assertProgramError(*self.moduleArgs(*args))
73
78
 
74
79
  def test_gen(self) -> None:
75
- """test wwvb.gen"""
80
+ """Test wwvb.gen"""
76
81
  self.assertModuleOutput(
77
82
  """\
78
83
  WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0
wwvb/testpm.py CHANGED
@@ -15,9 +15,9 @@ class TestPhaseModulation(unittest.TestCase):
15
15
 
16
16
  def test_pm(self) -> None:
17
17
  """Compare the generated signal from a reference minute in NIST docs"""
18
- ref_am = "2011000002" "0001001112" "0001010002" "0110001012" "0100000012" "0010010112"
18
+ ref_am = "201100000200010011120001010002011000101201000000120010010112"
19
19
 
20
- ref_pm = "0011101101" "0001001000" "0011001000" "0110001101" "0011010001" "0110110110"
20
+ ref_pm = "001110110100010010000011001000011000110100110100010110110110"
21
21
 
22
22
  ref_minute = wwvb.WWVBMinuteIERS(2012, 186, 17, 30, dst=3)
23
23
  ref_time = ref_minute.as_timecode()
wwvb/testuwwvb.py CHANGED
@@ -4,6 +4,7 @@
4
4
  #
5
5
  # SPDX-License-Identifier: GPL-3.0-only
6
6
 
7
+ # ruff: noqa: N802 D102
7
8
  import datetime
8
9
  import random
9
10
  import sys
@@ -11,8 +12,9 @@ import unittest
11
12
  from typing import Union
12
13
 
13
14
  import adafruit_datetime
14
-
15
15
  import uwwvb
16
+ import zoneinfo
17
+
16
18
  import wwvb
17
19
 
18
20
  EitherDatetimeOrNone = Union[None, datetime.datetime, adafruit_datetime.datetime]
@@ -24,7 +26,8 @@ class WWVBRoundtrip(unittest.TestCase):
24
26
  def assertDateTimeEqualExceptTzInfo(self, a: EitherDatetimeOrNone, b: EitherDatetimeOrNone) -> None:
25
27
  """Test two datetime objects for equality
26
28
 
27
- This equality test excludes tzinfo, and allows adafruit_datetime and core datetime modules to compare equal"""
29
+ This equality test excludes tzinfo, and allows adafruit_datetime and core datetime modules to compare equal
30
+ """
28
31
  assert a
29
32
  assert b
30
33
  self.assertEqual(
@@ -34,8 +37,10 @@ class WWVBRoundtrip(unittest.TestCase):
34
37
 
35
38
  def test_decode(self) -> None:
36
39
  """Test decoding of some minutes including a leap second.
37
- Each minute must decode and match the primary decoder."""
38
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
40
+
41
+ Each minute must decode and match the primary decoder.
42
+ """
43
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
39
44
  assert minute
40
45
  decoder = uwwvb.WWVBDecoder()
41
46
  decoder.update(uwwvb.MARK)
@@ -57,7 +62,7 @@ class WWVBRoundtrip(unittest.TestCase):
57
62
 
58
63
  def test_roundtrip(self) -> None:
59
64
  """Test that some big range of times all decode the same as the primary decoder"""
60
- dt = datetime.datetime(2002, 1, 1, 0, 0)
65
+ dt = datetime.datetime(2002, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
61
66
  delta = datetime.timedelta(minutes=7182 if sys.implementation.name == "cpython" else 86400 - 7182)
62
67
  while dt.year < 2013:
63
68
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
@@ -70,19 +75,19 @@ class WWVBRoundtrip(unittest.TestCase):
70
75
  def test_dst(self) -> None:
71
76
  """Test of DST as handled by the small decoder"""
72
77
  for dt in (
73
- datetime.datetime(2021, 3, 14, 8, 59),
74
- datetime.datetime(2021, 3, 14, 9, 00),
75
- datetime.datetime(2021, 3, 14, 9, 1),
76
- datetime.datetime(2021, 3, 15, 8, 59),
77
- datetime.datetime(2021, 3, 15, 9, 00),
78
- datetime.datetime(2021, 3, 15, 9, 1),
79
- datetime.datetime(2021, 11, 7, 8, 59),
80
- datetime.datetime(2021, 11, 7, 9, 00),
81
- datetime.datetime(2021, 11, 7, 9, 1),
82
- datetime.datetime(2021, 11, 8, 8, 59),
83
- datetime.datetime(2021, 11, 8, 9, 00),
84
- datetime.datetime(2021, 11, 8, 9, 1),
85
- datetime.datetime(2021, 7, 7, 9, 1),
78
+ datetime.datetime(2021, 3, 14, 8, 59, tzinfo=datetime.timezone.utc),
79
+ datetime.datetime(2021, 3, 14, 9, 00, tzinfo=datetime.timezone.utc),
80
+ datetime.datetime(2021, 3, 14, 9, 1, tzinfo=datetime.timezone.utc),
81
+ datetime.datetime(2021, 3, 15, 8, 59, tzinfo=datetime.timezone.utc),
82
+ datetime.datetime(2021, 3, 15, 9, 00, tzinfo=datetime.timezone.utc),
83
+ datetime.datetime(2021, 3, 15, 9, 1, tzinfo=datetime.timezone.utc),
84
+ datetime.datetime(2021, 11, 7, 8, 59, tzinfo=datetime.timezone.utc),
85
+ datetime.datetime(2021, 11, 7, 9, 00, tzinfo=datetime.timezone.utc),
86
+ datetime.datetime(2021, 11, 7, 9, 1, tzinfo=datetime.timezone.utc),
87
+ datetime.datetime(2021, 11, 8, 8, 59, tzinfo=datetime.timezone.utc),
88
+ datetime.datetime(2021, 11, 8, 9, 00, tzinfo=datetime.timezone.utc),
89
+ datetime.datetime(2021, 11, 8, 9, 1, tzinfo=datetime.timezone.utc),
90
+ datetime.datetime(2021, 7, 7, 9, 1, tzinfo=datetime.timezone.utc),
86
91
  ):
87
92
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
88
93
  decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
@@ -98,7 +103,9 @@ class WWVBRoundtrip(unittest.TestCase):
98
103
 
99
104
  def test_noise(self) -> None:
100
105
  """Test of the state-machine decoder when faced with pseudorandom noise"""
101
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
106
+ minute = wwvb.WWVBMinuteIERS.from_datetime(
107
+ datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
108
+ )
102
109
  r = random.Random(408)
103
110
  junk = [
104
111
  r.choice(
@@ -106,12 +113,12 @@ class WWVBRoundtrip(unittest.TestCase):
106
113
  wwvb.AmplitudeModulation.MARK,
107
114
  wwvb.AmplitudeModulation.ONE,
108
115
  wwvb.AmplitudeModulation.ZERO,
109
- ]
116
+ ],
110
117
  )
111
118
  for _ in range(480)
112
119
  ]
113
120
  timecode = minute.as_timecode()
114
- test_input = junk + [wwvb.AmplitudeModulation.MARK] + timecode.am
121
+ test_input = [*junk, wwvb.AmplitudeModulation.MARK, *timecode.am]
115
122
  decoder = uwwvb.WWVBDecoder()
116
123
  for code in test_input[:-1]:
117
124
  decoded = decoder.update(code)
@@ -131,7 +138,9 @@ class WWVBRoundtrip(unittest.TestCase):
131
138
 
132
139
  def test_noise2(self) -> None:
133
140
  """Test of the full minute decoder with targeted errors to get full coverage"""
134
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
141
+ minute = wwvb.WWVBMinuteIERS.from_datetime(
142
+ datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
143
+ )
135
144
  timecode = minute.as_timecode()
136
145
  decoded = uwwvb.decode_wwvb([int(i) for i in timecode.am])
137
146
  self.assertIsNotNone(decoded)
@@ -166,7 +175,9 @@ class WWVBRoundtrip(unittest.TestCase):
166
175
 
167
176
  def test_noise3(self) -> None:
168
177
  """Test impossible BCD values"""
169
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
178
+ minute = wwvb.WWVBMinuteIERS.from_datetime(
179
+ datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
180
+ )
170
181
  timecode = minute.as_timecode()
171
182
 
172
183
  for poslist in [
@@ -191,15 +202,17 @@ class WWVBRoundtrip(unittest.TestCase):
191
202
  self.assertEqual(str(uwwvb.WWVBDecoder()), "<WWVBDecoder 1 []>")
192
203
 
193
204
  def test_near_year_bug(self) -> None:
194
- """Chris's WWVB software had a bug where the hours after UTC
195
- midnight on 12-31 of a leap year would be shown incorrectly. Check that we
196
- don't have that bug."""
197
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2021, 1, 1, 0, 0))
205
+ """Test for a bug seen in another WWVB implementaiton
206
+
207
+ .. in which the hours after UTC midnight on 12-31 of a leap year would
208
+ be shown incorrectly. Check that we don't have that bug.
209
+ """
210
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2021, 1, 1, 0, 0, tzinfo=datetime.timezone.utc))
198
211
  timecode = minute.as_timecode()
199
212
  decoded = uwwvb.decode_wwvb([int(i) for i in timecode.am])
200
213
  assert decoded
201
214
  self.assertDateTimeEqualExceptTzInfo(
202
- datetime.datetime(2020, 12, 31, 17, 00), # Mountain time!
215
+ datetime.datetime(2020, 12, 31, 17, 00, tzinfo=zoneinfo.ZoneInfo("America/Denver")), # Mountain time!
203
216
  uwwvb.as_datetime_local(decoded),
204
217
  )
205
218
 
wwvb/testwwvb.py CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/python3
2
+ # ruff: noqa: E501
3
+
2
4
  """Test most wwvblib functionality"""
3
5
 
4
6
  # Copyright (C) 2011-2020 Jeff Epler <jepler@gmail.com>
@@ -6,16 +8,18 @@
6
8
  #
7
9
  # SPDX-License-Identifier: GPL-3.0-only
8
10
 
11
+ from __future__ import annotations
12
+
9
13
  import copy
10
14
  import datetime
11
- import glob
12
15
  import io
16
+ import pathlib
13
17
  import random
14
18
  import sys
15
19
  import unittest
16
- from typing import Optional
17
20
 
18
21
  import uwwvb
22
+
19
23
  import wwvb
20
24
 
21
25
  from . import decode, iersdata, tz
@@ -34,10 +38,9 @@ class WWVBTestCase(unittest.TestCase):
34
38
 
35
39
  def test_cases(self) -> None:
36
40
  """Generate a test case for each expected output in tests/"""
37
- for test in glob.glob("tests/*"):
41
+ for test in pathlib.Path("tests").glob("*"):
38
42
  with self.subTest(test=test):
39
- with open(test, "rt", encoding="utf-8") as f:
40
- text = f.read()
43
+ text = test.read_text(encoding="utf-8")
41
44
  lines = [line for line in text.split("\n") if not line.startswith("#")]
42
45
  while not lines[0]:
43
46
  del lines[0]
@@ -53,7 +56,7 @@ class WWVBTestCase(unittest.TestCase):
53
56
  elif o.startswith("--style="):
54
57
  style = o[8:]
55
58
  else:
56
- raise ValueError(f"Unknown option {repr(o)}")
59
+ raise ValueError(f"Unknown option {o!r}")
57
60
  num_minutes = len(lines) - 2
58
61
  if channel == "both":
59
62
  num_minutes = len(lines) // 3
@@ -84,14 +87,14 @@ class WWVBRoundtrip(unittest.TestCase):
84
87
 
85
88
  def test_decode(self) -> None:
86
89
  """Test that a range of minutes including a leap second are correctly decoded by the state-based decoder"""
87
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
90
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
88
91
  decoder = decode.wwvbreceive()
89
92
  next(decoder)
90
93
  decoder.send(wwvb.AmplitudeModulation.MARK)
91
94
  any_leap_second = False
92
95
  for _ in range(20):
93
96
  timecode = minute.as_timecode()
94
- decoded: Optional[wwvb.WWVBTimecode] = None
97
+ decoded: wwvb.WWVBTimecode | None = None
95
98
  if len(timecode.am) == 61:
96
99
  any_leap_second = True
97
100
  for code in timecode.am:
@@ -108,28 +111,28 @@ class WWVBRoundtrip(unittest.TestCase):
108
111
  def test_cover_fill_pm_timecode_extended(self) -> None:
109
112
  """Get full coverage of the function pm_timecode_extended"""
110
113
  for dt in (
111
- datetime.datetime(1992, 1, 1),
112
- datetime.datetime(1992, 4, 5),
113
- datetime.datetime(1992, 6, 1),
114
- datetime.datetime(1992, 10, 25),
114
+ datetime.datetime(1992, 1, 1, tzinfo=datetime.timezone.utc),
115
+ datetime.datetime(1992, 4, 5, tzinfo=datetime.timezone.utc),
116
+ datetime.datetime(1992, 6, 1, tzinfo=datetime.timezone.utc),
117
+ datetime.datetime(1992, 10, 25, tzinfo=datetime.timezone.utc),
115
118
  ):
116
119
  for hour in (0, 4, 11):
117
- dt = dt.replace(hour=hour, minute=10)
118
- minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
120
+ dt1 = dt.replace(hour=hour, minute=10)
121
+ minute = wwvb.WWVBMinuteIERS.from_datetime(dt1)
119
122
  assert minute is not None
120
123
  timecode = minute.as_timecode().am
121
124
  assert timecode
122
125
 
123
126
  def test_roundtrip(self) -> None:
124
127
  """Test that a wide of minutes are correctly decoded by the state-based decoder"""
125
- dt = datetime.datetime(1992, 1, 1, 0, 0)
128
+ dt = datetime.datetime(1992, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
126
129
  delta = datetime.timedelta(minutes=915 if sys.implementation.name == "cpython" else 86400 - 915)
127
130
  while dt.year < 1993:
128
131
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
129
132
  assert minute is not None
130
133
  timecode = minute.as_timecode().am
131
134
  assert timecode
132
- decoded_minute: Optional[wwvb.WWVBMinute] = wwvb.WWVBMinuteIERS.from_timecode_am(minute.as_timecode())
135
+ decoded_minute: wwvb.WWVBMinute | None = wwvb.WWVBMinuteIERS.from_timecode_am(minute.as_timecode())
133
136
  assert decoded_minute
134
137
  decoded = decoded_minute.as_timecode().am
135
138
  self.assertEqual(
@@ -141,7 +144,7 @@ class WWVBRoundtrip(unittest.TestCase):
141
144
 
142
145
  def test_noise(self) -> None:
143
146
  """Test against pseudorandom noise"""
144
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
147
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
145
148
  r = random.Random(408)
146
149
  junk = [
147
150
  r.choice(
@@ -149,12 +152,12 @@ class WWVBRoundtrip(unittest.TestCase):
149
152
  wwvb.AmplitudeModulation.MARK,
150
153
  wwvb.AmplitudeModulation.ONE,
151
154
  wwvb.AmplitudeModulation.ZERO,
152
- ]
155
+ ],
153
156
  )
154
157
  for _ in range(480)
155
158
  ]
156
159
  timecode = minute.as_timecode()
157
- test_input = junk + [wwvb.AmplitudeModulation.MARK] + timecode.am
160
+ test_input = [*junk, wwvb.AmplitudeModulation.MARK, *timecode.am]
158
161
  decoder = decode.wwvbreceive()
159
162
  next(decoder)
160
163
  for code in test_input[:-1]:
@@ -171,7 +174,7 @@ class WWVBRoundtrip(unittest.TestCase):
171
174
 
172
175
  def test_noise2(self) -> None:
173
176
  """Test of the full minute decoder with targeted errors to get full coverage"""
174
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
177
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
175
178
  timecode = minute.as_timecode()
176
179
  decoded = wwvb.WWVBMinute.from_timecode_am(timecode)
177
180
  self.assertIsNotNone(decoded)
@@ -206,7 +209,7 @@ class WWVBRoundtrip(unittest.TestCase):
206
209
 
207
210
  def test_noise3(self) -> None:
208
211
  """Test impossible BCD values"""
209
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
212
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
210
213
  timecode = minute.as_timecode()
211
214
 
212
215
  for poslist in [
@@ -228,12 +231,12 @@ class WWVBRoundtrip(unittest.TestCase):
228
231
 
229
232
  def test_previous_next_minute(self) -> None:
230
233
  """Test that previous minute and next minute are inverses"""
231
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
234
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
232
235
  self.assertEqual(minute, minute.next_minute().previous_minute())
233
236
 
234
237
  def test_timecode_str(self) -> None:
235
238
  """Test the str() and repr() methods"""
236
- minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
239
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
237
240
  timecode = minute.as_timecode()
238
241
  self.assertEqual(
239
242
  str(timecode),
@@ -256,6 +259,9 @@ class WWVBRoundtrip(unittest.TestCase):
256
259
 
257
260
  self.assertEqual(wwvb.get_dut1(e), wwvb.get_dut1(ep1))
258
261
 
262
+ ep2 = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=340)
263
+ wwvb.get_dut1(ep2)
264
+
259
265
  def test_epoch(self) -> None:
260
266
  """Test the 1970-to-2069 epoch"""
261
267
  m = wwvb.WWVBMinute(69, 1, 1, 0, 0)
@@ -268,7 +274,6 @@ class WWVBRoundtrip(unittest.TestCase):
268
274
 
269
275
  def test_fromstring(self) -> None:
270
276
  """Test the fromstring() classmethod"""
271
-
272
277
  s = "WWVB timecode: year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1"
273
278
  t = "year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1"
274
279
  self.assertEqual(wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t))
@@ -279,7 +284,7 @@ class WWVBRoundtrip(unittest.TestCase):
279
284
 
280
285
  def test_from_datetime(self) -> None:
281
286
  """Test the from_datetime() classmethod"""
282
- d = datetime.datetime(1998, 12, 31, 23, 56, 0)
287
+ d = datetime.datetime(1998, 12, 31, 23, 56, 0, tzinfo=datetime.timezone.utc)
283
288
  self.assertEqual(
284
289
  wwvb.WWVBMinuteIERS.from_datetime(d),
285
290
  wwvb.WWVBMinuteIERS.from_datetime(d, newls=True, newut1=-300),
@@ -299,16 +304,11 @@ class WWVBRoundtrip(unittest.TestCase):
299
304
  with self.assertRaises(ValueError):
300
305
  wwvb.WWVBMinute.fromstring("year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1 boo=1")
301
306
 
302
- def test_deprecated(self) -> None:
303
- """Ensure that the 'maybe_warn_update' function is covered"""
304
- with self.assertWarnsRegex(DeprecationWarning, "use ly"):
305
- wwvb.WWVBMinute(2020, 1, 1, 1).is_ly()
306
-
307
307
  def test_update(self) -> None:
308
308
  """Ensure that the 'maybe_warn_update' function is covered"""
309
309
  with self.assertWarnsRegex(Warning, "updateiers"):
310
310
  wwvb._maybe_warn_update(datetime.date(1970, 1, 1))
311
- wwvb._maybe_warn_update(datetime.datetime(1970, 1, 1, 0, 0))
311
+ wwvb._maybe_warn_update(datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc))
312
312
 
313
313
  def test_undefined(self) -> None:
314
314
  """Ensure that the check for unset elements in am works"""
@@ -317,32 +317,41 @@ class WWVBRoundtrip(unittest.TestCase):
317
317
 
318
318
  def test_tz(self) -> None:
319
319
  """Get a little more coverage in the dst change functions"""
320
- date, row = wwvb.get_dst_change_date_and_row(datetime.datetime(1960, 1, 1))
320
+ date, row = wwvb._get_dst_change_date_and_row(datetime.datetime(1960, 1, 1, tzinfo=datetime.timezone.utc))
321
321
  self.assertIsNone(date)
322
322
  self.assertIsNone(row)
323
323
 
324
- self.assertIsNone(wwvb.get_dst_change_hour(datetime.datetime(1960, 1, 1)))
324
+ self.assertIsNone(wwvb._get_dst_change_hour(datetime.datetime(1960, 1, 1, tzinfo=datetime.timezone.utc)))
325
325
 
326
- self.assertEqual(wwvb.get_dst_next(datetime.datetime(1960, 1, 1)), 0b000111)
326
+ self.assertEqual(wwvb._get_dst_next(datetime.datetime(1960, 1, 1, tzinfo=datetime.timezone.utc)), 0b000111)
327
327
 
328
328
  # Cuba followed year-round DST for several years
329
329
  self.assertEqual(
330
- wwvb.get_dst_next(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Cuba")),
330
+ wwvb._get_dst_next(datetime.datetime(2005, 1, 1, tzinfo=datetime.timezone.utc), tz=tz.ZoneInfo("Cuba")),
331
331
  0b101111,
332
332
  )
333
- date, row = wwvb.get_dst_change_date_and_row(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Cuba"))
333
+ date, row = wwvb._get_dst_change_date_and_row(
334
+ datetime.datetime(2005, 1, 1, tzinfo=datetime.timezone.utc),
335
+ tz=tz.ZoneInfo("Cuba"),
336
+ )
334
337
  self.assertIsNone(date)
335
338
  self.assertIsNone(row)
336
339
 
337
340
  # California was weird in 1948
338
341
  self.assertEqual(
339
- wwvb.get_dst_next(datetime.datetime(1948, 1, 1), tz=tz.ZoneInfo("America/Los_Angeles")),
342
+ wwvb._get_dst_next(
343
+ datetime.datetime(1948, 1, 1, tzinfo=datetime.timezone.utc),
344
+ tz=tz.ZoneInfo("America/Los_Angeles"),
345
+ ),
340
346
  0b100011,
341
347
  )
342
348
 
343
349
  # Berlin had DST changes on Monday in 1917
344
350
  self.assertEqual(
345
- wwvb.get_dst_next(datetime.datetime(1917, 1, 1), tz=tz.ZoneInfo("Europe/Berlin")),
351
+ wwvb._get_dst_next(
352
+ datetime.datetime(1917, 1, 1, tzinfo=datetime.timezone.utc),
353
+ tz=tz.ZoneInfo("Europe/Berlin"),
354
+ ),
346
355
  0b100011,
347
356
  )
348
357
 
@@ -350,7 +359,10 @@ class WWVBRoundtrip(unittest.TestCase):
350
359
  # Australia observes DST in the other half of the year compared to the
351
360
  # Northern hemisphere
352
361
  self.assertEqual(
353
- wwvb.get_dst_next(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Australia/Melbourne")),
362
+ wwvb._get_dst_next(
363
+ datetime.datetime(2005, 1, 1, tzinfo=datetime.timezone.utc),
364
+ tz=tz.ZoneInfo("Australia/Melbourne"),
365
+ ),
354
366
  0b100011,
355
367
  )
356
368