wwvb 4.0.0a0__py3-none-any.whl → 4.1.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/decode.py DELETED
@@ -1,90 +0,0 @@
1
- # SPDX-FileCopyrightText: 2021 Jeff Epler
2
- #
3
- # SPDX-License-Identifier: GPL-3.0-only
4
- """A stateful decoder of WWVB signals"""
5
-
6
- from __future__ import annotations
7
-
8
- import sys
9
- from typing import Generator
10
-
11
- import wwvb
12
-
13
- # State 1: Unsync'd
14
- # Marker: State 2
15
- # Other: State 1
16
- # State 2: One marker
17
- # Marker: State 3
18
- # Other: State 1
19
- # State 3: Two markers
20
- # Marker: State 3
21
- # Other: State 4
22
- # State 4: Decoding a minute, starting in second 1
23
- # Second
24
-
25
- always_zero = {4, 10, 11, 14, 20, 21, 34, 35, 44, 54}
26
-
27
-
28
- def wwvbreceive() -> Generator[wwvb.WWVBTimecode | None, wwvb.AmplitudeModulation, None]:
29
- """Decode WWVB signals statefully."""
30
- minute: list[wwvb.AmplitudeModulation] = []
31
- state = 1
32
-
33
- value = yield None
34
- while True:
35
- # print(state, value, len(minute), "".join(str(int(i)) for i in minute))
36
- if state == 1:
37
- minute = []
38
- if value == wwvb.AmplitudeModulation.MARK:
39
- state = 2
40
- value = yield None
41
-
42
- elif state == 2:
43
- state = 3 if value == wwvb.AmplitudeModulation.MARK else 1
44
- value = yield None
45
-
46
- elif state == 3:
47
- if value != wwvb.AmplitudeModulation.MARK:
48
- state = 4
49
- minute = [wwvb.AmplitudeModulation.MARK, value]
50
- value = yield None
51
-
52
- else: # state == 4:
53
- minute.append(value)
54
- if len(minute) % 10 == 0 and value != wwvb.AmplitudeModulation.MARK:
55
- # print("MISSING MARK", len(minute), "".join(str(int(i)) for i in minute))
56
- state = 1
57
- elif len(minute) % 10 and value == wwvb.AmplitudeModulation.MARK:
58
- # print("UNEXPECTED MARK")
59
- state = 1
60
- elif len(minute) - 1 in always_zero and value != wwvb.AmplitudeModulation.ZERO:
61
- # print("UNEXPECTED NONZERO")
62
- state = 1
63
- elif len(minute) == 60:
64
- # print("FULL MINUTE")
65
- tc = wwvb.WWVBTimecode(60)
66
- tc.am[:] = minute
67
- minute = []
68
- state = 2
69
- value = yield tc
70
- else:
71
- value = yield None
72
-
73
-
74
- def main() -> None:
75
- """Read symbols on stdin and print any successfully-decoded minutes"""
76
- decoder = wwvbreceive()
77
- next(decoder)
78
- decoder.send(wwvb.AmplitudeModulation.MARK)
79
- for s in sys.argv[1:]:
80
- for c in s:
81
- decoded = decoder.send(wwvb.AmplitudeModulation(int(c)))
82
- if decoded:
83
- print(decoded)
84
- w = wwvb.WWVBMinute.from_timecode_am(decoded)
85
- if w:
86
- print(w)
87
-
88
-
89
- if __name__ == "__main__": # pragma no cover
90
- main()
wwvb/dut1table.py DELETED
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # SPDX-FileCopyrightText: 2021 Jeff Epler
4
- #
5
- # SPDX-License-Identifier: GPL-3.0-only
6
-
7
- """Print the table of historical DUT1 values"""
8
-
9
- from datetime import timedelta
10
- from itertools import groupby
11
-
12
- import wwvb
13
-
14
- from .iersdata import DUT1_DATA_START, DUT1_OFFSETS
15
-
16
-
17
- def main() -> None:
18
- """Print the table of historical DUT1 values"""
19
- date = DUT1_DATA_START
20
- for key, it in groupby(DUT1_OFFSETS):
21
- dut1_ms = (ord(key) - ord("k")) / 10.0
22
- count = len(list(it))
23
- end = date + timedelta(days=count - 1)
24
- dut1_next = wwvb.get_dut1(date + timedelta(days=count), warn_outdated=False)
25
- ls = f" LS on {end:%F} 23:59:60 UTC" if dut1_ms * dut1_next < 0 else ""
26
- print(f"{date:%F} {dut1_ms: 3.1f} {count:4d}{ls}")
27
- date += timedelta(days=count)
28
- print(date)
29
-
30
-
31
- if __name__ == "__main__": # pragma no branch
32
- main()
wwvb/gen.py DELETED
@@ -1,128 +0,0 @@
1
- #!/usr/bin/python3
2
- """A command-line program for generating wwvb timecodes"""
3
-
4
- # Copyright (C) 2011-2020 Jeff Epler <jepler@gmail.com>
5
- # SPDX-FileCopyrightText: 2021 Jeff Epler
6
- #
7
- # SPDX-License-Identifier: GPL-3.0-only
8
-
9
- from __future__ import annotations
10
-
11
- import datetime
12
- import sys
13
- from typing import Any
14
-
15
- import click
16
- import dateutil.parser
17
-
18
- from . import WWVBMinute, WWVBMinuteIERS, print_timecodes, print_timecodes_json, styles
19
-
20
-
21
- def parse_timespec(ctx: Any, param: Any, value: list[str]) -> datetime.datetime: # noqa: ARG001
22
- """Parse a time specifier from the commandline"""
23
- try:
24
- if len(value) == 5:
25
- year, month, day, hour, minute = map(int, value)
26
- return datetime.datetime(year, month, day, hour, minute, tzinfo=datetime.timezone.utc)
27
- if len(value) == 4:
28
- year, yday, hour, minute = map(int, value)
29
- return datetime.datetime(year, 1, 1, hour, minute, tzinfo=datetime.timezone.utc) + datetime.timedelta(
30
- days=yday - 1,
31
- )
32
- if len(value) == 1:
33
- return dateutil.parser.parse(value[0])
34
- if len(value) == 0:
35
- return datetime.datetime.now(datetime.timezone.utc)
36
- raise ValueError("Unexpected number of arguments")
37
- except ValueError as e:
38
- raise click.UsageError(f"Could not parse timespec: {e}") from e
39
-
40
-
41
- @click.command()
42
- @click.option(
43
- "--iers/--no-iers",
44
- "-i/-I",
45
- default=True,
46
- help="Whether to use IESR data for DUT1 and LS. (Default: --iers)",
47
- )
48
- @click.option(
49
- "--leap-second",
50
- "-s",
51
- "leap_second",
52
- flag_value=1,
53
- default=None,
54
- help="Force a positive leap second at the end of the GMT month (Implies --no-iers)",
55
- )
56
- @click.option(
57
- "--negative-leap-second",
58
- "-n",
59
- "leap_second",
60
- flag_value=-1,
61
- help="Force a negative leap second at the end of the GMT month (Implies --no-iers)",
62
- )
63
- @click.option(
64
- "--no-leap-second",
65
- "-S",
66
- "leap_second",
67
- flag_value=0,
68
- help="Force no leap second at the end of the month (Implies --no-iers)",
69
- )
70
- @click.option("--dut1", "-d", type=int, help="Force the DUT1 value (Implies --no-iers)")
71
- @click.option("--minutes", "-m", default=10, help="Number of minutes to show (default: 10)")
72
- @click.option(
73
- "--style",
74
- default="default",
75
- type=click.Choice(sorted(["json", *list(styles.keys())])),
76
- help="Style of output",
77
- )
78
- @click.option(
79
- "--all-timecodes/--no-all-timecodes",
80
- "-t/-T",
81
- default=False,
82
- type=bool,
83
- help="Show the 'WWVB timecode' line before each minute",
84
- )
85
- @click.option(
86
- "--channel",
87
- type=click.Choice(["amplitude", "phase", "both"]),
88
- default="amplitude",
89
- help="Modulation to show (default: amplitude)",
90
- )
91
- @click.argument("timespec", type=str, nargs=-1, callback=parse_timespec)
92
- def main(
93
- *,
94
- iers: bool,
95
- leap_second: bool,
96
- dut1: int,
97
- minutes: int,
98
- style: str,
99
- channel: str,
100
- all_timecodes: bool,
101
- timespec: datetime.datetime,
102
- ) -> None:
103
- """Generate WWVB timecodes
104
-
105
- TIMESPEC: one of "year yday hour minute" or "year month day hour minute", or else the current minute
106
- """
107
- if (leap_second is not None) or (dut1 is not None):
108
- iers = False
109
-
110
- newut1 = None
111
- newls = None
112
-
113
- if iers:
114
- constructor: type[WWVBMinute] = WWVBMinuteIERS
115
- else:
116
- constructor = WWVBMinute
117
- newut1 = -500 * (leap_second or 0) if dut1 is None else dut1
118
- newls = bool(leap_second)
119
-
120
- w = constructor.from_datetime(timespec, newls=newls, newut1=newut1)
121
- if style == "json":
122
- print_timecodes_json(w, minutes, channel, file=sys.stdout)
123
- else:
124
- print_timecodes(w, minutes, channel, style, all_timecodes=all_timecodes, file=sys.stdout)
125
-
126
-
127
- if __name__ == "__main__": # pragma no branch
128
- main()
wwvb/iersdata.py DELETED
@@ -1,28 +0,0 @@
1
- # -*- python3 -*-
2
- """Retrieve iers data, possibly from user or site data or from the wwvbpy distribution"""
3
-
4
- # Copyright (C) 2021 Jeff Epler <jepler@gmail.com>
5
- # SPDX-FileCopyrightText: 2021 Jeff Epler
6
- #
7
- # SPDX-License-Identifier: GPL-3.0-only
8
-
9
- import datetime
10
- import pathlib
11
-
12
- import platformdirs
13
-
14
- __all__ = ["DUT1_DATA_START", "DUT1_OFFSETS", "start", "span", "end"]
15
- from .iersdata_dist import DUT1_DATA_START, DUT1_OFFSETS
16
-
17
- for location in [
18
- platformdirs.user_data_dir("wwvbpy", "unpythonic.net"),
19
- platformdirs.site_data_dir("wwvbpy", "unpythonic.net"),
20
- ]: # pragma no cover
21
- path = pathlib.Path(location) / "wwvbpy_iersdata.py"
22
- if path.exists():
23
- exec(path.read_text(encoding="utf-8"), globals(), globals())
24
- break
25
-
26
- start = datetime.datetime.combine(DUT1_DATA_START, datetime.time()).replace(tzinfo=datetime.timezone.utc)
27
- span = datetime.timedelta(days=len(DUT1_OFFSETS))
28
- end = start + span
wwvb/iersdata_dist.py DELETED
@@ -1,38 +0,0 @@
1
- # -*- python3 -*-
2
- # fmt: off
3
- """File generated from public data - not subject to copyright"""
4
- # SPDX-FileCopyrightText: Public domain
5
- # SPDX-License-Identifier: CC0-1.0
6
- # isort: skip_file
7
- import datetime
8
- __all__ = ["DUT1_DATA_START", "DUT1_OFFSETS"]
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")
11
- DUT1_OFFSETS = str( # 19720101
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
- +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
14
- +k*37+j*33+i*34+h*15+r*22+q*34+p*33+o*34+n*37+m*49+l*45+k*36 # 19751118
15
- +j*32+i*36+h*7+r*28+q*33+p*32+o*30+n*33+m*42+l*42+k*34+j*29 # 19761201
16
- +i*33+h*30+r*6+q*36+p*34+o*31+n*32+m*42+l*51+k*37+j*32+i*33 # 19771231
17
- +h*31+q*32+p*29+o*29+n*30+m*32+l*47+k*47+j*36+i*33+h*32+g*18 # 19790116
18
- +q*16+p*35+o*33+n*32+m*35+l*45+k*51+j*39+i*39+h*38+g*2+q*40 # 19800319
19
- +p*39+o*38+n*43+m*57+l*50+k*39+j*42+i*41+h*43+g*37+f*39+e*39 # 19810719
20
- +o*19+n*62+m*43+l*45+k*48+j*44+i*39+h*44+g*21+q*44+p*48+o*43 # 19821223
21
- +n*41+m*36+l*34+k*34+j*38+i*47+s+r*64+q*50+p*42+o*56+n*57 # 19840517
22
- +m*52+l*100+k*61+j*62+i*66+h*52+g*67+f+p*103+o*56+n*68+m*69 # 19860807
23
- +l*107+k*82+j*72+i*67+h*63+g*113+f*63+e*51+o*11+n*60+m*59 # 19880907
24
- +l*121+k*71+j*71+i*67+h*57+g*93+f*61+e*48+d*12+n*41+m*44 # 19900511
25
- +l*46+k*61+j*66+i*47+h*45+g*15+q*32+p*44+o*41+n*48+m*74+l*49 # 19911129
26
- +k*45+j*44+i*40+h*37+g*38+f*50+e*5+o*60+n*49+m*40+l*40+k*38 # 19930322
27
- +j*38+i*36+h*39+g*25+q*31+p*50+o*41+n*41+m*43+l*41+k*39+j*40 # 19940630
28
- +i*39+s*24+r*57+q*43+p*41+o*39+n*38+m*35+l*37+k*43+j*69+i*44 # 19951124
29
- +h*42+g*37+q*4+p*51+o*45+n*44+m*69+l*70+k*50+j*54+i*53+h*40 # 19970612
30
- +g*49+f*18+p*59+o*53+n*52+m*57+l*48+k*53+j*127+i*70+h*30 # 19990303
31
- +r*62+q*79+p*152+o*82+n*106+m*184+l*125+k*217+j*133+i*252 # 20030402
32
- +h*161+g*392+f*322+e*290+n*116+m*154+l*85+k*83+j*91+i*168 # 20080312
33
- +h*105+g*147+f*105+e*42+o*70+n*91+m*154+l*119+k*84+j*217 # 20110511
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
- +i*77+h*140+g*91+f*84+e*70+d*34+n*72+m*76+l*66+k*53+j*56 # 20160831
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*768+l*47+k*3+l*4+k*252, # 20250705
38
- )
wwvb/testcli.py DELETED
@@ -1,291 +0,0 @@
1
- #!/usr/bin/python3
2
- """Test most wwvblib commandline programs"""
3
-
4
- # ruff: noqa: N802 D102
5
- # Copyright (C) 2011-2020 Jeff Epler <jepler@gmail.com>
6
- # SPDX-FileCopyrightText: 2021 Jeff Epler
7
- #
8
- # SPDX-License-Identifier: GPL-3.0-only
9
-
10
- import json
11
- import os
12
- import subprocess
13
- import sys
14
- import unittest
15
- from typing import Any, Sequence
16
-
17
- coverage_add = ("-m", "coverage", "run", "--branch", "-p") if "COVERAGE_RUN" in os.environ else ()
18
-
19
-
20
- class CLITestCase(unittest.TestCase):
21
- """Test various CLI commands within wwvbpy"""
22
-
23
- def programOutput(self, *args: str) -> str:
24
- env = os.environ.copy()
25
- env["PYTHONIOENCODING"] = "utf-8"
26
- return subprocess.check_output(args, stdin=subprocess.DEVNULL, encoding="utf-8", env=env)
27
-
28
- def moduleArgs(self, *args: str) -> Sequence[str]:
29
- return (sys.executable, *coverage_add, "-m", *args)
30
-
31
- def moduleOutput(self, *args: str) -> str:
32
- return self.programOutput(sys.executable, *coverage_add, "-m", *args)
33
-
34
- def assertProgramOutput(self, expected: str, *args: str) -> None:
35
- """Check the output from invoking a program matches the expected"""
36
- actual = self.programOutput(*args)
37
- self.assertMultiLineEqual(expected, actual, f"args={args}")
38
-
39
- def assertProgramOutputStarts(self, expected: str, *args: str) -> None:
40
- """Check the output from invoking a program matches the expected"""
41
- actual = self.programOutput(*args)
42
- self.assertMultiLineEqual(expected, actual[: len(expected)], f"args={args}")
43
-
44
- def assertModuleOutput(self, expected: str, *args: str) -> None:
45
- """Check the output from invoking a `python -m modulename` program matches the expected"""
46
- actual = self.moduleOutput(*args)
47
- self.assertMultiLineEqual(expected, actual, f"args={args}")
48
-
49
- def assertStarts(self, expected: str, actual: str, *args: str) -> None:
50
- self.assertMultiLineEqual(expected, actual[: len(expected)], f"args={args}")
51
-
52
- def assertModuleJson(self, expected: Any, *args: str) -> None:
53
- """Check the output from invoking a `python -m modulename` program matches the expected"""
54
- actual = self.moduleOutput(*args)
55
- self.assertEqual(json.loads(actual), expected)
56
-
57
- def assertModuleOutputStarts(self, expected: str, *args: str) -> None:
58
- """Check the output from invoking a `python -m modulename` program matches the expected"""
59
- actual = self.moduleOutput(*args)
60
- self.assertStarts(expected, actual, *args)
61
-
62
- def assertProgramError(self, *args: str) -> None:
63
- """Check the output from invoking a program fails"""
64
- env = os.environ.copy()
65
- env["PYTHONIOENCODING"] = "utf-8"
66
- with self.assertRaises(subprocess.SubprocessError):
67
- subprocess.check_output(
68
- args,
69
- stdin=subprocess.DEVNULL,
70
- stderr=subprocess.DEVNULL,
71
- encoding="utf-8",
72
- env=env,
73
- )
74
-
75
- def assertModuleError(self, *args: str) -> None:
76
- """Check the output from invoking a `python -m modulename` program fails"""
77
- self.assertProgramError(*self.moduleArgs(*args))
78
-
79
- def test_gen(self) -> None:
80
- """Test wwvb.gen"""
81
- self.assertModuleOutput(
82
- """\
83
- WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0
84
- 2020-001 12:30 201100000200010001020000000002000100010200100001020000010002
85
- """,
86
- "wwvb.gen",
87
- "-m",
88
- "1",
89
- "2020-1-1 12:30",
90
- )
91
-
92
- self.assertModuleOutput(
93
- """\
94
- WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0
95
- 2020-001 12:30 201100000200010001020000000002000100010200100001020000010002
96
- """,
97
- "wwvb.gen",
98
- "-m",
99
- "1",
100
- "2020",
101
- "1",
102
- "12",
103
- "30",
104
- )
105
-
106
- self.assertModuleOutput(
107
- """\
108
- WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-200 ly=1 ls=0
109
- 2020-001 12:30 201100000200010001020000000002000100010200100001020000010002
110
- """,
111
- "wwvb.gen",
112
- "-m",
113
- "1",
114
- "2020",
115
- "1",
116
- "1",
117
- "12",
118
- "30",
119
- )
120
-
121
- self.assertModuleError("wwvb.gen", "-m", "1", "2021", "7")
122
-
123
- # Asserting a leap second
124
- self.assertModuleOutput(
125
- """\
126
- WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-500 ly=1 ls=1
127
- 2020-001 12:30 201100000200010001020000000002000100010201010001020000011002
128
- """,
129
- "wwvb.gen",
130
- "-m",
131
- "1",
132
- "-s",
133
- "2020-1-1 12:30",
134
- )
135
-
136
- # Asserting a different ut1 value
137
- self.assertModuleOutput(
138
- """\
139
- WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0
140
- 2020-001 12:30 201100000200010001020000000002000100010200110001020000010002
141
- """,
142
- "wwvb.gen",
143
- "-m",
144
- "1",
145
- "-d",
146
- "-300",
147
- "2020-1-1 12:30",
148
- )
149
-
150
- def test_dut1table(self) -> None:
151
- """Test the dut1table program"""
152
- self.assertModuleOutputStarts(
153
- """\
154
- 1972-01-01 -0.2 182 LS on 1972-06-30 23:59:60 UTC
155
- 1972-07-01 0.8 123
156
- 1972-11-01 0.0 30
157
- 1972-12-01 -0.2 31 LS on 1972-12-31 23:59:60 UTC
158
- """,
159
- "wwvb.dut1table",
160
- )
161
-
162
- def test_json(self) -> None:
163
- """Test the JSON output format"""
164
- self.assertModuleJson(
165
- [
166
- {
167
- "year": 2021,
168
- "days": 340,
169
- "hour": 3,
170
- "minute": 40,
171
- "amplitude": "210000000200000001120011001002000000010200010001020001000002",
172
- "phase": "111110011011010101000100100110011110001110111010111101001011",
173
- },
174
- {
175
- "year": 2021,
176
- "days": 340,
177
- "hour": 3,
178
- "minute": 41,
179
- "amplitude": "210000001200000001120011001002000000010200010001020001000002",
180
- "phase": "001010011100100011000101110000100001101000001111101100000010",
181
- },
182
- ],
183
- "wwvb.gen",
184
- "-m",
185
- "2",
186
- "--style",
187
- "json",
188
- "--channel",
189
- "both",
190
- "2021-12-6 3:40",
191
- )
192
- self.assertModuleJson(
193
- [
194
- {
195
- "year": 2021,
196
- "days": 340,
197
- "hour": 3,
198
- "minute": 40,
199
- "amplitude": "210000000200000001120011001002000000010200010001020001000002",
200
- },
201
- {
202
- "year": 2021,
203
- "days": 340,
204
- "hour": 3,
205
- "minute": 41,
206
- "amplitude": "210000001200000001120011001002000000010200010001020001000002",
207
- },
208
- ],
209
- "wwvb.gen",
210
- "-m",
211
- "2",
212
- "--style",
213
- "json",
214
- "--channel",
215
- "amplitude",
216
- "2021-12-6 3:40",
217
- )
218
- self.assertModuleJson(
219
- [
220
- {
221
- "year": 2021,
222
- "days": 340,
223
- "hour": 3,
224
- "minute": 40,
225
- "phase": "111110011011010101000100100110011110001110111010111101001011",
226
- },
227
- {
228
- "year": 2021,
229
- "days": 340,
230
- "hour": 3,
231
- "minute": 41,
232
- "phase": "001010011100100011000101110000100001101000001111101100000010",
233
- },
234
- ],
235
- "wwvb.gen",
236
- "-m",
237
- "2",
238
- "--style",
239
- "json",
240
- "--channel",
241
- "phase",
242
- "2021-12-6 3:40",
243
- )
244
-
245
- def test_sextant(self) -> None:
246
- """Test the sextant output format"""
247
- self.assertModuleOutput(
248
- """\
249
- WWVB timecode: year=2021 days=340 hour=03 min=40 dst=0 ut1=-100 ly=0 ls=0 --style=sextant
250
- 2021-340 03:40 \
251
- 🬋🬩🬋🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬩🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬩🬹🬍🬎🬩🬹🬍🬎🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬎🬩🬹🬍🬎🬋🬎🬩🬹🬩🬹🬋🬍🬍🬎🬩🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬋🬎🬩🬹🬋🬩🬩🬹🬍🬎🬩🬹🬋🬹🬩🬹🬍🬎🬩🬹🬋🬎🬩🬹🬋🬩🬩🬹🬩🬹🬍🬎🬋🬹🬍🬎🬍🬎🬩🬹🬍🬎🬩🬹🬋🬩
252
-
253
- 2021-340 03:41 \
254
- 🬋🬍🬋🬎🬩🬹🬍🬎🬩🬹🬍🬎🬍🬎🬩🬹🬋🬹🬋🬩🬍🬎🬍🬎🬩🬹🬍🬎🬍🬎🬍🬎🬩🬹🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬹🬩🬹🬩🬹🬋🬎🬍🬎🬍🬎🬋🬍🬩🬹🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬩🬹🬋🬎🬩🬹🬋🬍🬍🬎🬍🬎🬍🬎🬋🬎🬩🬹🬩🬹🬩🬹🬋🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬋🬎🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬋🬍
255
-
256
- """,
257
- "wwvb.gen",
258
- "-m",
259
- "2",
260
- "--style",
261
- "sextant",
262
- "2021-12-6 3:40",
263
- )
264
-
265
- def test_now(self) -> None:
266
- """Test outputting timecodes for 'now'"""
267
- self.assertModuleOutputStarts(
268
- "WWVB timecode: year=",
269
- "wwvb.gen",
270
- "-m",
271
- "1",
272
- )
273
-
274
- def test_decode(self) -> None:
275
- """Test the commandline decoder"""
276
- self.assertModuleOutput(
277
- """\
278
- 201100000200100001020011001012000000010200010001020001000002
279
- year=2021 days=350 hour=22 min=30 dst=0 ut1=-100 ly=0 ls=0
280
- """,
281
- "wwvb.decode",
282
- "201100000200100001020011001012000000010200010001020001000002",
283
- )
284
-
285
- self.assertModuleOutput(
286
- """\
287
- 201101111200100001020011001012000000010200010001020001000002
288
- """,
289
- "wwvb.decode",
290
- "201101111200100001020011001012000000010200010001020001000002",
291
- )
wwvb/testdaylight.py DELETED
@@ -1,60 +0,0 @@
1
- #!/usr/bin/python3
2
- """Test of daylight saving time calculations"""
3
-
4
- # SPDX-FileCopyrightText: 2021 Jeff Epler
5
- #
6
- # SPDX-License-Identifier: GPL-3.0-only
7
-
8
- import datetime
9
- import unittest
10
-
11
- import wwvb
12
- from wwvb.tz import Mountain
13
-
14
-
15
- class TestDaylight(unittest.TestCase):
16
- """Test of daylight saving time calculations"""
17
-
18
- def test_onset(self) -> None:
19
- """Test that the onset of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
20
- for h in [8, 9, 10]:
21
- for dm in range(-1441, 1442):
22
- d = datetime.datetime(2021, 3, 14, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
23
- m = wwvb.WWVBMinute.from_datetime(d)
24
- self.assertEqual(
25
- m.as_datetime_local().replace(tzinfo=Mountain),
26
- d.astimezone(Mountain),
27
- )
28
-
29
- def test_end(self) -> None:
30
- """Test that the end of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
31
- for h in [7, 8, 9]:
32
- for dm in range(-1441, 1442):
33
- d = datetime.datetime(2021, 11, 7, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
34
- m = wwvb.WWVBMinute.from_datetime(d)
35
- self.assertEqual(
36
- m.as_datetime_local().replace(tzinfo=Mountain),
37
- d.astimezone(Mountain),
38
- )
39
-
40
- def test_midsummer(self) -> None:
41
- """Test that middle of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
42
- for h in [7, 8, 9]:
43
- for dm in (-1, 0, 1):
44
- d = datetime.datetime(2021, 7, 7, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
45
- m = wwvb.WWVBMinute.from_datetime(d)
46
- self.assertEqual(
47
- m.as_datetime_local().replace(tzinfo=Mountain),
48
- d.astimezone(Mountain),
49
- )
50
-
51
- def test_midwinter(self) -> None:
52
- """Test that middle of standard time is the same in Mountain and WWVBMinute (which uses ls bits)"""
53
- for h in [7, 8, 9]:
54
- for dm in (-1, 0, 1):
55
- d = datetime.datetime(2021, 12, 25, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
56
- m = wwvb.WWVBMinute.from_datetime(d)
57
- self.assertEqual(
58
- m.as_datetime_local().replace(tzinfo=Mountain),
59
- d.astimezone(Mountain),
60
- )