staran 1.0.10__tar.gz → 1.0.11__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.
Files changed (41) hide show
  1. {staran-1.0.10/staran.egg-info → staran-1.0.11}/PKG-INFO +1 -1
  2. {staran-1.0.10 → staran-1.0.11}/setup.py +1 -1
  3. staran-1.0.11/staran/__init__.py +61 -0
  4. {staran-1.0.10 → staran-1.0.11}/staran/date/__init__.py +1 -1
  5. staran-1.0.11/staran/date/core/lunar.py +150 -0
  6. {staran-1.0.10 → staran-1.0.11/staran.egg-info}/PKG-INFO +1 -1
  7. staran-1.0.10/staran/__init__.py +0 -0
  8. staran-1.0.10/staran/date/core/lunar.py +0 -320
  9. {staran-1.0.10 → staran-1.0.11}/LICENSE +0 -0
  10. {staran-1.0.10 → staran-1.0.11}/README.md +0 -0
  11. {staran-1.0.10 → staran-1.0.11}/setup.cfg +0 -0
  12. {staran-1.0.10 → staran-1.0.11}/staran/date/core/__init__.py +0 -0
  13. {staran-1.0.10 → staran-1.0.11}/staran/date/core/core.py +0 -0
  14. {staran-1.0.10 → staran-1.0.11}/staran/date/core/i18n.py +0 -0
  15. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/__init__.py +0 -0
  16. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/basic_usage.py +0 -0
  17. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/enhanced_features.py +0 -0
  18. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/v1010_features_demo.py +0 -0
  19. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/v108_features_demo.py +0 -0
  20. {staran-1.0.10 → staran-1.0.11}/staran/date/examples/v109_features_demo.py +0 -0
  21. {staran-1.0.10 → staran-1.0.11}/staran/date/extensions/__init__.py +0 -0
  22. {staran-1.0.10 → staran-1.0.11}/staran/date/extensions/expressions.py +0 -0
  23. {staran-1.0.10 → staran-1.0.11}/staran/date/extensions/solar_terms.py +0 -0
  24. {staran-1.0.10 → staran-1.0.11}/staran/date/extensions/timezone.py +0 -0
  25. {staran-1.0.10 → staran-1.0.11}/staran/date/integrations/__init__.py +0 -0
  26. {staran-1.0.10 → staran-1.0.11}/staran/date/integrations/api_server.py +0 -0
  27. {staran-1.0.10 → staran-1.0.11}/staran/date/integrations/visualization.py +0 -0
  28. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/__init__.py +0 -0
  29. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/run_tests.py +0 -0
  30. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/test_core.py +0 -0
  31. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/test_enhancements.py +0 -0
  32. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/test_v1010_features.py +0 -0
  33. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/test_v108_features.py +0 -0
  34. {staran-1.0.10 → staran-1.0.11}/staran/date/tests/test_v109_features.py +0 -0
  35. {staran-1.0.10 → staran-1.0.11}/staran/date/utils/__init__.py +0 -0
  36. {staran-1.0.10 → staran-1.0.11}/staran/date/utils/helpers.py +0 -0
  37. {staran-1.0.10 → staran-1.0.11}/staran.egg-info/SOURCES.txt +0 -0
  38. {staran-1.0.10 → staran-1.0.11}/staran.egg-info/dependency_links.txt +0 -0
  39. {staran-1.0.10 → staran-1.0.11}/staran.egg-info/entry_points.txt +0 -0
  40. {staran-1.0.10 → staran-1.0.11}/staran.egg-info/requires.txt +0 -0
  41. {staran-1.0.10 → staran-1.0.11}/staran.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: staran
3
- Version: 1.0.10
3
+ Version: 1.0.11
4
4
  Summary: staran - 企业级Python日期处理库
5
5
  Home-page: https://github.com/starlxa/staran
6
6
  Author: StarAn
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="staran",
5
- version="1.0.10",
5
+ version="1.0.11",
6
6
  description="staran - 企业级Python日期处理库",
7
7
  long_description=open("README.md", encoding="utf-8").read(),
8
8
  long_description_content_type="text/markdown",
