pymillis 1.0.0__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.
pymillis-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-PRESENT WuChenDi<https://github.com/WuChenDi>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: pymillis
3
+ Version: 1.0.0
4
+ Summary: Use this package to easily convert various time formats to milliseconds.
5
+ Keywords: time,milliseconds,ms,duration,conversion
6
+ Author-email: wudi <wuchendi96@gmail.com>
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ License-File: LICENSE
23
+ Project-URL: Bug Reports, https://github.com/WuChenDi/pymillis/issues
24
+ Project-URL: Source, https://github.com/WuChenDi/pymillis
25
+
26
+ # pymillis
27
+
28
+ Use this package to easily convert various time formats to milliseconds.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install pymillis
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Basic Usage
39
+
40
+ ```python
41
+ from pymillis import ms
42
+
43
+ # Parse time strings to milliseconds
44
+ ms('2 days') # 172800000
45
+ ms('1d') # 86400000
46
+ ms('10h') # 36000000
47
+ ms('2.5 hrs') # 9000000
48
+ ms('2h') # 7200000
49
+ ms('1m') # 60000
50
+ ms('5s') # 5000
51
+ ms('1y') # 31557600000
52
+ ms('100') # 100
53
+ ms('-3 days') # -259200000
54
+ ms('-1h') # -3600000
55
+ ms('-200') # -200
56
+
57
+ # Format milliseconds to strings
58
+ ms(60000) # '1m'
59
+ ms(2 * 60000) # '2m'
60
+ ms(-3 * 60000) # '-3m'
61
+ ms(172800000) # '2d'
62
+
63
+ # Use long format
64
+ ms(60000, long=True) # '1 minute'
65
+ ms(2 * 60000, long=True) # '2 minutes'
66
+ ms(172800000, long=True) # '2 days'
67
+ ms(ms('10 hours'), long=True) # '10 hours'
68
+ ```
69
+
70
+ ### API
71
+
72
+ #### `ms(value, *, long=False)`
73
+
74
+ Parse or format the given value.
75
+
76
+ **Parameters:**
77
+ - `value` (str | int | float): The string or number to convert
78
+ - `long` (bool, optional): Set to `True` to use verbose formatting. Defaults to `False`.
79
+
80
+ **Returns:**
81
+ - If `value` is a string, returns milliseconds as `float`
82
+ - If `value` is a number, returns formatted string as `str`
83
+
84
+ **Raises:**
85
+ - `MSError`: If value is not a non-empty string or a number
86
+
87
+ #### `parse(value)`
88
+
89
+ Parse the given string and return milliseconds.
90
+
91
+ **Parameters:**
92
+ - `value` (str): A string to parse to milliseconds
93
+
94
+ **Returns:**
95
+ - `float`: The parsed value in milliseconds
96
+
97
+ **Raises:**
98
+ - `MSError`: If the string is invalid or cannot be parsed
99
+
100
+ #### `format(ms_value, *, long=False)`
101
+
102
+ Format the given milliseconds as a string.
103
+
104
+ **Parameters:**
105
+ - `ms_value` (int | float): Milliseconds to format
106
+ - `long` (bool, optional): Use verbose formatting if `True`
107
+
108
+ **Returns:**
109
+ - `str`: The formatted string
110
+
111
+ **Raises:**
112
+ - `MSError`: If ms_value is not a finite number
113
+
114
+ ### Import Options
115
+
116
+ ```python
117
+ # Import main function
118
+ from pymillis import ms
119
+
120
+ # Import specific functions
121
+ from pymillis import parse, format, parse_strict
122
+
123
+ # Import exception
124
+ from pymillis import MSError
125
+
126
+ # Import everything
127
+ from pymillis import ms, parse, format, MSError
128
+ ```
129
+
130
+ ## Supported Time Units
131
+
132
+ ### Short Format
133
+
134
+ - `ms`, `msec`, `msecs`, `millisecond`, `milliseconds` - Milliseconds
135
+ - `s`, `sec`, `secs`, `second`, `seconds` - Seconds
136
+ - `m`, `min`, `mins`, `minute`, `minutes` - Minutes
137
+ - `h`, `hr`, `hrs`, `hour`, `hours` - Hours
138
+ - `d`, `day`, `days` - Days
139
+ - `w`, `week`, `weeks` - Weeks
140
+ - `mo`, `month`, `months` - Months (calculated as 1/12 of a year)
141
+ - `y`, `yr`, `yrs`, `year`, `years` - Years (calculated as 365.25 days)
142
+
143
+ ### Case Insensitive
144
+
145
+ All units are case-insensitive, so `1D`, `1d`, `1 Day`, `1 DAY` are all equivalent.
146
+
147
+ ## Features
148
+
149
+ - 🚀 Simple and intuitive API
150
+ - 📦 Zero dependencies
151
+ - 🔄 Bidirectional conversion (string ↔ milliseconds)
152
+ - ⏱️ Supports negative time values
153
+ - 📝 Long and short format options
154
+ - 🎯 Type hints for better IDE support
155
+ - ✅ Comprehensive error handling
156
+
157
+ ## Common Use Cases
158
+
159
+ ### Setting Timeouts
160
+
161
+ ```python
162
+ import time
163
+ from pymillis import ms
164
+
165
+ # Convert to seconds for time.sleep()
166
+ timeout = ms('5s') / 1000
167
+ time.sleep(timeout)
168
+ ```
169
+
170
+ ### Caching
171
+
172
+ ```python
173
+ import time
174
+ from pymillis import ms
175
+
176
+ # Set cache expiration
177
+ cache_duration = ms('1h')
178
+ expires_at = time.time() * 1000 + cache_duration
179
+ ```
180
+
181
+ ### Rate Limiting
182
+
183
+ ```python
184
+ from pymillis import ms
185
+
186
+ # Define rate limit window
187
+ rate_limit_window = ms('1m')
188
+ max_requests = 100
189
+ ```
190
+
191
+ ### Calculating Durations
192
+
193
+ ```python
194
+ from pymillis import ms
195
+
196
+ # Calculate time differences
197
+ meeting_duration = ms('2h') - ms('30m') # 5400000.0 ms (1.5 hours)
198
+ ```
199
+
200
+ ## Error Handling
201
+
202
+ The library raises `MSError` for invalid inputs:
203
+
204
+ ```python
205
+ from pymillis import ms, MSError
206
+
207
+ # Invalid format
208
+ try:
209
+ ms('invalid')
210
+ except MSError as e:
211
+ print(f"Error: {e}")
212
+
213
+ # Non-finite number
214
+ try:
215
+ ms(float('nan'))
216
+ except MSError as e:
217
+ print(f"Error: {e}")
218
+
219
+ # Empty string
220
+ try:
221
+ ms('')
222
+ except MSError as e:
223
+ print(f"Error: {e}")
224
+
225
+ # String too long (>100 characters)
226
+ try:
227
+ ms('a' * 101)
228
+ except MSError as e:
229
+ print(f"Error: {e}")
230
+ ```
231
+
232
+ ## Notes
233
+
234
+ ### Precision
235
+
236
+ - Results are returned as `float` for precision
237
+ - **Month calculation**: 1 month = 1/12 year ≈ 30.44 days (average value)
238
+ - **Year calculation**: 1 year = 365.25 days (accounting for leap years)
239
+
240
+ ### Rounding
241
+
242
+ When formatting, values are rounded to the nearest integer for the selected unit:
243
+
244
+ ```python
245
+ ms(1500) # '2s' (rounded from 1.5s)
246
+ ms(90000) # '2m' (rounded from 1.5m)
247
+ ```
248
+
249
+ ## 📜 License
250
+
251
+ [MIT](./LICENSE) License &copy; 2025-PRESENT [wudi](https://github.com/WuChenDi)
252
+
@@ -0,0 +1,226 @@
1
+ # pymillis
2
+
3
+ Use this package to easily convert various time formats to milliseconds.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install pymillis
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Usage
14
+
15
+ ```python
16
+ from pymillis import ms
17
+
18
+ # Parse time strings to milliseconds
19
+ ms('2 days') # 172800000
20
+ ms('1d') # 86400000
21
+ ms('10h') # 36000000
22
+ ms('2.5 hrs') # 9000000
23
+ ms('2h') # 7200000
24
+ ms('1m') # 60000
25
+ ms('5s') # 5000
26
+ ms('1y') # 31557600000
27
+ ms('100') # 100
28
+ ms('-3 days') # -259200000
29
+ ms('-1h') # -3600000
30
+ ms('-200') # -200
31
+
32
+ # Format milliseconds to strings
33
+ ms(60000) # '1m'
34
+ ms(2 * 60000) # '2m'
35
+ ms(-3 * 60000) # '-3m'
36
+ ms(172800000) # '2d'
37
+
38
+ # Use long format
39
+ ms(60000, long=True) # '1 minute'
40
+ ms(2 * 60000, long=True) # '2 minutes'
41
+ ms(172800000, long=True) # '2 days'
42
+ ms(ms('10 hours'), long=True) # '10 hours'
43
+ ```
44
+
45
+ ### API
46
+
47
+ #### `ms(value, *, long=False)`
48
+
49
+ Parse or format the given value.
50
+
51
+ **Parameters:**
52
+ - `value` (str | int | float): The string or number to convert
53
+ - `long` (bool, optional): Set to `True` to use verbose formatting. Defaults to `False`.
54
+
55
+ **Returns:**
56
+ - If `value` is a string, returns milliseconds as `float`
57
+ - If `value` is a number, returns formatted string as `str`
58
+
59
+ **Raises:**
60
+ - `MSError`: If value is not a non-empty string or a number
61
+
62
+ #### `parse(value)`
63
+
64
+ Parse the given string and return milliseconds.
65
+
66
+ **Parameters:**
67
+ - `value` (str): A string to parse to milliseconds
68
+
69
+ **Returns:**
70
+ - `float`: The parsed value in milliseconds
71
+
72
+ **Raises:**
73
+ - `MSError`: If the string is invalid or cannot be parsed
74
+
75
+ #### `format(ms_value, *, long=False)`
76
+
77
+ Format the given milliseconds as a string.
78
+
79
+ **Parameters:**
80
+ - `ms_value` (int | float): Milliseconds to format
81
+ - `long` (bool, optional): Use verbose formatting if `True`
82
+
83
+ **Returns:**
84
+ - `str`: The formatted string
85
+
86
+ **Raises:**
87
+ - `MSError`: If ms_value is not a finite number
88
+
89
+ ### Import Options
90
+
91
+ ```python
92
+ # Import main function
93
+ from pymillis import ms
94
+
95
+ # Import specific functions
96
+ from pymillis import parse, format, parse_strict
97
+
98
+ # Import exception
99
+ from pymillis import MSError
100
+
101
+ # Import everything
102
+ from pymillis import ms, parse, format, MSError
103
+ ```
104
+
105
+ ## Supported Time Units
106
+
107
+ ### Short Format
108
+
109
+ - `ms`, `msec`, `msecs`, `millisecond`, `milliseconds` - Milliseconds
110
+ - `s`, `sec`, `secs`, `second`, `seconds` - Seconds
111
+ - `m`, `min`, `mins`, `minute`, `minutes` - Minutes
112
+ - `h`, `hr`, `hrs`, `hour`, `hours` - Hours
113
+ - `d`, `day`, `days` - Days
114
+ - `w`, `week`, `weeks` - Weeks
115
+ - `mo`, `month`, `months` - Months (calculated as 1/12 of a year)
116
+ - `y`, `yr`, `yrs`, `year`, `years` - Years (calculated as 365.25 days)
117
+
118
+ ### Case Insensitive
119
+
120
+ All units are case-insensitive, so `1D`, `1d`, `1 Day`, `1 DAY` are all equivalent.
121
+
122
+ ## Features
123
+
124
+ - 🚀 Simple and intuitive API
125
+ - 📦 Zero dependencies
126
+ - 🔄 Bidirectional conversion (string ↔ milliseconds)
127
+ - ⏱️ Supports negative time values
128
+ - 📝 Long and short format options
129
+ - 🎯 Type hints for better IDE support
130
+ - ✅ Comprehensive error handling
131
+
132
+ ## Common Use Cases
133
+
134
+ ### Setting Timeouts
135
+
136
+ ```python
137
+ import time
138
+ from pymillis import ms
139
+
140
+ # Convert to seconds for time.sleep()
141
+ timeout = ms('5s') / 1000
142
+ time.sleep(timeout)
143
+ ```
144
+
145
+ ### Caching
146
+
147
+ ```python
148
+ import time
149
+ from pymillis import ms
150
+
151
+ # Set cache expiration
152
+ cache_duration = ms('1h')
153
+ expires_at = time.time() * 1000 + cache_duration
154
+ ```
155
+
156
+ ### Rate Limiting
157
+
158
+ ```python
159
+ from pymillis import ms
160
+
161
+ # Define rate limit window
162
+ rate_limit_window = ms('1m')
163
+ max_requests = 100
164
+ ```
165
+
166
+ ### Calculating Durations
167
+
168
+ ```python
169
+ from pymillis import ms
170
+
171
+ # Calculate time differences
172
+ meeting_duration = ms('2h') - ms('30m') # 5400000.0 ms (1.5 hours)
173
+ ```
174
+
175
+ ## Error Handling
176
+
177
+ The library raises `MSError` for invalid inputs:
178
+
179
+ ```python
180
+ from pymillis import ms, MSError
181
+
182
+ # Invalid format
183
+ try:
184
+ ms('invalid')
185
+ except MSError as e:
186
+ print(f"Error: {e}")
187
+
188
+ # Non-finite number
189
+ try:
190
+ ms(float('nan'))
191
+ except MSError as e:
192
+ print(f"Error: {e}")
193
+
194
+ # Empty string
195
+ try:
196
+ ms('')
197
+ except MSError as e:
198
+ print(f"Error: {e}")
199
+
200
+ # String too long (>100 characters)
201
+ try:
202
+ ms('a' * 101)
203
+ except MSError as e:
204
+ print(f"Error: {e}")
205
+ ```
206
+
207
+ ## Notes
208
+
209
+ ### Precision
210
+
211
+ - Results are returned as `float` for precision
212
+ - **Month calculation**: 1 month = 1/12 year ≈ 30.44 days (average value)
213
+ - **Year calculation**: 1 year = 365.25 days (accounting for leap years)
214
+
215
+ ### Rounding
216
+
217
+ When formatting, values are rounded to the nearest integer for the selected unit:
218
+
219
+ ```python
220
+ ms(1500) # '2s' (rounded from 1.5s)
221
+ ms(90000) # '2m' (rounded from 1.5m)
222
+ ```
223
+
224
+ ## 📜 License
225
+
226
+ [MIT](./LICENSE) License &copy; 2025-PRESENT [wudi](https://github.com/WuChenDi)
@@ -0,0 +1,20 @@
1
+ """
2
+ pymillis - Milliseconds conversion utility
3
+
4
+ A Python port of the popular JavaScript ms library.
5
+ Convert between milliseconds and human-readable time strings.
6
+
7
+ Usage:
8
+ >>> from pymillis import ms
9
+ >>> ms('2 days')
10
+ 172800000
11
+ >>> ms(172800000)
12
+ '2d'
13
+ >>> ms(172800000, long=True)
14
+ '2 days'
15
+ """
16
+
17
+ from .ms import ms, parse, parse_strict, format, MSError
18
+ from ._version import __version__
19
+
20
+ __all__ = ['ms', 'parse', 'parse_strict', 'format', 'MSError', '__version__']
@@ -0,0 +1,3 @@
1
+ """Version information for pymillis package."""
2
+
3
+ __version__ = '1.0.0'
@@ -0,0 +1,223 @@
1
+ """
2
+ pymillis.ms - Core milliseconds conversion functionality
3
+ """
4
+
5
+ import re
6
+ import math
7
+ from typing import Union
8
+
9
+ __all__ = ['ms', 'parse', 'parse_strict', 'format', 'MSError']
10
+
11
+ # Time constants in milliseconds
12
+ S = 1000
13
+ M = S * 60
14
+ H = M * 60
15
+ D = H * 24
16
+ W = D * 7
17
+ Y = D * 365.25
18
+ MO = Y / 12
19
+
20
+
21
+ class MSError(Exception):
22
+ """Base exception for ms library errors."""
23
+ pass
24
+
25
+
26
+ def ms(value: Union[str, int, float], *, long: bool = False) -> Union[float, str]:
27
+ """
28
+ Parse or format the given value.
29
+
30
+ Args:
31
+ value: The string or number to convert
32
+ long: Set to True to use verbose formatting. Defaults to False.
33
+
34
+ Returns:
35
+ If value is a string, returns milliseconds as number (float).
36
+ If value is a number, returns formatted string.
37
+
38
+ Raises:
39
+ MSError: If value is not a non-empty string or a number
40
+
41
+ Examples:
42
+ >>> ms('2 days')
43
+ 172800000.0
44
+ >>> ms(172800000)
45
+ '2d'
46
+ >>> ms(172800000, long=True)
47
+ '2 days'
48
+ """
49
+ if isinstance(value, str):
50
+ return parse(value)
51
+ elif isinstance(value, (int, float)):
52
+ return format(value, long=long)
53
+ else:
54
+ raise MSError(
55
+ f"Value provided to ms() must be a string or number. value={repr(value)}"
56
+ )
57
+
58
+
59
+ def parse(value: str) -> float:
60
+ """
61
+ Parse the given string and return milliseconds.
62
+
63
+ Args:
64
+ value: A string to parse to milliseconds
65
+
66
+ Returns:
67
+ The parsed value in milliseconds, or raises MSError if invalid
68
+
69
+ Raises:
70
+ MSError: If the string is invalid or cannot be parsed
71
+
72
+ Examples:
73
+ >>> parse('2d')
74
+ 172800000.0
75
+ >>> parse('1.5 hours')
76
+ 5400000.0
77
+ >>> parse('1y')
78
+ 31557600000.0
79
+ """
80
+ if not isinstance(value, str):
81
+ raise MSError(
82
+ f"Value provided to ms.parse() must be a string. value={repr(value)}"
83
+ )
84
+
85
+ if len(value) == 0 or len(value) > 100:
86
+ raise MSError(
87
+ f"Value provided to ms.parse() must be a string with length between 1 and 99. value={repr(value)}"
88
+ )
89
+
90
+ pattern = r'^(?P<value>-?\d*\.?\d+)\s*(?P<unit>milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)?$'
91
+ match = re.match(pattern, value, re.IGNORECASE)
92
+
93
+ if not match:
94
+ raise MSError(f"Invalid time string format. value={repr(value)}")
95
+
96
+ groups = match.groupdict()
97
+ num_value = float(groups['value'])
98
+ unit = (groups['unit'] or 'ms').lower()
99
+
100
+ # Years
101
+ if unit in ('years', 'year', 'yrs', 'yr', 'y'):
102
+ return num_value * Y
103
+ # Months
104
+ elif unit in ('months', 'month', 'mo'):
105
+ return num_value * MO
106
+ # Weeks
107
+ elif unit in ('weeks', 'week', 'w'):
108
+ return num_value * W
109
+ # Days
110
+ elif unit in ('days', 'day', 'd'):
111
+ return num_value * D
112
+ # Hours
113
+ elif unit in ('hours', 'hour', 'hrs', 'hr', 'h'):
114
+ return num_value * H
115
+ # Minutes
116
+ elif unit in ('minutes', 'minute', 'mins', 'min', 'm'):
117
+ return num_value * M
118
+ # Seconds
119
+ elif unit in ('seconds', 'second', 'secs', 'sec', 's'):
120
+ return num_value * S
121
+ # Milliseconds
122
+ elif unit in ('milliseconds', 'millisecond', 'msecs', 'msec', 'ms'):
123
+ return num_value
124
+ else:
125
+ raise MSError(
126
+ f'Unknown unit "{unit}" provided to ms.parse(). value={repr(value)}'
127
+ )
128
+
129
+
130
+ def parse_strict(value: str) -> float:
131
+ """
132
+ Parse the given string and return milliseconds (strict version).
133
+
134
+ This is an alias for parse() provided for API compatibility.
135
+
136
+ Args:
137
+ value: A string to parse to milliseconds
138
+
139
+ Returns:
140
+ The parsed value in milliseconds
141
+ """
142
+ return parse(value)
143
+
144
+
145
+ def format(ms_value: Union[int, float], *, long: bool = False) -> str:
146
+ """
147
+ Format the given milliseconds as a string.
148
+
149
+ Args:
150
+ ms_value: Milliseconds to format
151
+ long: Use verbose formatting if True
152
+
153
+ Returns:
154
+ The formatted string
155
+
156
+ Raises:
157
+ MSError: If ms_value is not a finite number
158
+
159
+ Examples:
160
+ >>> format(172800000)
161
+ '2d'
162
+ >>> format(172800000, long=True)
163
+ '2 days'
164
+ >>> format(3600000)
165
+ '1h'
166
+ """
167
+ if not isinstance(ms_value, (int, float)):
168
+ raise MSError('Value provided to ms.format() must be of type number.')
169
+
170
+ # Check if the number is finite (not NaN, not Infinity)
171
+ if not math.isfinite(ms_value):
172
+ raise MSError('Value provided to ms.format() must be of type number.')
173
+
174
+ return _fmt_long(ms_value) if long else _fmt_short(ms_value)
175
+
176
+
177
+ def _fmt_short(ms_value: Union[int, float]) -> str:
178
+ """Short format for ms."""
179
+ ms_abs = abs(ms_value)
180
+
181
+ if ms_abs >= Y:
182
+ return f"{round(ms_value / Y)}y"
183
+ if ms_abs >= MO:
184
+ return f"{round(ms_value / MO)}mo"
185
+ if ms_abs >= W:
186
+ return f"{round(ms_value / W)}w"
187
+ if ms_abs >= D:
188
+ return f"{round(ms_value / D)}d"
189
+ if ms_abs >= H:
190
+ return f"{round(ms_value / H)}h"
191
+ if ms_abs >= M:
192
+ return f"{round(ms_value / M)}m"
193
+ if ms_abs >= S:
194
+ return f"{round(ms_value / S)}s"
195
+ return f"{int(ms_value)}ms"
196
+
197
+
198
+ def _fmt_long(ms_value: Union[int, float]) -> str:
199
+ """Long format for ms."""
200
+ ms_abs = abs(ms_value)
201
+
202
+ if ms_abs >= Y:
203
+ return _plural(ms_value, ms_abs, Y, 'year')
204
+ if ms_abs >= MO:
205
+ return _plural(ms_value, ms_abs, MO, 'month')
206
+ if ms_abs >= W:
207
+ return _plural(ms_value, ms_abs, W, 'week')
208
+ if ms_abs >= D:
209
+ return _plural(ms_value, ms_abs, D, 'day')
210
+ if ms_abs >= H:
211
+ return _plural(ms_value, ms_abs, H, 'hour')
212
+ if ms_abs >= M:
213
+ return _plural(ms_value, ms_abs, M, 'minute')
214
+ if ms_abs >= S:
215
+ return _plural(ms_value, ms_abs, S, 'second')
216
+ return f"{int(ms_value)} ms"
217
+
218
+
219
+ def _plural(ms_value: Union[int, float], ms_abs: float, n: float, name: str) -> str:
220
+ """Pluralization helper."""
221
+ is_plural = ms_abs >= n * 1.5
222
+ rounded = round(ms_value / n)
223
+ return f"{rounded} {name}{'s' if is_plural else ''}"
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["flit_core >=3.11,<4"]
3
+ build-backend = "flit_core.buildapi"
4
+
5
+ [project]
6
+ name = "pymillis"
7
+ authors = [{name = "wudi", email = "wuchendi96@gmail.com"}]
8
+ description = "Use this package to easily convert various time formats to milliseconds."
9
+ readme = "README.md"
10
+ dynamic = ["version"]
11
+ license = {text = "MIT"}
12
+ requires-python = ">=3.9"
13
+ keywords = ["time", "milliseconds", "ms", "duration", "conversion"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Topic :: Software Development :: Libraries :: Python Modules",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Programming Language :: Python :: 3.14",
28
+ ]
29
+
30
+ [project.urls]
31
+ "Bug Reports" = "https://github.com/WuChenDi/pymillis/issues"
32
+ "Source" = "https://github.com/WuChenDi/pymillis"
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["tests"]
36
+ python_files = ["test_*.py"]
37
+ python_classes = ["Test*"]
38
+ python_functions = ["test_*"]
39
+
40
+ [tool.black]
41
+ line-length = 100
42
+ target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
43
+ include = '\.pyi?$'
44
+
45
+ [tool.mypy]
46
+ python_version = "3.9"
47
+ warn_return_any = true
48
+ warn_unused_configs = true
49
+ disallow_untyped_defs = false