fly-hour-timer 1.1.3__tar.gz
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.
- fly_hour_timer-1.1.3/PKG-INFO +147 -0
- fly_hour_timer-1.1.3/README.md +130 -0
- fly_hour_timer-1.1.3/README_zh.md +127 -0
- fly_hour_timer-1.1.3/fly_hour_timer/__init__.py +10 -0
- fly_hour_timer-1.1.3/fly_hour_timer/fly_hour_timer.py +157 -0
- fly_hour_timer-1.1.3/fly_hour_timer.egg-info/PKG-INFO +147 -0
- fly_hour_timer-1.1.3/fly_hour_timer.egg-info/SOURCES.txt +10 -0
- fly_hour_timer-1.1.3/fly_hour_timer.egg-info/dependency_links.txt +1 -0
- fly_hour_timer-1.1.3/fly_hour_timer.egg-info/top_level.txt +1 -0
- fly_hour_timer-1.1.3/pyproject.toml +31 -0
- fly_hour_timer-1.1.3/setup.cfg +4 -0
- fly_hour_timer-1.1.3/tests/test_fly_hour_timer.py +124 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fly_hour_timer
|
|
3
|
+
Version: 1.1.3
|
|
4
|
+
Summary: A timezone-free Python time library: represents time as <hour>.<min_sec>
|
|
5
|
+
Author: fly
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: time,timezone-free,hour-timer,ai-bot,unix-time
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# fly_hour_timer
|
|
19
|
+
|
|
20
|
+
**English** | [简体中文](README_zh.md)
|
|
21
|
+
|
|
22
|
+
A timezone-free Python time library that represents time as **`<hour>.<min_sec>`**.
|
|
23
|
+
|
|
24
|
+
The core idea: express time as `<hour>.<min_sec>`, where the **hour is simply
|
|
25
|
+
`unix_time // 3600`** (whole hours elapsed since the unix epoch) and `<min_sec>`
|
|
26
|
+
is the minutes and seconds inside that hour. Because everything is derived
|
|
27
|
+
directly from the absolute unix time, the string is fully timezone-free and
|
|
28
|
+
identical on any machine anywhere — especially friendly for AI bots that
|
|
29
|
+
exchange timestamps across regions.
|
|
30
|
+
|
|
31
|
+
- No third-party dependency, pure standard library (`datetime` / `time` only)
|
|
32
|
+
- Fully timezone-free by construction
|
|
33
|
+
- Python >= 3.8
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install fly_hour_timer
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/fly-ttrr/fly_hour_timer.git
|
|
45
|
+
cd fly_hour_timer
|
|
46
|
+
pip install .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
`fly_hour_timer` is a static tool — no instance configuration required:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fly_hour_timer import fly_hour_timer
|
|
55
|
+
|
|
56
|
+
fly_hour_timer.hour() # '277777' -> hour (unix_time // 3600), str
|
|
57
|
+
fly_hour_timer.min_sec() # '4640' -> minutes + seconds, str
|
|
58
|
+
fly_hour_timer.hour_timer() # '277777.4640' -> <hour>.<min_sec>
|
|
59
|
+
fly_hour_timer.hour_timer_short() # '777.4640' -> <last 3 digits of hour>.<min_sec>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
With an explicit unix time (seconds):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from fly_hour_timer import FlyHourTimer
|
|
66
|
+
|
|
67
|
+
FlyHourTimer.hour_timer(1_000_000_000) # '277777.4640'
|
|
68
|
+
FlyHourTimer.hour_timer_short(1_000_000_000) # '777.4640'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
| Method | Description | Returns |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| `hour(unix_time=None)` | Whole hours since the epoch, `unix_time // 3600` | `str` |
|
|
76
|
+
| `min_sec(unix_time=None)` | Minutes + seconds inside the hour, 4 digits (`'0102'` = 01m02s) | `str` |
|
|
77
|
+
| `hour_timer(unix_time=None)` | `<hour>.<min_sec>` | `str` |
|
|
78
|
+
| `hour_timer_short(unix_time=None)` | `<last 3 digits of hour>.<min_sec>` | `str` |
|
|
79
|
+
| `hour_timer_to_time(hour_timer)` | Parse `<hour>.<min_sec>` back into a UTC `time.struct_time` | `time.struct_time` |
|
|
80
|
+
|
|
81
|
+
### Parameters
|
|
82
|
+
|
|
83
|
+
- **`unix_time`** — unix time in seconds (`int` / `float` / numeric `str`).
|
|
84
|
+
Omit to use the current time.
|
|
85
|
+
|
|
86
|
+
## How it works
|
|
87
|
+
|
|
88
|
+
Given a unix time in seconds:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
hour = unix_time // 3600 # whole hours since the epoch
|
|
92
|
+
rem = unix_time - hour * 3600 # seconds left inside the hour (0..3599)
|
|
93
|
+
min_sec = "%02d%02d" % (rem // 60, rem % 60)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `hour_timer()` -> `f"{hour}.{min_sec}"`
|
|
97
|
+
- `hour_timer_short()` -> `f"{hour % 1000:03d}.{min_sec}"` — the last 3 digits
|
|
98
|
+
of the hour roll over roughly every 1000 hours (~41.7 days), giving a compact,
|
|
99
|
+
timezone-free time id for logs and bot conversations.
|
|
100
|
+
|
|
101
|
+
No timezone parameter is needed: the hour count and the leftover minutes/seconds
|
|
102
|
+
come straight from the absolute unix time, so the result is the same everywhere.
|
|
103
|
+
|
|
104
|
+
## Reverse Conversion: `hour_timer_to_time()`
|
|
105
|
+
|
|
106
|
+
Since `<hour>` already encodes the total elapsed hours since the epoch (and thus
|
|
107
|
+
the full date), the conversion back is complete and needs no "today" anchor. It
|
|
108
|
+
returns a UTC `time.struct_time`:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from fly_hour_timer import FlyHourTimer
|
|
112
|
+
|
|
113
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
114
|
+
# time.struct_time(... 2001, 9, 9, 1, 46, 40 ...)
|
|
115
|
+
st.tm_hour, st.tm_min, st.tm_sec # (1, 46, 40)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
It is the exact inverse of `hour_timer()` for the clock fields, and raises
|
|
119
|
+
`ValueError` on malformed or out-of-range input (e.g. minutes `>= 60`).
|
|
120
|
+
|
|
121
|
+
> Note: pass the **full** `hour_timer()` string here. A short string like
|
|
122
|
+
> `'777.4640'` (from `hour_timer_short()`) has lost the high-order hour digits,
|
|
123
|
+
> so it reconstructs a different (much earlier) instant.
|
|
124
|
+
|
|
125
|
+
## Examples
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
>>> from fly_hour_timer import FlyHourTimer
|
|
129
|
+
>>> FlyHourTimer.hour_timer(0)
|
|
130
|
+
'0.0000'
|
|
131
|
+
>>> FlyHourTimer.hour_timer(1_000_000_000)
|
|
132
|
+
'277777.4640'
|
|
133
|
+
>>> FlyHourTimer.hour_timer_short(1_000_000_000)
|
|
134
|
+
'777.4640'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Run `python examples.py` in this repository for a live demo.
|
|
138
|
+
|
|
139
|
+
## Tests
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python tests/test_fly_hour_timer.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# fly_hour_timer
|
|
2
|
+
|
|
3
|
+
**English** | [简体中文](README_zh.md)
|
|
4
|
+
|
|
5
|
+
A timezone-free Python time library that represents time as **`<hour>.<min_sec>`**.
|
|
6
|
+
|
|
7
|
+
The core idea: express time as `<hour>.<min_sec>`, where the **hour is simply
|
|
8
|
+
`unix_time // 3600`** (whole hours elapsed since the unix epoch) and `<min_sec>`
|
|
9
|
+
is the minutes and seconds inside that hour. Because everything is derived
|
|
10
|
+
directly from the absolute unix time, the string is fully timezone-free and
|
|
11
|
+
identical on any machine anywhere — especially friendly for AI bots that
|
|
12
|
+
exchange timestamps across regions.
|
|
13
|
+
|
|
14
|
+
- No third-party dependency, pure standard library (`datetime` / `time` only)
|
|
15
|
+
- Fully timezone-free by construction
|
|
16
|
+
- Python >= 3.8
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install fly_hour_timer
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or from source:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/fly-ttrr/fly_hour_timer.git
|
|
28
|
+
cd fly_hour_timer
|
|
29
|
+
pip install .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
`fly_hour_timer` is a static tool — no instance configuration required:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from fly_hour_timer import fly_hour_timer
|
|
38
|
+
|
|
39
|
+
fly_hour_timer.hour() # '277777' -> hour (unix_time // 3600), str
|
|
40
|
+
fly_hour_timer.min_sec() # '4640' -> minutes + seconds, str
|
|
41
|
+
fly_hour_timer.hour_timer() # '277777.4640' -> <hour>.<min_sec>
|
|
42
|
+
fly_hour_timer.hour_timer_short() # '777.4640' -> <last 3 digits of hour>.<min_sec>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
With an explicit unix time (seconds):
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from fly_hour_timer import FlyHourTimer
|
|
49
|
+
|
|
50
|
+
FlyHourTimer.hour_timer(1_000_000_000) # '277777.4640'
|
|
51
|
+
FlyHourTimer.hour_timer_short(1_000_000_000) # '777.4640'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
| Method | Description | Returns |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `hour(unix_time=None)` | Whole hours since the epoch, `unix_time // 3600` | `str` |
|
|
59
|
+
| `min_sec(unix_time=None)` | Minutes + seconds inside the hour, 4 digits (`'0102'` = 01m02s) | `str` |
|
|
60
|
+
| `hour_timer(unix_time=None)` | `<hour>.<min_sec>` | `str` |
|
|
61
|
+
| `hour_timer_short(unix_time=None)` | `<last 3 digits of hour>.<min_sec>` | `str` |
|
|
62
|
+
| `hour_timer_to_time(hour_timer)` | Parse `<hour>.<min_sec>` back into a UTC `time.struct_time` | `time.struct_time` |
|
|
63
|
+
|
|
64
|
+
### Parameters
|
|
65
|
+
|
|
66
|
+
- **`unix_time`** — unix time in seconds (`int` / `float` / numeric `str`).
|
|
67
|
+
Omit to use the current time.
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
Given a unix time in seconds:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
hour = unix_time // 3600 # whole hours since the epoch
|
|
75
|
+
rem = unix_time - hour * 3600 # seconds left inside the hour (0..3599)
|
|
76
|
+
min_sec = "%02d%02d" % (rem // 60, rem % 60)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `hour_timer()` -> `f"{hour}.{min_sec}"`
|
|
80
|
+
- `hour_timer_short()` -> `f"{hour % 1000:03d}.{min_sec}"` — the last 3 digits
|
|
81
|
+
of the hour roll over roughly every 1000 hours (~41.7 days), giving a compact,
|
|
82
|
+
timezone-free time id for logs and bot conversations.
|
|
83
|
+
|
|
84
|
+
No timezone parameter is needed: the hour count and the leftover minutes/seconds
|
|
85
|
+
come straight from the absolute unix time, so the result is the same everywhere.
|
|
86
|
+
|
|
87
|
+
## Reverse Conversion: `hour_timer_to_time()`
|
|
88
|
+
|
|
89
|
+
Since `<hour>` already encodes the total elapsed hours since the epoch (and thus
|
|
90
|
+
the full date), the conversion back is complete and needs no "today" anchor. It
|
|
91
|
+
returns a UTC `time.struct_time`:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from fly_hour_timer import FlyHourTimer
|
|
95
|
+
|
|
96
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
97
|
+
# time.struct_time(... 2001, 9, 9, 1, 46, 40 ...)
|
|
98
|
+
st.tm_hour, st.tm_min, st.tm_sec # (1, 46, 40)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It is the exact inverse of `hour_timer()` for the clock fields, and raises
|
|
102
|
+
`ValueError` on malformed or out-of-range input (e.g. minutes `>= 60`).
|
|
103
|
+
|
|
104
|
+
> Note: pass the **full** `hour_timer()` string here. A short string like
|
|
105
|
+
> `'777.4640'` (from `hour_timer_short()`) has lost the high-order hour digits,
|
|
106
|
+
> so it reconstructs a different (much earlier) instant.
|
|
107
|
+
|
|
108
|
+
## Examples
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
>>> from fly_hour_timer import FlyHourTimer
|
|
112
|
+
>>> FlyHourTimer.hour_timer(0)
|
|
113
|
+
'0.0000'
|
|
114
|
+
>>> FlyHourTimer.hour_timer(1_000_000_000)
|
|
115
|
+
'277777.4640'
|
|
116
|
+
>>> FlyHourTimer.hour_timer_short(1_000_000_000)
|
|
117
|
+
'777.4640'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Run `python examples.py` in this repository for a live demo.
|
|
121
|
+
|
|
122
|
+
## Tests
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python tests/test_fly_hour_timer.py
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# fly_hour_timer
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **简体中文**
|
|
4
|
+
|
|
5
|
+
一个「无时区干扰」的 Python 时间库,用 **`<小时>.<分秒>`** 表示时间。
|
|
6
|
+
|
|
7
|
+
核心思想:用 `<小时>.<分秒>` 表达时间,其中**小时就是 `unix_time // 3600`**
|
|
8
|
+
(自 unix 纪元以来经过的整小时数),`<分秒>` 是该小时内的分钟和秒。因为一切都
|
|
9
|
+
直接由绝对的 unix 时间推导而来,所以这个字符串完全与时区无关,在任何机器、任何
|
|
10
|
+
地域得到的结果都一致——尤其适合在不同地域之间交换时间戳的 AI Bot 使用。
|
|
11
|
+
|
|
12
|
+
- 无第三方依赖,纯标准库(仅用到 `datetime` / `time`)
|
|
13
|
+
- 与生俱来的时区无关
|
|
14
|
+
- 支持 Python >= 3.8
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install fly_hour_timer
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
或从源码安装:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/fly-ttrr/fly_hour_timer.git
|
|
26
|
+
cd fly_hour_timer
|
|
27
|
+
pip install .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 快速开始
|
|
31
|
+
|
|
32
|
+
`fly_hour_timer` 是一个静态工具,无需创建或配置实例:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from fly_hour_timer import fly_hour_timer
|
|
36
|
+
|
|
37
|
+
fly_hour_timer.hour() # '277777' -> 小时(unix_time // 3600),str
|
|
38
|
+
fly_hour_timer.min_sec() # '4640' -> 分秒,str
|
|
39
|
+
fly_hour_timer.hour_timer() # '277777.4640' -> <小时>.<分秒>
|
|
40
|
+
fly_hour_timer.hour_timer_short() # '777.4640' -> <小时后三位>.<分秒>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
传入指定的 unix 时间(秒):
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from fly_hour_timer import FlyHourTimer
|
|
47
|
+
|
|
48
|
+
FlyHourTimer.hour_timer(1_000_000_000) # '277777.4640'
|
|
49
|
+
FlyHourTimer.hour_timer_short(1_000_000_000) # '777.4640'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
| 方法 | 说明 | 返回类型 |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `hour(unix_time=None)` | 自纪元以来的整小时数,即 `unix_time // 3600` | `str` |
|
|
57
|
+
| `min_sec(unix_time=None)` | 该小时内的分 + 秒拼接的 4 位(`'0102'` 表示 01 分 02 秒) | `str` |
|
|
58
|
+
| `hour_timer(unix_time=None)` | `<小时>.<分秒>` | `str` |
|
|
59
|
+
| `hour_timer_short(unix_time=None)` | `<小时后三位>.<分秒>` | `str` |
|
|
60
|
+
| `hour_timer_to_time(hour_timer)` | 将 `<小时>.<分秒>` 解析回 UTC 的 `time.struct_time` | `time.struct_time` |
|
|
61
|
+
|
|
62
|
+
### 参数说明
|
|
63
|
+
|
|
64
|
+
- **`unix_time`** —— unix 时间,单位为秒(`int` / `float` / 数字字符串)。
|
|
65
|
+
不传则使用当前时间。
|
|
66
|
+
|
|
67
|
+
## 原理
|
|
68
|
+
|
|
69
|
+
给定以秒为单位的 unix 时间:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
小时 = unix_time // 3600 # 自纪元以来的整小时数
|
|
73
|
+
余秒 = unix_time - 小时 * 3600 # 该小时内剩余的秒数(0..3599)
|
|
74
|
+
分秒 = "%02d%02d" % (余秒 // 60, 余秒 % 60)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `hour_timer()` -> `f"{小时}.{分秒}"`
|
|
78
|
+
- `hour_timer_short()` -> `f"{小时 % 1000:03d}.{分秒}"`——小时后三位大约每
|
|
79
|
+
1000 小时(约 41.7 天)滚动一轮,是一个紧凑、与时区无关的时间标识,适合用于
|
|
80
|
+
日志与 Bot 对话。
|
|
81
|
+
|
|
82
|
+
不需要任何时区参数:小时数与该小时内的分秒都直接来自绝对的 unix 时间,因此
|
|
83
|
+
无论在哪台机器、哪个时区,结果都完全一致。
|
|
84
|
+
|
|
85
|
+
## 逆向转换:`hour_timer_to_time()`
|
|
86
|
+
|
|
87
|
+
由于 `<小时>` 已经编码了自纪元以来的累计小时数(因而也包含了完整日期),逆向
|
|
88
|
+
转换是完备的,不需要「今天」作为锚点。它返回一个 UTC 的 `time.struct_time`:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from fly_hour_timer import FlyHourTimer
|
|
92
|
+
|
|
93
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
94
|
+
# time.struct_time(... 2001, 9, 9, 1, 46, 40 ...)
|
|
95
|
+
st.tm_hour, st.tm_min, st.tm_sec # (1, 46, 40)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
它对时钟字段而言是 `hour_timer()` 的精确逆运算;当输入格式非法或越界(例如
|
|
99
|
+
分钟 `>= 60`)时会抛出 `ValueError`。
|
|
100
|
+
|
|
101
|
+
> 注意:请传入**完整的** `hour_timer()` 字符串。像 `'777.4640'`(来自
|
|
102
|
+
> `hour_timer_short()`)这样的短字符串已经丢弃了高位小时数字,因此会重构成
|
|
103
|
+
> 一个不同的(更早的)时刻。
|
|
104
|
+
|
|
105
|
+
## 示例
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
>>> from fly_hour_timer import FlyHourTimer
|
|
109
|
+
>>> FlyHourTimer.hour_timer(0)
|
|
110
|
+
'0.0000'
|
|
111
|
+
>>> FlyHourTimer.hour_timer(1_000_000_000)
|
|
112
|
+
'277777.4640'
|
|
113
|
+
>>> FlyHourTimer.hour_timer_short(1_000_000_000)
|
|
114
|
+
'777.4640'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
在本仓库运行 `python examples.py` 可查看实时演示。
|
|
118
|
+
|
|
119
|
+
## 测试
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
python tests/test_fly_hour_timer.py
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 许可证
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""fly_hour_timer - a timezone-free time library: <hour>.<min_sec>"""
|
|
2
|
+
|
|
3
|
+
from .fly_hour_timer import FlyHourTimer
|
|
4
|
+
|
|
5
|
+
#: The static tool (alias of the :class:`FlyHourTimer` class). Call the methods
|
|
6
|
+
#: directly, e.g. ``fly_hour_timer.hour_timer()``.
|
|
7
|
+
fly_hour_timer = FlyHourTimer
|
|
8
|
+
|
|
9
|
+
__all__ = ["FlyHourTimer", "fly_hour_timer"]
|
|
10
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Core static utility of the fly_hour_timer library.
|
|
2
|
+
|
|
3
|
+
Time representation: <hour>.<min_sec>
|
|
4
|
+
|
|
5
|
+
The "hour" is simply ``unix_time // 3600`` -- the number of whole hours elapsed
|
|
6
|
+
since the unix epoch. ``<min_sec>`` is the minutes and seconds inside that
|
|
7
|
+
hour. Because everything is derived directly from the absolute unix time, the
|
|
8
|
+
string is fully timezone-free and identical on any machine anywhere.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import time as _time
|
|
12
|
+
from datetime import datetime, timezone as dt_timezone
|
|
13
|
+
|
|
14
|
+
__all__ = ["FlyHourTimer"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FlyHourTimer:
|
|
18
|
+
"""Static time tool: represents time as ``<hour>.<min_sec>``.
|
|
19
|
+
|
|
20
|
+
All methods are static; no instance is required.
|
|
21
|
+
|
|
22
|
+
>>> FlyHourTimer.hour_timer(1_000_000_000) # 2001-09-09 01:46:40 UTC
|
|
23
|
+
'277777.4640'
|
|
24
|
+
>>> FlyHourTimer.hour_timer_short(1_000_000_000)
|
|
25
|
+
'777.4640'
|
|
26
|
+
>>> FlyHourTimer.hour_timer(0) # the epoch
|
|
27
|
+
'0.0000'
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# ------------------------------------------------------------------
|
|
31
|
+
# public API
|
|
32
|
+
# ------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def hour(unix_time=None):
|
|
36
|
+
"""Return the hour: whole hours elapsed since the unix epoch.
|
|
37
|
+
|
|
38
|
+
Equivalent to ``unix_time // 3600``.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
unix_time: unix time in seconds (int/float/numeric str).
|
|
42
|
+
Defaults to the current time when omitted.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: e.g. ``'277777'``.
|
|
46
|
+
"""
|
|
47
|
+
hours, _, _ = FlyHourTimer._split(unix_time)
|
|
48
|
+
return str(hours)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def min_sec(unix_time=None):
|
|
52
|
+
"""Return the ``<min_sec>`` part: minutes and seconds inside the hour.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
unix_time: unix time in seconds, defaults to the current time.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: 4 digits ``MMSS``, e.g. ``'0102'`` means 01 min 02 sec.
|
|
59
|
+
"""
|
|
60
|
+
_, minute, second = FlyHourTimer._split(unix_time)
|
|
61
|
+
return f"{minute:02d}{second:02d}"
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def hour_timer(unix_time=None):
|
|
65
|
+
"""Return time as ``<hour>.<min_sec>``, where hour = ``unix_time // 3600``.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
unix_time: unix time in seconds, defaults to the current time.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: e.g. ``'277777.4640'``.
|
|
72
|
+
"""
|
|
73
|
+
hours, minute, second = FlyHourTimer._split(unix_time)
|
|
74
|
+
return f"{hours}.{minute:02d}{second:02d}"
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def hour_timer_short(unix_time=None):
|
|
78
|
+
"""Return time as ``<last 3 digits of hour>.<min_sec>``.
|
|
79
|
+
|
|
80
|
+
The last 3 digits of the hour roll over roughly every 1000 hours
|
|
81
|
+
(~41.7 days), giving a compact, timezone-free time id for logs and
|
|
82
|
+
bot conversations.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
unix_time: unix time in seconds, defaults to the current time.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
str: e.g. hour ``277777`` -> ``'777.4640'``.
|
|
89
|
+
"""
|
|
90
|
+
hours, minute, second = FlyHourTimer._split(unix_time)
|
|
91
|
+
return f"{hours % 1000:03d}.{minute:02d}{second:02d}"
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def hour_timer_to_time(hour_timer):
|
|
95
|
+
"""Convert a ``<hour>.<min_sec>`` string into a ``time.struct_time``.
|
|
96
|
+
|
|
97
|
+
Because ``<hour>`` already encodes the total elapsed hours since the
|
|
98
|
+
epoch (and therefore the date), this is a complete, timezone-free
|
|
99
|
+
inverse of :meth:`hour_timer` -- no "today" anchor is needed. The
|
|
100
|
+
result is UTC based (``time.gmtime``).
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
hour_timer: the ``<hour>.<min_sec>`` string, e.g.
|
|
104
|
+
``'277777.4640'``.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
time.struct_time: UTC struct_time reconstructed from the string.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
ValueError: if ``hour_timer`` is malformed or out of range.
|
|
111
|
+
"""
|
|
112
|
+
hours, minute, second = FlyHourTimer._parse_hour_timer(hour_timer)
|
|
113
|
+
unix_time = hours * 3600 + minute * 60 + second
|
|
114
|
+
return _time.gmtime(unix_time)
|
|
115
|
+
|
|
116
|
+
# ------------------------------------------------------------------
|
|
117
|
+
# internals
|
|
118
|
+
# ------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _split(unix_time):
|
|
122
|
+
"""Split a unix time into (total_hours, minute, second)."""
|
|
123
|
+
if unix_time is None:
|
|
124
|
+
ts = datetime.now(dt_timezone.utc).timestamp()
|
|
125
|
+
else:
|
|
126
|
+
ts = float(unix_time)
|
|
127
|
+
hours = int(ts // 3600)
|
|
128
|
+
rem = ts - hours * 3600 # seconds inside the hour, always in [0, 3600)
|
|
129
|
+
minute = int(rem // 60)
|
|
130
|
+
second = int(rem % 60)
|
|
131
|
+
return hours, minute, second
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def _parse_hour_timer(hour_timer):
|
|
135
|
+
"""Parse ``'<hour>.<min_sec>'`` into (total_hours, minute, second)."""
|
|
136
|
+
if not isinstance(hour_timer, str):
|
|
137
|
+
raise ValueError(f"hour_timer must be a str, got {hour_timer!r}")
|
|
138
|
+
text = hour_timer.strip()
|
|
139
|
+
if text.count(".") != 1:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"hour_timer must look like '<hour>.<min_sec>', got {hour_timer!r}"
|
|
142
|
+
)
|
|
143
|
+
hour_part, min_sec_part = text.split(".", 1)
|
|
144
|
+
if not min_sec_part.isdigit() or len(min_sec_part) != 4:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"hour_timer '<min_sec>' must be 4 digits, got {hour_timer!r}"
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
hours = int(hour_part)
|
|
150
|
+
except ValueError:
|
|
151
|
+
raise ValueError(
|
|
152
|
+
f"hour_timer '<hour>' must be an integer, got {hour_timer!r}"
|
|
153
|
+
) from None
|
|
154
|
+
minute, second = int(min_sec_part[:2]), int(min_sec_part[2:])
|
|
155
|
+
if not (0 <= minute <= 59 and 0 <= second <= 59):
|
|
156
|
+
raise ValueError(f"hour_timer '<min_sec>' out of range, got {hour_timer!r}")
|
|
157
|
+
return hours, minute, second
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fly_hour_timer
|
|
3
|
+
Version: 1.1.3
|
|
4
|
+
Summary: A timezone-free Python time library: represents time as <hour>.<min_sec>
|
|
5
|
+
Author: fly
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: time,timezone-free,hour-timer,ai-bot,unix-time
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# fly_hour_timer
|
|
19
|
+
|
|
20
|
+
**English** | [简体中文](README_zh.md)
|
|
21
|
+
|
|
22
|
+
A timezone-free Python time library that represents time as **`<hour>.<min_sec>`**.
|
|
23
|
+
|
|
24
|
+
The core idea: express time as `<hour>.<min_sec>`, where the **hour is simply
|
|
25
|
+
`unix_time // 3600`** (whole hours elapsed since the unix epoch) and `<min_sec>`
|
|
26
|
+
is the minutes and seconds inside that hour. Because everything is derived
|
|
27
|
+
directly from the absolute unix time, the string is fully timezone-free and
|
|
28
|
+
identical on any machine anywhere — especially friendly for AI bots that
|
|
29
|
+
exchange timestamps across regions.
|
|
30
|
+
|
|
31
|
+
- No third-party dependency, pure standard library (`datetime` / `time` only)
|
|
32
|
+
- Fully timezone-free by construction
|
|
33
|
+
- Python >= 3.8
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install fly_hour_timer
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/fly-ttrr/fly_hour_timer.git
|
|
45
|
+
cd fly_hour_timer
|
|
46
|
+
pip install .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
`fly_hour_timer` is a static tool — no instance configuration required:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fly_hour_timer import fly_hour_timer
|
|
55
|
+
|
|
56
|
+
fly_hour_timer.hour() # '277777' -> hour (unix_time // 3600), str
|
|
57
|
+
fly_hour_timer.min_sec() # '4640' -> minutes + seconds, str
|
|
58
|
+
fly_hour_timer.hour_timer() # '277777.4640' -> <hour>.<min_sec>
|
|
59
|
+
fly_hour_timer.hour_timer_short() # '777.4640' -> <last 3 digits of hour>.<min_sec>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
With an explicit unix time (seconds):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from fly_hour_timer import FlyHourTimer
|
|
66
|
+
|
|
67
|
+
FlyHourTimer.hour_timer(1_000_000_000) # '277777.4640'
|
|
68
|
+
FlyHourTimer.hour_timer_short(1_000_000_000) # '777.4640'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
| Method | Description | Returns |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| `hour(unix_time=None)` | Whole hours since the epoch, `unix_time // 3600` | `str` |
|
|
76
|
+
| `min_sec(unix_time=None)` | Minutes + seconds inside the hour, 4 digits (`'0102'` = 01m02s) | `str` |
|
|
77
|
+
| `hour_timer(unix_time=None)` | `<hour>.<min_sec>` | `str` |
|
|
78
|
+
| `hour_timer_short(unix_time=None)` | `<last 3 digits of hour>.<min_sec>` | `str` |
|
|
79
|
+
| `hour_timer_to_time(hour_timer)` | Parse `<hour>.<min_sec>` back into a UTC `time.struct_time` | `time.struct_time` |
|
|
80
|
+
|
|
81
|
+
### Parameters
|
|
82
|
+
|
|
83
|
+
- **`unix_time`** — unix time in seconds (`int` / `float` / numeric `str`).
|
|
84
|
+
Omit to use the current time.
|
|
85
|
+
|
|
86
|
+
## How it works
|
|
87
|
+
|
|
88
|
+
Given a unix time in seconds:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
hour = unix_time // 3600 # whole hours since the epoch
|
|
92
|
+
rem = unix_time - hour * 3600 # seconds left inside the hour (0..3599)
|
|
93
|
+
min_sec = "%02d%02d" % (rem // 60, rem % 60)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- `hour_timer()` -> `f"{hour}.{min_sec}"`
|
|
97
|
+
- `hour_timer_short()` -> `f"{hour % 1000:03d}.{min_sec}"` — the last 3 digits
|
|
98
|
+
of the hour roll over roughly every 1000 hours (~41.7 days), giving a compact,
|
|
99
|
+
timezone-free time id for logs and bot conversations.
|
|
100
|
+
|
|
101
|
+
No timezone parameter is needed: the hour count and the leftover minutes/seconds
|
|
102
|
+
come straight from the absolute unix time, so the result is the same everywhere.
|
|
103
|
+
|
|
104
|
+
## Reverse Conversion: `hour_timer_to_time()`
|
|
105
|
+
|
|
106
|
+
Since `<hour>` already encodes the total elapsed hours since the epoch (and thus
|
|
107
|
+
the full date), the conversion back is complete and needs no "today" anchor. It
|
|
108
|
+
returns a UTC `time.struct_time`:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from fly_hour_timer import FlyHourTimer
|
|
112
|
+
|
|
113
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
114
|
+
# time.struct_time(... 2001, 9, 9, 1, 46, 40 ...)
|
|
115
|
+
st.tm_hour, st.tm_min, st.tm_sec # (1, 46, 40)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
It is the exact inverse of `hour_timer()` for the clock fields, and raises
|
|
119
|
+
`ValueError` on malformed or out-of-range input (e.g. minutes `>= 60`).
|
|
120
|
+
|
|
121
|
+
> Note: pass the **full** `hour_timer()` string here. A short string like
|
|
122
|
+
> `'777.4640'` (from `hour_timer_short()`) has lost the high-order hour digits,
|
|
123
|
+
> so it reconstructs a different (much earlier) instant.
|
|
124
|
+
|
|
125
|
+
## Examples
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
>>> from fly_hour_timer import FlyHourTimer
|
|
129
|
+
>>> FlyHourTimer.hour_timer(0)
|
|
130
|
+
'0.0000'
|
|
131
|
+
>>> FlyHourTimer.hour_timer(1_000_000_000)
|
|
132
|
+
'277777.4640'
|
|
133
|
+
>>> FlyHourTimer.hour_timer_short(1_000_000_000)
|
|
134
|
+
'777.4640'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Run `python examples.py` in this repository for a live demo.
|
|
138
|
+
|
|
139
|
+
## Tests
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python tests/test_fly_hour_timer.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
README_zh.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
fly_hour_timer/__init__.py
|
|
5
|
+
fly_hour_timer/fly_hour_timer.py
|
|
6
|
+
fly_hour_timer.egg-info/PKG-INFO
|
|
7
|
+
fly_hour_timer.egg-info/SOURCES.txt
|
|
8
|
+
fly_hour_timer.egg-info/dependency_links.txt
|
|
9
|
+
fly_hour_timer.egg-info/top_level.txt
|
|
10
|
+
tests/test_fly_hour_timer.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fly_hour_timer
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fly_hour_timer"
|
|
7
|
+
version = "1.1.3"
|
|
8
|
+
description = "A timezone-free Python time library: represents time as <hour>.<min_sec>"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["time", "timezone-free", "hour-timer", "ai-bot", "unix-time"]
|
|
13
|
+
authors = [{ name = "fly" }]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
21
|
+
"Topic :: Utilities",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
addopts = "-p no:dash"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools]
|
|
28
|
+
packages = ["fly_hour_timer"]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.data-files]
|
|
31
|
+
"share/fly_hour_timer" = ["README_zh.md"]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Unit tests for fly_hour_timer (no third-party dependency needed).
|
|
2
|
+
|
|
3
|
+
Run: python tests/test_fly_hour_timer.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import time
|
|
9
|
+
import unittest
|
|
10
|
+
from datetime import datetime, timezone as dt_timezone
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
13
|
+
|
|
14
|
+
from fly_hour_timer import FlyHourTimer, fly_hour_timer
|
|
15
|
+
|
|
16
|
+
# 2001-09-09 01:46:40 UTC -> 1_000_000_000 // 3600 == 277777 hours
|
|
17
|
+
UNIX_911 = 1_000_000_000
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestStaticMethods(unittest.TestCase):
|
|
21
|
+
def test_hour(self):
|
|
22
|
+
self.assertEqual(FlyHourTimer.hour(UNIX_911), "277777")
|
|
23
|
+
|
|
24
|
+
def test_hour_epoch(self):
|
|
25
|
+
self.assertEqual(FlyHourTimer.hour(0), "0")
|
|
26
|
+
|
|
27
|
+
def test_min_sec(self):
|
|
28
|
+
self.assertEqual(FlyHourTimer.min_sec(UNIX_911), "4640")
|
|
29
|
+
|
|
30
|
+
def test_min_sec_padding(self):
|
|
31
|
+
# 1970-01-01 00:01:02 UTC -> 01 min 02 sec
|
|
32
|
+
self.assertEqual(FlyHourTimer.min_sec(62), "0102")
|
|
33
|
+
|
|
34
|
+
def test_hour_timer(self):
|
|
35
|
+
self.assertEqual(FlyHourTimer.hour_timer(UNIX_911), "277777.4640")
|
|
36
|
+
|
|
37
|
+
def test_hour_timer_epoch(self):
|
|
38
|
+
self.assertEqual(FlyHourTimer.hour_timer(0), "0.0000")
|
|
39
|
+
|
|
40
|
+
def test_hour_timer_short(self):
|
|
41
|
+
self.assertEqual(FlyHourTimer.hour_timer_short(UNIX_911), "777.4640")
|
|
42
|
+
|
|
43
|
+
def test_hour_timer_short_epoch(self):
|
|
44
|
+
self.assertEqual(FlyHourTimer.hour_timer_short(0), "000.0000")
|
|
45
|
+
|
|
46
|
+
def test_hour_matches_division(self):
|
|
47
|
+
# the hour is exactly unix_time // 3600, timezone-free
|
|
48
|
+
for ts in (0, 3599, 3600, 7199, UNIX_911, 1_700_000_000):
|
|
49
|
+
self.assertEqual(
|
|
50
|
+
FlyHourTimer.hour(ts), str(ts // 3600)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def test_min_sec_matches_remainder(self):
|
|
54
|
+
# min_sec reflects the seconds left over after whole hours
|
|
55
|
+
for ts in (62, 3599, UNIX_911):
|
|
56
|
+
rem = ts - (ts // 3600) * 3600
|
|
57
|
+
expected = f"{rem // 60:02d}{rem % 60:02d}"
|
|
58
|
+
self.assertEqual(FlyHourTimer.min_sec(ts), expected)
|
|
59
|
+
|
|
60
|
+
def test_unix_time_float_and_str(self):
|
|
61
|
+
self.assertEqual(FlyHourTimer.hour_timer(1_000_000_000.0), "277777.4640")
|
|
62
|
+
self.assertEqual(FlyHourTimer.hour_timer("1000000000"), "277777.4640")
|
|
63
|
+
|
|
64
|
+
def test_default_returns_str(self):
|
|
65
|
+
for value in (
|
|
66
|
+
FlyHourTimer.hour(),
|
|
67
|
+
FlyHourTimer.min_sec(),
|
|
68
|
+
FlyHourTimer.hour_timer(),
|
|
69
|
+
FlyHourTimer.hour_timer_short(),
|
|
70
|
+
):
|
|
71
|
+
self.assertIsInstance(value, str)
|
|
72
|
+
|
|
73
|
+
def test_output_matches_current_time(self):
|
|
74
|
+
expected = FlyHourTimer.hour_timer(
|
|
75
|
+
int(datetime.now(dt_timezone.utc).timestamp())
|
|
76
|
+
)
|
|
77
|
+
actual = FlyHourTimer.hour_timer()
|
|
78
|
+
# allow the whole-second boundary to differ between the two reads
|
|
79
|
+
self.assertEqual(expected.split(".")[0], actual.split(".")[0])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestHourTimerToTime(unittest.TestCase):
|
|
83
|
+
def test_returns_struct_time(self):
|
|
84
|
+
self.assertIsInstance(
|
|
85
|
+
FlyHourTimer.hour_timer_to_time("277777.4640"), time.struct_time
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def test_full_round_trip(self):
|
|
89
|
+
# hour_timer -> hour_timer_to_time reconstructs the exact UTC struct
|
|
90
|
+
st = FlyHourTimer.hour_timer_to_time(FlyHourTimer.hour_timer(UNIX_911))
|
|
91
|
+
self.assertEqual(st, time.gmtime(UNIX_911))
|
|
92
|
+
|
|
93
|
+
def test_fields(self):
|
|
94
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
95
|
+
self.assertEqual(
|
|
96
|
+
(st.tm_year, st.tm_mon, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec),
|
|
97
|
+
(2001, 9, 9, 1, 46, 40),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def test_epoch(self):
|
|
101
|
+
self.assertEqual(FlyHourTimer.hour_timer_to_time("0.0000"), time.gmtime(0))
|
|
102
|
+
|
|
103
|
+
def test_encodes_date_not_today(self):
|
|
104
|
+
# the hour number carries the date, so it is NOT anchored to today
|
|
105
|
+
st = FlyHourTimer.hour_timer_to_time("277777.4640")
|
|
106
|
+
self.assertEqual(st.tm_year, 2001)
|
|
107
|
+
|
|
108
|
+
def test_invalid_format_raises(self):
|
|
109
|
+
for bad in ("", "12", "12.345", "12.34567", "ab.cd", None,
|
|
110
|
+
"1.6060", "1.-1.23", "12.34 56"):
|
|
111
|
+
with self.assertRaises(ValueError):
|
|
112
|
+
FlyHourTimer.hour_timer_to_time(bad)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TestStaticAlias(unittest.TestCase):
|
|
116
|
+
def test_alias_is_class(self):
|
|
117
|
+
self.assertIs(fly_hour_timer, FlyHourTimer)
|
|
118
|
+
|
|
119
|
+
def test_alias_call(self):
|
|
120
|
+
self.assertEqual(fly_hour_timer.hour_timer(UNIX_911), "277777.4640")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
unittest.main(verbosity=2)
|