@@ -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
+ ]
@@ -6,7 +6,7 @@ Staran 日期处理库 v1.0.10 - 简化版导入
6
6
  """
7
7
 
8
8
  # 版本信息
9
- __version__ = "1.0.10"
9
+ __version__ = "1.0.11"
10
10
  __author__ = "StarAn"
11
11
  __email__ = "starlxa@icloud.com"
12
12
 
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 农历计算模块
6
+ ============
7
+
8
+ 提供公历与农历互转功能,支持农历日期的创建、输出和比较。
9
+ 基于中国传统农历历法计算,支持1900-2100年范围。
10
+ """
11
+ from typing import Tuple, Optional, Union, TYPE_CHECKING
12
+ import datetime
13
+
14
+ if TYPE_CHECKING:
15
+ from .core import Date
16
+
17
+
18
+ class LunarDate:
19
+ """农历日期类
20
+
21
+ 支持农历与公历的互相转换,农历日期的创建和格式化输出。
22
+ """
23
+
24
+ # 农历数据表 (1900-2100年)
25
+ _LUNAR_INFO = [
26
+ 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
27
+ 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
28
+ 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
29
+ 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
30
+ 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
31
+ 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
32
+ 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
33
+ 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
34
+ 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
35
+ 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
36
+ 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
37
+ 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
38
+ 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
39
+ 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
40
+ 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
41
+ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,
42
+ 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
43
+ 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
44
+ 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
45
+ 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
46
+ 0x0d520
47
+ ]
48
+
49
+ def __init__(self, year: int, month: int, day: int, is_leap: bool = False):
50
+ self.year = year
51
+ self.month = month
52
+ self.day = day
53
+ self.is_leap = is_leap
54
+
55
+ @classmethod
56
+ def from_solar(cls, solar_date):
57
+ """从公历日期转换为农历日期"""
58
+ if hasattr(solar_date, 'to_datetime_object'):
59
+ solar_date = solar_date.to_datetime_object()
60
+
61
+ # 计算距离1900年1月31日的天数
62
+ base_date = datetime.date(1900, 1, 31)
63
+ delta = solar_date - base_date
64
+ offset = delta.days
65
+
66
+ # 计算农历年份
67
+ lunar_year = 1900
68
+ while offset > 0:
69
+ year_days = cls._get_lunar_year_days(lunar_year)
70
+ if offset >= year_days:
71
+ offset -= year_days
72
+ lunar_year += 1
73
+ else:
74
+ break
75
+
76
+ # 计算农历月份和日期
77
+ lunar_month = 1
78
+ is_leap = False
79
+
80
+ while offset > 0:
81
+ month_days = cls._get_lunar_month_days(lunar_year, lunar_month, False)
82
+ leap_month = cls._get_leap_month(lunar_year)
83
+
84
+ if offset >= month_days:
85
+ offset -= month_days
86
+ if lunar_month == leap_month and not is_leap:
87
+ leap_days = cls._get_lunar_month_days(lunar_year, lunar_month, True)
88
+ if offset >= leap_days:
89
+ offset -= leap_days
90
+ lunar_month += 1
91
+ else:
92
+ is_leap = True
93
+ break
94
+ else:
95
+ lunar_month += 1
96
+ else:
97
+ break
98
+
99
+ lunar_day = offset + 1
100
+ return cls(lunar_year, lunar_month, lunar_day, is_leap)
101
+
102
+ @classmethod
103
+ def _get_lunar_year_days(cls, year):
104
+ """获取农历年的总天数"""
105
+ if year < 1900 or year > 2100:
106
+ return 0
107
+
108
+ info = cls._LUNAR_INFO[year - 1900]
109
+ days = 0
110
+
111
+ # 12个月的天数
112
+ for month in range(1, 13):
113
+ days += 29 if (info & (0x10000 >> month)) == 0 else 30
114
+
115
+ # 闰月天数
116
+ leap_month = cls._get_leap_month(year)
117
+ if leap_month > 0:
118
+ days += cls._get_lunar_month_days(year, leap_month, True)
119
+
120
+ return days
121
+
122
+ @classmethod
123
+ def _get_lunar_month_days(cls, year, month, is_leap=False):
124
+ """获取农历月的天数"""
125
+ if year < 1900 or year > 2100:
126
+ return 0
127
+
128
+ info = cls._LUNAR_INFO[year - 1900]
129
+
130
+ if is_leap:
131
+ leap_month = cls._get_leap_month(year)
132
+ if month != leap_month:
133
+ return 0
134
+ return 29 if (info & 0x10000) == 0 else 30
135
+ else:
136
+ return 29 if (info & (0x10000 >> month)) == 0 else 30
137
+
138
+ @classmethod
139
+ def _get_leap_month(cls, year):
140
+ """获取闰月月份"""
141
+ if year < 1900 or year > 2100:
142
+ return 0
143
+
144
+ info = cls._LUNAR_INFO[year - 1900]
145
+ return info & 0xf
146
+
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: staran
3
- Version: 1.0.10
3
+ Version: 1.0.11
4
4
  Summary: staran - 企业级Python日期处理库
5
5
  Home-page: https://github.com/starlxa/staran
6
6
  Author: StarAn
