staran 1.0.10__py3-none-any.whl → 1.0.11__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.
- staran/__init__.py +61 -0
- staran/date/__init__.py +1 -1
- staran/date/core/lunar.py +14 -184
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/METADATA +1 -1
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/RECORD +9 -9
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/WHEEL +0 -0
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/entry_points.txt +0 -0
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/licenses/LICENSE +0 -0
- {staran-1.0.10.dist-info → staran-1.0.11.dist-info}/top_level.txt +0 -0
staran/__init__.py
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
Staran - 企业级Python日期处理库
|
6
|
+
"""
|
7
|
+
|
8
|
+
__version__ = "1.0.11"
|
9
|
+
__author__ = "StarAn"
|
10
|
+
__email__ = "starlxa@icloud.com"
|
11
|
+
__license__ = "MIT"
|
12
|
+
|
13
|
+
# 导入核心功能
|
14
|
+
try:
|
15
|
+
from .date import (
|
16
|
+
Date,
|
17
|
+
DateRange,
|
18
|
+
DateError,
|
19
|
+
LunarDate,
|
20
|
+
Language,
|
21
|
+
today,
|
22
|
+
from_string,
|
23
|
+
from_lunar,
|
24
|
+
parse_expression,
|
25
|
+
get_version_info,
|
26
|
+
get_feature_status
|
27
|
+
)
|
28
|
+
except ImportError as e:
|
29
|
+
import warnings
|
30
|
+
warnings.warn(f"Staran核心功能导入失败: {e}")
|
31
|
+
|
32
|
+
Date = None
|
33
|
+
DateRange = None
|
34
|
+
DateError = Exception
|
35
|
+
LunarDate = None
|
36
|
+
Language = None
|
37
|
+
|
38
|
+
def today():
|
39
|
+
raise ImportError("Staran核心功能不可用")
|
40
|
+
|
41
|
+
def from_string(date_string: str):
|
42
|
+
raise ImportError("Staran核心功能不可用")
|
43
|
+
|
44
|
+
def from_lunar(year: int, month: int, day: int, is_leap: bool = False):
|
45
|
+
raise ImportError("Staran核心功能不可用")
|
46
|
+
|
47
|
+
def parse_expression(expression: str):
|
48
|
+
raise ImportError("Staran核心功能不可用")
|
49
|
+
|
50
|
+
def get_version_info():
|
51
|
+
return {'version': __version__, 'status': 'core_unavailable'}
|
52
|
+
|
53
|
+
def get_feature_status():
|
54
|
+
return {'core_available': False}
|
55
|
+
|
56
|
+
__all__ = [
|
57
|
+
'__version__', '__author__', '__email__', '__license__',
|
58
|
+
'Date', 'DateRange', 'DateError', 'LunarDate', 'Language',
|
59
|
+
'today', 'from_string', 'from_lunar', 'parse_expression',
|
60
|
+
'get_version_info', 'get_feature_status'
|
61
|
+
]
|
staran/date/__init__.py
CHANGED
staran/date/core/lunar.py
CHANGED
@@ -8,9 +8,8 @@
|
|
8
8
|
提供公历与农历互转功能,支持农历日期的创建、输出和比较。
|
9
9
|
基于中国传统农历历法计算,支持1900-2100年范围。
|
10
10
|
"""
|
11
|
-
|
12
|
-
import datetime
|
13
11
|
from typing import Tuple, Optional, Union, TYPE_CHECKING
|
12
|
+
import datetime
|
14
13
|
|
15
14
|
if TYPE_CHECKING:
|
16
15
|
from .core import Date
|
@@ -23,7 +22,6 @@ class LunarDate:
|
|
23
22
|
"""
|
24
23
|
|
25
24
|
# 农历数据表 (1900-2100年)
|
26
|
-
# 每个数字的低12位表示12个月,第13位表示闰月月份,第14-17位表示闰月天数
|
27
25
|
_LUNAR_INFO = [
|
28
26
|
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
|
29
27
|
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
|
@@ -48,63 +46,19 @@ class LunarDate:
|
|
48
46
|
0x0d520
|
49
47
|
]
|
50
48
|
|
51
|
-
# 农历月份名称
|
52
|
-
_LUNAR_MONTHS = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊']
|
53
|
-
_LUNAR_DAYS = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
|
54
|
-
'十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
|
55
|
-
'廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十']
|
56
|
-
|
57
|
-
# 天干地支
|
58
|
-
_TIANGAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
|
59
|
-
_DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
|
60
|
-
_ZODIAC = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
|
61
|
-
|
62
49
|
def __init__(self, year: int, month: int, day: int, is_leap: bool = False):
|
63
|
-
"""初始化农历日期
|
64
|
-
|
65
|
-
Args:
|
66
|
-
year: 农历年份
|
67
|
-
month: 农历月份
|
68
|
-
day: 农历日期
|
69
|
-
is_leap: 是否闰月
|
70
|
-
"""
|
71
50
|
self.year = year
|
72
51
|
self.month = month
|
73
52
|
self.day = day
|
74
53
|
self.is_leap = is_leap
|
75
|
-
|
76
|
-
if not self._is_valid():
|
77
|
-
raise ValueError(f"无效的农历日期: {year}年{month}月{day}日")
|
78
|
-
|
79
|
-
def _is_valid(self) -> bool:
|
80
|
-
"""验证农历日期是否有效"""
|
81
|
-
if not (1900 <= self.year <= 2100):
|
82
|
-
return False
|
83
|
-
if not (1 <= self.month <= 12):
|
84
|
-
return False
|
85
|
-
if not (1 <= self.day <= 30):
|
86
|
-
return False
|
87
|
-
return True
|
88
54
|
|
89
55
|
@classmethod
|
90
|
-
def from_solar(cls, solar_date
|
91
|
-
"""从公历日期转换为农历日期
|
92
|
-
|
93
|
-
Args:
|
94
|
-
solar_date: 公历日期对象
|
95
|
-
|
96
|
-
Returns:
|
97
|
-
农历日期对象
|
98
|
-
"""
|
56
|
+
def from_solar(cls, solar_date):
|
57
|
+
"""从公历日期转换为农历日期"""
|
99
58
|
if hasattr(solar_date, 'to_datetime_object'):
|
100
|
-
# 处理自定义Date对象
|
101
59
|
solar_date = solar_date.to_datetime_object()
|
102
60
|
|
103
|
-
|
104
|
-
month = solar_date.month
|
105
|
-
day = solar_date.day
|
106
|
-
|
107
|
-
# 计算距离1900年1月31日(农历1900年正月初一)的天数
|
61
|
+
# 计算距离1900年1月31日的天数
|
108
62
|
base_date = datetime.date(1900, 1, 31)
|
109
63
|
delta = solar_date - base_date
|
110
64
|
offset = delta.days
|
@@ -130,7 +84,6 @@ class LunarDate:
|
|
130
84
|
if offset >= month_days:
|
131
85
|
offset -= month_days
|
132
86
|
if lunar_month == leap_month and not is_leap:
|
133
|
-
# 处理闰月
|
134
87
|
leap_days = cls._get_lunar_month_days(lunar_year, lunar_month, True)
|
135
88
|
if offset >= leap_days:
|
136
89
|
offset -= leap_days
|
@@ -144,43 +97,10 @@ class LunarDate:
|
|
144
97
|
break
|
145
98
|
|
146
99
|
lunar_day = offset + 1
|
147
|
-
|
148
100
|
return cls(lunar_year, lunar_month, lunar_day, is_leap)
|
149
101
|
|
150
|
-
def to_solar(self) -> datetime.date:
|
151
|
-
"""转换为公历日期
|
152
|
-
|
153
|
-
Returns:
|
154
|
-
公历日期对象
|
155
|
-
"""
|
156
|
-
# 计算农历日期距离1900年正月初一的天数
|
157
|
-
offset = 0
|
158
|
-
|
159
|
-
# 累加年份天数
|
160
|
-
for year in range(1900, self.year):
|
161
|
-
offset += self._get_lunar_year_days(year)
|
162
|
-
|
163
|
-
# 累加月份天数
|
164
|
-
for month in range(1, self.month):
|
165
|
-
offset += self._get_lunar_month_days(self.year, month, False)
|
166
|
-
# 如果有闰月且月份匹配,还要加上闰月天数
|
167
|
-
leap_month = self._get_leap_month(self.year)
|
168
|
-
if month == leap_month:
|
169
|
-
offset += self._get_lunar_month_days(self.year, month, True)
|
170
|
-
|
171
|
-
# 如果是闰月,还要加上正常月份的天数
|
172
|
-
if self.is_leap:
|
173
|
-
offset += self._get_lunar_month_days(self.year, self.month, False)
|
174
|
-
|
175
|
-
# 加上日期天数
|
176
|
-
offset += self.day - 1
|
177
|
-
|
178
|
-
# 基准日期: 1900年1月31日(农历1900年正月初一)
|
179
|
-
base_date = datetime.date(1900, 1, 31)
|
180
|
-
return base_date + datetime.timedelta(days=offset)
|
181
|
-
|
182
102
|
@classmethod
|
183
|
-
def _get_lunar_year_days(cls, year
|
103
|
+
def _get_lunar_year_days(cls, year):
|
184
104
|
"""获取农历年的总天数"""
|
185
105
|
if year < 1900 or year > 2100:
|
186
106
|
return 0
|
@@ -189,8 +109,8 @@ class LunarDate:
|
|
189
109
|
days = 0
|
190
110
|
|
191
111
|
# 12个月的天数
|
192
|
-
for
|
193
|
-
days += 29 if (info & (0x10000 >>
|
112
|
+
for month in range(1, 13):
|
113
|
+
days += 29 if (info & (0x10000 >> month)) == 0 else 30
|
194
114
|
|
195
115
|
# 闰月天数
|
196
116
|
leap_month = cls._get_leap_month(year)
|
@@ -200,7 +120,7 @@ class LunarDate:
|
|
200
120
|
return days
|
201
121
|
|
202
122
|
@classmethod
|
203
|
-
def _get_lunar_month_days(cls, year
|
123
|
+
def _get_lunar_month_days(cls, year, month, is_leap=False):
|
204
124
|
"""获取农历月的天数"""
|
205
125
|
if year < 1900 or year > 2100:
|
206
126
|
return 0
|
@@ -208,113 +128,23 @@ class LunarDate:
|
|
208
128
|
info = cls._LUNAR_INFO[year - 1900]
|
209
129
|
|
210
130
|
if is_leap:
|
211
|
-
# 闰月天数
|
212
131
|
leap_month = cls._get_leap_month(year)
|
213
132
|
if month != leap_month:
|
214
133
|
return 0
|
215
134
|
return 29 if (info & 0x10000) == 0 else 30
|
216
135
|
else:
|
217
|
-
# 正常月份天数
|
218
136
|
return 29 if (info & (0x10000 >> month)) == 0 else 30
|
219
137
|
|
220
138
|
@classmethod
|
221
|
-
def _get_leap_month(cls, year
|
222
|
-
"""
|
139
|
+
def _get_leap_month(cls, year):
|
140
|
+
"""获取闰月月份"""
|
223
141
|
if year < 1900 or year > 2100:
|
224
142
|
return 0
|
225
143
|
|
226
144
|
info = cls._LUNAR_INFO[year - 1900]
|
227
145
|
return info & 0xf
|
228
146
|
|
229
|
-
def
|
230
|
-
"""
|
231
|
-
|
232
|
-
|
233
|
-
tiangan_index = offset % 10
|
234
|
-
dizhi_index = offset % 12
|
235
|
-
return self._TIANGAN[tiangan_index] + self._DIZHI[dizhi_index]
|
236
|
-
|
237
|
-
def get_zodiac(self) -> str:
|
238
|
-
"""获取生肖"""
|
239
|
-
return self._ZODIAC[(self.year - 1900) % 12]
|
240
|
-
|
241
|
-
def format_chinese(self, include_year: bool = True, include_zodiac: bool = False) -> str:
|
242
|
-
"""格式化为中文农历日期
|
243
|
-
|
244
|
-
Args:
|
245
|
-
include_year: 是否包含年份
|
246
|
-
include_zodiac: 是否包含生肖
|
247
|
-
|
248
|
-
Returns:
|
249
|
-
中文农历日期字符串
|
250
|
-
"""
|
251
|
-
result = ""
|
252
|
-
|
253
|
-
if include_year:
|
254
|
-
if include_zodiac:
|
255
|
-
result += f"{self.get_ganzhi_year()}({self.get_zodiac()})年"
|
256
|
-
else:
|
257
|
-
result += f"农历{self.year}年"
|
258
|
-
|
259
|
-
# 月份
|
260
|
-
if self.is_leap:
|
261
|
-
result += f"闰{self._LUNAR_MONTHS[self.month - 1]}月"
|
262
|
-
else:
|
263
|
-
result += f"{self._LUNAR_MONTHS[self.month - 1]}月"
|
264
|
-
|
265
|
-
# 日期
|
266
|
-
result += self._LUNAR_DAYS[self.day - 1]
|
267
|
-
|
268
|
-
return result
|
269
|
-
|
270
|
-
def format_compact(self) -> str:
|
271
|
-
"""紧凑格式"""
|
272
|
-
leap_prefix = "闰" if self.is_leap else ""
|
273
|
-
return f"{self.year}{leap_prefix}{self.month:02d}{self.day:02d}"
|
274
|
-
|
275
|
-
def format_iso_like(self) -> str:
|
276
|
-
"""类ISO格式"""
|
277
|
-
leap_suffix = "L" if self.is_leap else ""
|
278
|
-
return f"{self.year}-{self.month:02d}{leap_suffix}-{self.day:02d}"
|
279
|
-
|
280
|
-
def __str__(self) -> str:
|
281
|
-
"""字符串表示"""
|
282
|
-
return self.format_chinese()
|
283
|
-
|
284
|
-
def __repr__(self) -> str:
|
285
|
-
"""调试表示"""
|
286
|
-
return f"LunarDate({self.year}, {self.month}, {self.day}, is_leap={self.is_leap})"
|
287
|
-
|
288
|
-
def __eq__(self, other) -> bool:
|
289
|
-
"""相等比较"""
|
290
|
-
if not isinstance(other, LunarDate):
|
291
|
-
return False
|
292
|
-
return (self.year == other.year and
|
293
|
-
self.month == other.month and
|
294
|
-
self.day == other.day and
|
295
|
-
self.is_leap == other.is_leap)
|
296
|
-
|
297
|
-
def __lt__(self, other) -> bool:
|
298
|
-
"""小于比较"""
|
299
|
-
if not isinstance(other, LunarDate):
|
300
|
-
return NotImplemented
|
301
|
-
|
302
|
-
if self.year != other.year:
|
303
|
-
return self.year < other.year
|
304
|
-
if self.month != other.month:
|
305
|
-
return self.month < other.month
|
306
|
-
if self.is_leap != other.is_leap:
|
307
|
-
return not self.is_leap # 正常月份小于闰月
|
308
|
-
return self.day < other.day
|
309
|
-
|
310
|
-
def __le__(self, other) -> bool:
|
311
|
-
"""小于等于比较"""
|
312
|
-
return self == other or self < other
|
313
|
-
|
314
|
-
def __gt__(self, other) -> bool:
|
315
|
-
"""大于比较"""
|
316
|
-
return not self <= other
|
317
|
-
|
318
|
-
def __ge__(self, other) -> bool:
|
319
|
-
"""大于等于比较"""
|
320
|
-
return not self < other
|
147
|
+
def format_chinese(self):
|
148
|
+
"""格式化为中文"""
|
149
|
+
leap_str = "闰" if self.is_leap else ""
|
150
|
+
return f"农历{self.year}年{leap_str}{self.month}月{self.day}日"
|
@@ -1,9 +1,9 @@
|
|
1
|
-
staran/__init__.py,sha256=
|
2
|
-
staran/date/__init__.py,sha256=
|
1
|
+
staran/__init__.py,sha256=2fdUCSoO3lRBtz1lIayT3wjEZY2EnsEC-4kdqFYemLQ,1538
|
2
|
+
staran/date/__init__.py,sha256=jvim48EMYAu8vR3HZRoqALh8ADkbyQinV7oAxyF28pU,2858
|
3
3
|
staran/date/core/__init__.py,sha256=cZmvd7X36qmXUGPrf-K0xOchzPiSONv1rw1WMi1uNXA,430
|
4
4
|
staran/date/core/core.py,sha256=1rlVqJaZZh8PxlBcE8R6aJoHP27Xwvpc6DZ5Mv30RhU,88769
|
5
5
|
staran/date/core/i18n.py,sha256=bvnqhVGMYvW8Lt3W4uksJw9aRZ2l4cNAy0GiYEQy4II,14677
|
6
|
-
staran/date/core/lunar.py,sha256=
|
6
|
+
staran/date/core/lunar.py,sha256=CiGQIOdfh8mLJAKJ4VDzqp7IWObPr_6S1SCgULkfMuQ,5875
|
7
7
|
staran/date/examples/__init__.py,sha256=5q6uxzeIhPcFo64gXybEozx4E4lt8TEEibFC-dF_EV0,163
|
8
8
|
staran/date/examples/basic_usage.py,sha256=hsQZaMRR6tY9krLjmYrH7GrMkk2cGapY-bfjwsL_6YQ,4738
|
9
9
|
staran/date/examples/enhanced_features.py,sha256=KDxw1d-h3N19oidCyeHKCSMUrOtr0sP7K5YI1fsowrw,6598
|
@@ -26,9 +26,9 @@ staran/date/tests/test_v108_features.py,sha256=wih6mY-fgq1aEYdvXkFqzoeSk73-8Mf9W
|
|
26
26
|
staran/date/tests/test_v109_features.py,sha256=hN5m3DTQM8j_U9ilh3fN3cQvBZH4DPI7stGlsgdIEEw,10932
|
27
27
|
staran/date/utils/__init__.py,sha256=W5DkeslSOINF7kq6wFz3l16fUmGI0XALNuJAALQeLLM,142
|
28
28
|
staran/date/utils/helpers.py,sha256=9TlebdCr-YD4vrXjTFMXDG413gbSFwdUNyivAarIp5M,5553
|
29
|
-
staran-1.0.
|
30
|
-
staran-1.0.
|
31
|
-
staran-1.0.
|
32
|
-
staran-1.0.
|
33
|
-
staran-1.0.
|
34
|
-
staran-1.0.
|
29
|
+
staran-1.0.11.dist-info/licenses/LICENSE,sha256=2EmsBIyDCono4iVXNpv5_px9qt2b7hfPq1WuyGVMNP4,1361
|
30
|
+
staran-1.0.11.dist-info/METADATA,sha256=mhsT-yZaaQ6NnC_UDQQICgYv7Rx_oWZzLc6dpz1x-Ms,8012
|
31
|
+
staran-1.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
32
|
+
staran-1.0.11.dist-info/entry_points.txt,sha256=igvLxDd-ONmb_m9IwbFKMv_uMhAyb_nPfNIq3J3rZUg,54
|
33
|
+
staran-1.0.11.dist-info/top_level.txt,sha256=NOUZtXSh5oSIEjHrC0lQ9WmoKtD010Q00dghWyag-Zs,7
|
34
|
+
staran-1.0.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|