File without changes
@@ -1,320 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- 农历计算模块
6
- ============
7
-
8
- 提供公历与农历互转功能,支持农历日期的创建、输出和比较。
9
- 基于中国传统农历历法计算,支持1900-2100年范围。
10
- """
11
-
12
- import datetime
13
- from typing import Tuple, Optional, Union, TYPE_CHECKING
14
-
15
- if TYPE_CHECKING:
16
- from .core import Date
17
-
18
-
19
- class LunarDate:
20
- """农历日期类
21
-
22
- 支持农历与公历的互相转换,农历日期的创建和格式化输出。
23
- """
24
-
25
- # 农历数据表 (1900-2100年)
26
- # 每个数字的低12位表示12个月,第13位表示闰月月份,第14-17位表示闰月天数
27
- _LUNAR_INFO = [
28
- 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
29
- 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
30
- 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
31
- 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
32
- 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
33
- 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
34
- 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
35
- 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
36
- 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
37
- 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
38
- 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
39
- 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
40
- 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
41
- 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
42
- 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
43
- 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,
44
- 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
45
- 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
46
- 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
47
- 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
48
- 0x0d520
49
- ]
50
-
51
- # 农历月份名称
52
- _LUNAR_MONTHS = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊']
53
- _LUNAR_DAYS = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
54
- '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
55
- '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十']
56
-
57
- # 天干地支
58
- _TIANGAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
59
- _DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
60
- _ZODIAC = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
61
-
62
- 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
- self.year = year
72
- self.month = month
73
- self.day = day
74
- 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
-
89
- @classmethod
90
- def from_solar(cls, solar_date: Union[datetime.date, 'Date']) -> 'LunarDate':
91
- """从公历日期转换为农历日期
92
-
93
- Args:
94
- solar_date: 公历日期对象
95
-
96
- Returns:
97
- 农历日期对象
98
- """
99
- if hasattr(solar_date, 'to_datetime_object'):
100
- # 处理自定义Date对象
101
- solar_date = solar_date.to_datetime_object()
102
-
103
- year = solar_date.year
104
- month = solar_date.month
105
- day = solar_date.day
106
-
107
- # 计算距离1900年1月31日(农历1900年正月初一)的天数
108
- base_date = datetime.date(1900, 1, 31)
109
- delta = solar_date - base_date
110
- offset = delta.days
111
-
112
- # 计算农历年份
113
- lunar_year = 1900
114
- while offset > 0:
115
- year_days = cls._get_lunar_year_days(lunar_year)
116
- if offset >= year_days:
117
- offset -= year_days
118
- lunar_year += 1
119
- else:
120
- break
121
-
122
- # 计算农历月份和日期
123
- lunar_month = 1
124
- is_leap = False
125
-
126
- while offset > 0:
127
- month_days = cls._get_lunar_month_days(lunar_year, lunar_month, False)
128
- leap_month = cls._get_leap_month(lunar_year)
129
-
130
- if offset >= month_days:
131
- offset -= month_days
132
- if lunar_month == leap_month and not is_leap:
133
- # 处理闰月
134
- leap_days = cls._get_lunar_month_days(lunar_year, lunar_month, True)
135
- if offset >= leap_days:
136
- offset -= leap_days
137
- lunar_month += 1
138
- else:
139
- is_leap = True
140
- break
141
- else:
142
- lunar_month += 1
143
- else:
144
- break
145
-
146
- lunar_day = offset + 1
147
-
148
- return cls(lunar_year, lunar_month, lunar_day, is_leap)
149
-
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
- @classmethod
183
- def _get_lunar_year_days(cls, year: int) -> int:
184
- """获取农历年的总天数"""
185
- if year < 1900 or year > 2100:
186
- return 0
187
-
188
- info = cls._LUNAR_INFO[year - 1900]
189
- days = 0
190
-
191
- # 12个月的天数
192
- for i in range(12):
193
- days += 29 if (info & (0x10000 >> i)) == 0 else 30
194
-
195
- # 闰月天数
196
- leap_month = cls._get_leap_month(year)
197
- if leap_month > 0:
198
- days += cls._get_lunar_month_days(year, leap_month, True)
199
-
200
- return days
201
-
202
- @classmethod
203
- def _get_lunar_month_days(cls, year: int, month: int, is_leap: bool = False) -> int:
204
- """获取农历月的天数"""
205
- if year < 1900 or year > 2100:
206
- return 0
207
-
208
- info = cls._LUNAR_INFO[year - 1900]
209
-
210
- if is_leap:
211
- # 闰月天数
212
- leap_month = cls._get_leap_month(year)
213
- if month != leap_month:
214
- return 0
215
- return 29 if (info & 0x10000) == 0 else 30
216
- else:
217
- # 正常月份天数
218
- return 29 if (info & (0x10000 >> month)) == 0 else 30
219
-
220
- @classmethod
221
- def _get_leap_month(cls, year: int) -> int:
222
- """获取闰月月份,如果没有闰月返回0"""
223
- if year < 1900 or year > 2100:
224
- return 0
225
-
226
- info = cls._LUNAR_INFO[year - 1900]
227
- return info & 0xf
228
-
229
- def get_ganzhi_year(self) -> str:
230
- """获取天干地支年份"""
231
- # 甲子年为1984年,每60年一个周期
232
- offset = (self.year - 1984) % 60
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
File without changes
File without changes
File without changes