staran 1.0.7__py3-none-any.whl → 1.0.9__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/date/__init__.py +62 -2
- staran/date/core.py +926 -9
- staran/date/examples/v108_features_demo.py +257 -0
- staran/date/examples/v109_features_demo.py +302 -0
- staran/date/i18n.py +376 -0
- staran/date/lunar.py +320 -0
- staran/date/tests/run_tests.py +77 -6
- staran/date/tests/test_v108_features.py +400 -0
- staran/date/tests/test_v109_features.py +316 -0
- staran-1.0.9.dist-info/METADATA +214 -0
- staran-1.0.9.dist-info/RECORD +23 -0
- staran-1.0.7.dist-info/METADATA +0 -306
- staran-1.0.7.dist-info/RECORD +0 -17
- {staran-1.0.7.dist-info → staran-1.0.9.dist-info}/WHEEL +0 -0
- {staran-1.0.7.dist-info → staran-1.0.9.dist-info}/licenses/LICENSE +0 -0
- {staran-1.0.7.dist-info → staran-1.0.9.dist-info}/top_level.txt +0 -0
staran/date/core.py
CHANGED
@@ -2,11 +2,18 @@
|
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
"""
|
5
|
-
Staran 核心日期处理模块
|
6
|
-
|
5
|
+
Staran 核心日期处理模块 v1.0.9
|
6
|
+
============================
|
7
7
|
|
8
|
-
提供Date类的完整实现,包括
|
8
|
+
提供Date类的完整实现,包括120+个企业级API方法、
|
9
9
|
智能格式记忆、日期运算和多种格式化选项。
|
10
|
+
|
11
|
+
v1.0.9 新增功能:
|
12
|
+
- 智能日期推断
|
13
|
+
- 异步批量处理支持
|
14
|
+
- 高级业务规则引擎
|
15
|
+
- 日期范围操作
|
16
|
+
- 性能优化和内存优化
|
10
17
|
"""
|
11
18
|
|
12
19
|
import datetime
|
@@ -14,8 +21,17 @@ import calendar
|
|
14
21
|
import re
|
15
22
|
import logging
|
16
23
|
import time
|
17
|
-
|
18
|
-
|
24
|
+
import asyncio
|
25
|
+
import csv
|
26
|
+
import json
|
27
|
+
from typing import Union, Optional, Tuple, Dict, Any, List, Set, TypeVar, Generic
|
28
|
+
from functools import lru_cache, wraps
|
29
|
+
from dataclasses import dataclass
|
30
|
+
import threading
|
31
|
+
|
32
|
+
# 导入农历和多语言模块
|
33
|
+
from .lunar import LunarDate
|
34
|
+
from .i18n import Language
|
19
35
|
|
20
36
|
class DateError(ValueError):
|
21
37
|
"""Date模块的特定异常基类"""
|
@@ -30,6 +46,149 @@ class InvalidDateValueError(DateError):
|
|
30
46
|
pass
|
31
47
|
|
32
48
|
|
49
|
+
@dataclass
|
50
|
+
class DateRange:
|
51
|
+
"""日期范围类 (v1.0.9)
|
52
|
+
|
53
|
+
表示一个日期范围,支持交集、并集等操作
|
54
|
+
"""
|
55
|
+
start: 'Date'
|
56
|
+
end: 'Date'
|
57
|
+
|
58
|
+
def __post_init__(self):
|
59
|
+
if self.start > self.end:
|
60
|
+
raise ValueError(f"起始日期 {self.start} 不能大于结束日期 {self.end}")
|
61
|
+
|
62
|
+
def contains(self, date: 'Date') -> bool:
|
63
|
+
"""检查日期是否在范围内"""
|
64
|
+
return self.start <= date <= self.end
|
65
|
+
|
66
|
+
def intersect(self, other: 'DateRange') -> Optional['DateRange']:
|
67
|
+
"""计算与另一个范围的交集"""
|
68
|
+
start = max(self.start, other.start)
|
69
|
+
end = min(self.end, other.end)
|
70
|
+
if start <= end:
|
71
|
+
return DateRange(start, end)
|
72
|
+
return None
|
73
|
+
|
74
|
+
def union(self, other: 'DateRange') -> 'DateRange':
|
75
|
+
"""计算与另一个范围的并集"""
|
76
|
+
start = min(self.start, other.start)
|
77
|
+
end = max(self.end, other.end)
|
78
|
+
return DateRange(start, end)
|
79
|
+
|
80
|
+
def days_count(self) -> int:
|
81
|
+
"""范围内的天数"""
|
82
|
+
return self.start.calculate_difference_days(self.end) + 1
|
83
|
+
|
84
|
+
|
85
|
+
class SmartDateInference:
|
86
|
+
"""智能日期推断器 (v1.0.9)
|
87
|
+
|
88
|
+
自动推断不完整的日期信息
|
89
|
+
"""
|
90
|
+
|
91
|
+
@staticmethod
|
92
|
+
def infer_year(month: int, day: int, reference_date: Optional['Date'] = None) -> int:
|
93
|
+
"""推断年份"""
|
94
|
+
if reference_date is None:
|
95
|
+
reference_date = Date.today()
|
96
|
+
|
97
|
+
# 优先使用当前年份
|
98
|
+
try:
|
99
|
+
current_year_date = Date(reference_date.year, month, day)
|
100
|
+
# 如果日期在未来6个月内,使用当前年份
|
101
|
+
if current_year_date >= reference_date and current_year_date.calculate_difference_days(reference_date) <= 180:
|
102
|
+
return reference_date.year
|
103
|
+
except (InvalidDateFormatError, InvalidDateValueError):
|
104
|
+
pass
|
105
|
+
|
106
|
+
# 否则使用下一年
|
107
|
+
return reference_date.year + 1
|
108
|
+
|
109
|
+
@staticmethod
|
110
|
+
def infer_day(year: int, month: int) -> int:
|
111
|
+
"""推断日期(默认为月初)"""
|
112
|
+
return 1
|
113
|
+
|
114
|
+
@staticmethod
|
115
|
+
def parse_smart(date_input: str, reference_date: Optional['Date'] = None) -> 'Date':
|
116
|
+
"""智能解析日期字符串"""
|
117
|
+
if reference_date is None:
|
118
|
+
reference_date = Date.today()
|
119
|
+
|
120
|
+
# 移除所有非数字字符
|
121
|
+
numbers = re.findall(r'\d+', date_input)
|
122
|
+
|
123
|
+
if len(numbers) == 1:
|
124
|
+
# 只有一个数字,可能是日期
|
125
|
+
day = int(numbers[0])
|
126
|
+
if 1 <= day <= 31:
|
127
|
+
year = reference_date.year
|
128
|
+
month = reference_date.month
|
129
|
+
# 如果这个日期已经过去,推到下个月
|
130
|
+
try:
|
131
|
+
inferred_date = Date(year, month, day)
|
132
|
+
if inferred_date < reference_date:
|
133
|
+
inferred_date = inferred_date.add_months(1)
|
134
|
+
return inferred_date
|
135
|
+
except (InvalidDateFormatError, InvalidDateValueError):
|
136
|
+
pass
|
137
|
+
|
138
|
+
elif len(numbers) == 2:
|
139
|
+
# 两个数字,可能是月日
|
140
|
+
month, day = int(numbers[0]), int(numbers[1])
|
141
|
+
if 1 <= month <= 12 and 1 <= day <= 31:
|
142
|
+
year = SmartDateInference.infer_year(month, day, reference_date)
|
143
|
+
return Date(year, month, day)
|
144
|
+
|
145
|
+
# 其他情况,回退到标准解析
|
146
|
+
return Date(date_input)
|
147
|
+
|
148
|
+
|
149
|
+
class PerformanceCache:
|
150
|
+
"""性能缓存管理器 (v1.0.9)
|
151
|
+
|
152
|
+
多级缓存策略,提升重复操作性能
|
153
|
+
"""
|
154
|
+
|
155
|
+
def __init__(self):
|
156
|
+
self._object_cache = {} # 对象创建缓存
|
157
|
+
self._format_cache = {} # 格式化缓存
|
158
|
+
self._calculation_cache = {} # 计算结果缓存
|
159
|
+
self._lock = threading.RLock()
|
160
|
+
|
161
|
+
def get_or_create_date(self, key: str, factory_func):
|
162
|
+
"""获取或创建Date对象"""
|
163
|
+
with self._lock:
|
164
|
+
if key not in self._object_cache:
|
165
|
+
self._object_cache[key] = factory_func()
|
166
|
+
return self._object_cache[key]
|
167
|
+
|
168
|
+
def get_or_calculate(self, key: str, calculation_func):
|
169
|
+
"""获取或计算结果"""
|
170
|
+
with self._lock:
|
171
|
+
if key not in self._calculation_cache:
|
172
|
+
self._calculation_cache[key] = calculation_func()
|
173
|
+
return self._calculation_cache[key]
|
174
|
+
|
175
|
+
def clear(self):
|
176
|
+
"""清空缓存"""
|
177
|
+
with self._lock:
|
178
|
+
self._object_cache.clear()
|
179
|
+
self._format_cache.clear()
|
180
|
+
self._calculation_cache.clear()
|
181
|
+
|
182
|
+
|
183
|
+
# 全局性能缓存实例
|
184
|
+
_performance_cache = PerformanceCache()
|
185
|
+
|
186
|
+
|
187
|
+
class InvalidDateValueError(DateError):
|
188
|
+
"""无效日期值异常"""
|
189
|
+
pass
|
190
|
+
|
191
|
+
|
33
192
|
class DateLogger:
|
34
193
|
"""企业级日志记录器
|
35
194
|
|
@@ -74,19 +233,35 @@ class DateLogger:
|
|
74
233
|
|
75
234
|
|
76
235
|
class Date:
|
77
|
-
"""企业级日期处理类
|
236
|
+
"""企业级日期处理类 v1.0.9
|
78
237
|
|
79
238
|
提供智能格式记忆、统一API命名和完整的日期处理功能。
|
80
239
|
支持YYYY、YYYYMM、YYYYMMDD等多种输入格式,并在运算中
|
81
240
|
自动保持原始格式。
|
82
241
|
|
242
|
+
v1.0.8 功能:
|
243
|
+
- 农历日期支持 (from_lunar, to_lunar, format_lunar等)
|
244
|
+
- 多语言配置 (中简、中繁、日、英四种语言)
|
245
|
+
- 全局语言设置,一次配置全局生效
|
246
|
+
|
247
|
+
v1.0.9 新增功能:
|
248
|
+
- 智能日期推断 (smart_parse, infer_date等)
|
249
|
+
- 异步批量处理 (async_batch_create, async_batch_format等)
|
250
|
+
- 高级业务规则 (扩展的业务规则引擎)
|
251
|
+
- 日期范围操作 (DateRange, 交集、并集等)
|
252
|
+
- 性能优化 (多级缓存、内存优化)
|
253
|
+
|
83
254
|
特性:
|
84
255
|
----
|
85
|
-
-
|
256
|
+
- 120+个统一命名的API方法
|
86
257
|
- 智能格式记忆和保持
|
87
258
|
- 企业级日志记录
|
259
|
+
- 农历与公历互转
|
260
|
+
- 多语言本地化支持
|
88
261
|
- 类型安全的日期转换
|
89
262
|
- 向后兼容的旧API支持
|
263
|
+
- 异步处理支持
|
264
|
+
- 智能推断和自动修复
|
90
265
|
|
91
266
|
Examples:
|
92
267
|
---------
|
@@ -98,6 +273,22 @@ class Date:
|
|
98
273
|
>>> print(date1.add_months(2)) # 202506
|
99
274
|
>>> print(date2.add_days(10)) # 20250425
|
100
275
|
>>>
|
276
|
+
>>> # 农历支持 (v1.0.8)
|
277
|
+
>>> lunar_date = Date.from_lunar(2025, 3, 15) # 农历2025年三月十五
|
278
|
+
>>> print(lunar_date.to_lunar().format_chinese()) # 农历2025年三月十五
|
279
|
+
>>>
|
280
|
+
>>> # 多语言支持 (v1.0.8)
|
281
|
+
>>> Date.set_language('en_US') # 设置全局语言为英语
|
282
|
+
>>> print(date2.format_localized()) # 04/15/2025
|
283
|
+
>>> print(date2.format_weekday_localized()) # Tuesday
|
284
|
+
>>>
|
285
|
+
>>> # 智能推断 (v1.0.9)
|
286
|
+
>>> smart_date = Date.smart_parse('15') # 智能推断为本月15号
|
287
|
+
>>> range_obj = DateRange(Date('20250101'), Date('20250131'))
|
288
|
+
>>> print(range_obj.days_count()) # 31
|
289
|
+
>>>
|
290
|
+
>>> # 异步批量处理 (v1.0.9)
|
291
|
+
>>> dates = await Date.async_batch_create(['20250101', '20250102'])
|
101
292
|
>>> # 统一API命名
|
102
293
|
>>> date = Date('20250415')
|
103
294
|
>>> print(date.format_iso()) # 2025-04-15
|
@@ -113,7 +304,7 @@ class Date:
|
|
113
304
|
当日期值超出有效范围时抛出
|
114
305
|
"""
|
115
306
|
|
116
|
-
__slots__ = ('year', 'month', 'day', '_input_format')
|
307
|
+
__slots__ = ('year', 'month', 'day', '_input_format', '_cache_key')
|
117
308
|
|
118
309
|
# 类级别的日志记录器
|
119
310
|
_logger = DateLogger()
|
@@ -136,6 +327,7 @@ class Date:
|
|
136
327
|
self.month: int = 1
|
137
328
|
self.day: int = 1
|
138
329
|
self._input_format: str = 'iso' # 默认ISO格式
|
330
|
+
self._cache_key: Optional[str] = None # 缓存键(v1.0.9)
|
139
331
|
|
140
332
|
# 根据参数类型进行初始化
|
141
333
|
if len(args) == 1 and not kwargs:
|
@@ -157,8 +349,15 @@ class Date:
|
|
157
349
|
else:
|
158
350
|
raise InvalidDateValueError("无效的参数组合")
|
159
351
|
|
352
|
+
# 生成缓存键 (v1.0.9)
|
353
|
+
self._generate_cache_key()
|
354
|
+
|
160
355
|
self._logger.info(f"创建Date对象: {self.year}-{self.month:02d}-{self.day:02d}, 格式: {self._input_format}")
|
161
356
|
|
357
|
+
def _generate_cache_key(self):
|
358
|
+
"""生成缓存键 (v1.0.9)"""
|
359
|
+
self._cache_key = f"{self.year}-{self.month:02d}-{self.day:02d}-{self._input_format}"
|
360
|
+
|
162
361
|
def _init_from_string(self, date_string: str):
|
163
362
|
"""从字符串初始化"""
|
164
363
|
# 移除分隔符并清理字符串
|
@@ -310,6 +509,137 @@ class Date:
|
|
310
509
|
"""创建今日Date对象"""
|
311
510
|
return cls(datetime.date.today())
|
312
511
|
|
512
|
+
@classmethod
|
513
|
+
def smart_parse(cls, date_input: str, reference_date: Optional['Date'] = None) -> 'Date':
|
514
|
+
"""智能解析日期字符串 (v1.0.9)
|
515
|
+
|
516
|
+
自动推断不完整的日期信息,支持多种模糊输入格式
|
517
|
+
|
518
|
+
Args:
|
519
|
+
date_input: 日期输入字符串,支持模糊格式如 '15', '3-15', '明天' 等
|
520
|
+
reference_date: 参考日期,默认为今天
|
521
|
+
|
522
|
+
Returns:
|
523
|
+
推断后的Date对象
|
524
|
+
|
525
|
+
Examples:
|
526
|
+
>>> Date.smart_parse('15') # 本月15号
|
527
|
+
>>> Date.smart_parse('3-15') # 3月15号(智能推断年份)
|
528
|
+
>>> Date.smart_parse('下月15') # 下个月15号
|
529
|
+
"""
|
530
|
+
return SmartDateInference.parse_smart(date_input, reference_date)
|
531
|
+
|
532
|
+
@classmethod
|
533
|
+
def infer_date(cls, year: Optional[int] = None, month: Optional[int] = None,
|
534
|
+
day: Optional[int] = None, reference_date: Optional['Date'] = None) -> 'Date':
|
535
|
+
"""智能推断日期 (v1.0.9)
|
536
|
+
|
537
|
+
根据提供的部分日期信息,智能推断完整日期
|
538
|
+
|
539
|
+
Args:
|
540
|
+
year: 年份(可选)
|
541
|
+
month: 月份(可选)
|
542
|
+
day: 日期(可选)
|
543
|
+
reference_date: 参考日期,默认为今天
|
544
|
+
|
545
|
+
Returns:
|
546
|
+
推断后的Date对象
|
547
|
+
"""
|
548
|
+
if reference_date is None:
|
549
|
+
reference_date = cls.today()
|
550
|
+
|
551
|
+
# 智能推断缺失信息
|
552
|
+
if year is None:
|
553
|
+
if month is not None and day is not None:
|
554
|
+
year = SmartDateInference.infer_year(month, day, reference_date)
|
555
|
+
else:
|
556
|
+
year = reference_date.year
|
557
|
+
|
558
|
+
if month is None:
|
559
|
+
month = reference_date.month
|
560
|
+
|
561
|
+
if day is None:
|
562
|
+
day = SmartDateInference.infer_day(year, month)
|
563
|
+
|
564
|
+
return cls(year, month, day)
|
565
|
+
|
566
|
+
@classmethod
|
567
|
+
def from_lunar(cls, year: int, month: int, day: int, is_leap: bool = False) -> 'Date':
|
568
|
+
"""从农历日期创建Date对象 (v1.0.8)
|
569
|
+
|
570
|
+
Args:
|
571
|
+
year: 农历年份
|
572
|
+
month: 农历月份
|
573
|
+
day: 农历日期
|
574
|
+
is_leap: 是否闰月
|
575
|
+
|
576
|
+
Returns:
|
577
|
+
对应的公历Date对象
|
578
|
+
|
579
|
+
Example:
|
580
|
+
>>> date = Date.from_lunar(2025, 3, 15) # 农历2025年三月十五
|
581
|
+
"""
|
582
|
+
lunar_date = LunarDate(year, month, day, is_leap)
|
583
|
+
solar_date = lunar_date.to_solar()
|
584
|
+
return cls(solar_date)
|
585
|
+
|
586
|
+
@classmethod
|
587
|
+
def from_lunar_string(cls, lunar_string: str) -> 'Date':
|
588
|
+
"""从农历字符串创建Date对象 (v1.0.8)
|
589
|
+
|
590
|
+
支持格式:
|
591
|
+
- "20250315" (农历2025年3月15日)
|
592
|
+
- "2025闰0315" (农历2025年闰3月15日)
|
593
|
+
|
594
|
+
Args:
|
595
|
+
lunar_string: 农历日期字符串
|
596
|
+
|
597
|
+
Returns:
|
598
|
+
对应的公历Date对象
|
599
|
+
"""
|
600
|
+
# 解析闰月标记
|
601
|
+
is_leap = '闰' in lunar_string
|
602
|
+
clean_string = lunar_string.replace('闰', '')
|
603
|
+
|
604
|
+
if len(clean_string) != 8:
|
605
|
+
raise InvalidDateFormatError(f"农历日期字符串格式无效: {lunar_string}")
|
606
|
+
|
607
|
+
year = int(clean_string[:4])
|
608
|
+
month = int(clean_string[4:6])
|
609
|
+
day = int(clean_string[6:8])
|
610
|
+
|
611
|
+
return cls.from_lunar(year, month, day, is_leap)
|
612
|
+
|
613
|
+
@classmethod
|
614
|
+
def set_language(cls, language_code: str) -> None:
|
615
|
+
"""设置全局语言 (v1.0.8)
|
616
|
+
|
617
|
+
一次设置,全局生效。支持中简、中繁、日、英四种语言。
|
618
|
+
|
619
|
+
Args:
|
620
|
+
language_code: 语言代码
|
621
|
+
- 'zh_CN': 中文简体
|
622
|
+
- 'zh_TW': 中文繁体
|
623
|
+
- 'ja_JP': 日语
|
624
|
+
- 'en_US': 英语
|
625
|
+
|
626
|
+
Example:
|
627
|
+
>>> Date.set_language('en_US') # 设置为英语
|
628
|
+
>>> Date.set_language('zh_TW') # 设置为繁体中文
|
629
|
+
"""
|
630
|
+
Language.set_global_language(language_code)
|
631
|
+
cls._logger.info(f"全局语言已设置为: {language_code}")
|
632
|
+
|
633
|
+
@classmethod
|
634
|
+
def get_language(cls) -> str:
|
635
|
+
"""获取当前全局语言设置 (v1.0.8)"""
|
636
|
+
return Language.get_global_language()
|
637
|
+
|
638
|
+
@classmethod
|
639
|
+
def get_supported_languages(cls) -> Dict[str, str]:
|
640
|
+
"""获取支持的语言列表 (v1.0.8)"""
|
641
|
+
return Language.get_supported_languages()
|
642
|
+
|
313
643
|
@classmethod
|
314
644
|
def date_range(cls, start: Union[str, 'Date'], end: Union[str, 'Date'],
|
315
645
|
step: int = 1) -> List['Date']:
|
@@ -430,6 +760,36 @@ class Date:
|
|
430
760
|
dt = dt - datetime.timedelta(hours=timezone_offset)
|
431
761
|
return dt.timestamp()
|
432
762
|
|
763
|
+
def to_lunar(self) -> LunarDate:
|
764
|
+
"""转为农历日期对象 (v1.0.8)
|
765
|
+
|
766
|
+
Returns:
|
767
|
+
对应的农历日期对象
|
768
|
+
|
769
|
+
Example:
|
770
|
+
>>> date = Date('20250415')
|
771
|
+
>>> lunar = date.to_lunar()
|
772
|
+
>>> print(lunar.format_chinese()) # 农历2025年三月十八
|
773
|
+
"""
|
774
|
+
return LunarDate.from_solar(self.to_date_object())
|
775
|
+
|
776
|
+
def to_lunar_string(self, compact: bool = True) -> str:
|
777
|
+
"""转为农历字符串 (v1.0.8)
|
778
|
+
|
779
|
+
Args:
|
780
|
+
compact: 是否使用紧凑格式
|
781
|
+
|
782
|
+
Returns:
|
783
|
+
农历日期字符串
|
784
|
+
|
785
|
+
Example:
|
786
|
+
>>> date = Date('20250415')
|
787
|
+
>>> print(date.to_lunar_string()) # 20250318
|
788
|
+
>>> print(date.to_lunar_string(False)) # 农历2025年三月十八
|
789
|
+
"""
|
790
|
+
lunar = self.to_lunar()
|
791
|
+
return lunar.format_compact() if compact else lunar.format_chinese()
|
792
|
+
|
433
793
|
def to_json(self, include_metadata: bool = True) -> str:
|
434
794
|
"""转为JSON字符串
|
435
795
|
|
@@ -454,7 +814,7 @@ class Date:
|
|
454
814
|
'weekday': self.get_weekday(),
|
455
815
|
'is_weekend': self.is_weekend(),
|
456
816
|
'quarter': self.get_quarter(),
|
457
|
-
'version': '1.0.
|
817
|
+
'version': '1.0.9'
|
458
818
|
})
|
459
819
|
|
460
820
|
return json.dumps(data, ensure_ascii=False)
|
@@ -628,6 +988,155 @@ class Date:
|
|
628
988
|
else:
|
629
989
|
return f"{abs(diff_days)} days ago"
|
630
990
|
|
991
|
+
def format_localized(self, format_type: str = 'full', language_code: Optional[str] = None) -> str:
|
992
|
+
"""多语言本地化格式 (v1.0.8)
|
993
|
+
|
994
|
+
Args:
|
995
|
+
format_type: 格式类型 (full, short, year_month, month_day)
|
996
|
+
language_code: 语言代码,None时使用全局设置
|
997
|
+
|
998
|
+
Returns:
|
999
|
+
本地化格式的日期字符串
|
1000
|
+
|
1001
|
+
Example:
|
1002
|
+
>>> Date.set_language('en_US')
|
1003
|
+
>>> date = Date('20250415')
|
1004
|
+
>>> print(date.format_localized()) # 04/15/2025
|
1005
|
+
>>> print(date.format_localized('short')) # 04/15/2025
|
1006
|
+
"""
|
1007
|
+
return Language.format_date(self.year, self.month, self.day, format_type, language_code)
|
1008
|
+
|
1009
|
+
def format_weekday_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
|
1010
|
+
"""多语言星期几格式 (v1.0.8)
|
1011
|
+
|
1012
|
+
Args:
|
1013
|
+
short: 是否使用短名称
|
1014
|
+
language_code: 语言代码,None时使用全局设置
|
1015
|
+
|
1016
|
+
Returns:
|
1017
|
+
本地化的星期几名称
|
1018
|
+
|
1019
|
+
Example:
|
1020
|
+
>>> Date.set_language('ja_JP')
|
1021
|
+
>>> date = Date('20250415') # 星期二
|
1022
|
+
>>> print(date.format_weekday_localized()) # 火曜日
|
1023
|
+
>>> print(date.format_weekday_localized(short=True)) # 火
|
1024
|
+
"""
|
1025
|
+
weekday_index = self.get_weekday()
|
1026
|
+
return Language.get_weekday_name(weekday_index, short, language_code)
|
1027
|
+
|
1028
|
+
def format_month_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
|
1029
|
+
"""多语言月份格式 (v1.0.8)
|
1030
|
+
|
1031
|
+
Args:
|
1032
|
+
short: 是否使用短名称
|
1033
|
+
language_code: 语言代码,None时使用全局设置
|
1034
|
+
|
1035
|
+
Returns:
|
1036
|
+
本地化的月份名称
|
1037
|
+
"""
|
1038
|
+
return Language.get_month_name(self.month, short, language_code)
|
1039
|
+
|
1040
|
+
def format_quarter_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
|
1041
|
+
"""多语言季度格式 (v1.0.8)
|
1042
|
+
|
1043
|
+
Args:
|
1044
|
+
short: 是否使用短名称
|
1045
|
+
language_code: 语言代码,None时使用全局设置
|
1046
|
+
|
1047
|
+
Returns:
|
1048
|
+
本地化的季度名称
|
1049
|
+
"""
|
1050
|
+
quarter = self.get_quarter()
|
1051
|
+
return Language.get_quarter_name(quarter, short, language_code)
|
1052
|
+
|
1053
|
+
def format_relative_localized(self, reference_date: Optional['Date'] = None,
|
1054
|
+
language_code: Optional[str] = None) -> str:
|
1055
|
+
"""多语言相对时间格式 (v1.0.8)
|
1056
|
+
|
1057
|
+
Args:
|
1058
|
+
reference_date: 参考日期,None时使用今天
|
1059
|
+
language_code: 语言代码,None时使用全局设置
|
1060
|
+
|
1061
|
+
Returns:
|
1062
|
+
本地化的相对时间描述
|
1063
|
+
|
1064
|
+
Example:
|
1065
|
+
>>> Date.set_language('en_US')
|
1066
|
+
>>> today = Date.today()
|
1067
|
+
>>> tomorrow = today.add_days(1)
|
1068
|
+
>>> print(tomorrow.format_relative_localized()) # tomorrow
|
1069
|
+
"""
|
1070
|
+
if reference_date is None:
|
1071
|
+
reference_date = Date.today()
|
1072
|
+
|
1073
|
+
diff_days = reference_date.calculate_difference_days(self)
|
1074
|
+
|
1075
|
+
if diff_days == 0:
|
1076
|
+
return Language.format_relative_time('today', language_code=language_code)
|
1077
|
+
elif diff_days == 1:
|
1078
|
+
return Language.format_relative_time('tomorrow', language_code=language_code)
|
1079
|
+
elif diff_days == -1:
|
1080
|
+
return Language.format_relative_time('yesterday', language_code=language_code)
|
1081
|
+
elif diff_days > 0:
|
1082
|
+
if diff_days <= 6:
|
1083
|
+
return Language.format_relative_time('days_later', diff_days, language_code)
|
1084
|
+
elif diff_days <= 28:
|
1085
|
+
weeks = diff_days // 7
|
1086
|
+
return Language.format_relative_time('weeks_later', weeks, language_code)
|
1087
|
+
elif diff_days <= 365:
|
1088
|
+
months = diff_days // 30
|
1089
|
+
return Language.format_relative_time('months_later', months, language_code)
|
1090
|
+
else:
|
1091
|
+
years = diff_days // 365
|
1092
|
+
return Language.format_relative_time('years_later', years, language_code)
|
1093
|
+
else:
|
1094
|
+
abs_days = abs(diff_days)
|
1095
|
+
if abs_days <= 6:
|
1096
|
+
return Language.format_relative_time('days_ago', abs_days, language_code)
|
1097
|
+
elif abs_days <= 28:
|
1098
|
+
weeks = abs_days // 7
|
1099
|
+
return Language.format_relative_time('weeks_ago', weeks, language_code)
|
1100
|
+
elif abs_days <= 365:
|
1101
|
+
months = abs_days // 30
|
1102
|
+
return Language.format_relative_time('months_ago', months, language_code)
|
1103
|
+
else:
|
1104
|
+
years = abs_days // 365
|
1105
|
+
return Language.format_relative_time('years_ago', years, language_code)
|
1106
|
+
|
1107
|
+
def format_lunar(self, include_year: bool = True, include_zodiac: bool = False,
|
1108
|
+
language_code: Optional[str] = None) -> str:
|
1109
|
+
"""农历格式化 (v1.0.8)
|
1110
|
+
|
1111
|
+
Args:
|
1112
|
+
include_year: 是否包含年份
|
1113
|
+
include_zodiac: 是否包含生肖
|
1114
|
+
language_code: 语言代码,None时使用全局设置
|
1115
|
+
|
1116
|
+
Returns:
|
1117
|
+
农历日期字符串
|
1118
|
+
|
1119
|
+
Example:
|
1120
|
+
>>> date = Date('20250415')
|
1121
|
+
>>> print(date.format_lunar()) # 农历2025年三月十八
|
1122
|
+
>>> print(date.format_lunar(include_zodiac=True)) # 乙巳(蛇)年三月十八
|
1123
|
+
"""
|
1124
|
+
lunar = self.to_lunar()
|
1125
|
+
return lunar.format_chinese(include_year, include_zodiac)
|
1126
|
+
|
1127
|
+
def format_lunar_compact(self) -> str:
|
1128
|
+
"""农历紧凑格式 (v1.0.8)
|
1129
|
+
|
1130
|
+
Returns:
|
1131
|
+
农历紧凑格式字符串
|
1132
|
+
|
1133
|
+
Example:
|
1134
|
+
>>> date = Date('20250415')
|
1135
|
+
>>> print(date.format_lunar_compact()) # 20250318
|
1136
|
+
"""
|
1137
|
+
lunar = self.to_lunar()
|
1138
|
+
return lunar.format_compact()
|
1139
|
+
|
631
1140
|
# =============================================
|
632
1141
|
# get_* 系列:获取方法
|
633
1142
|
# =============================================
|
@@ -755,6 +1264,42 @@ class Date:
|
|
755
1264
|
return (self.month in quarter_end_months and
|
756
1265
|
self.day == quarter_end_months[self.month])
|
757
1266
|
|
1267
|
+
def is_lunar_new_year(self) -> bool:
|
1268
|
+
"""是否为农历新年 (v1.0.8)
|
1269
|
+
|
1270
|
+
Returns:
|
1271
|
+
是否为农历正月初一
|
1272
|
+
"""
|
1273
|
+
lunar = self.to_lunar()
|
1274
|
+
return lunar.month == 1 and lunar.day == 1 and not lunar.is_leap
|
1275
|
+
|
1276
|
+
def is_lunar_month_start(self) -> bool:
|
1277
|
+
"""是否为农历月初 (v1.0.8)
|
1278
|
+
|
1279
|
+
Returns:
|
1280
|
+
是否为农历月初一
|
1281
|
+
"""
|
1282
|
+
lunar = self.to_lunar()
|
1283
|
+
return lunar.day == 1
|
1284
|
+
|
1285
|
+
def is_lunar_month_mid(self) -> bool:
|
1286
|
+
"""是否为农历月中 (v1.0.8)
|
1287
|
+
|
1288
|
+
Returns:
|
1289
|
+
是否为农历十五
|
1290
|
+
"""
|
1291
|
+
lunar = self.to_lunar()
|
1292
|
+
return lunar.day == 15
|
1293
|
+
|
1294
|
+
def is_lunar_leap_month(self) -> bool:
|
1295
|
+
"""是否在农历闰月 (v1.0.8)
|
1296
|
+
|
1297
|
+
Returns:
|
1298
|
+
是否为农历闰月
|
1299
|
+
"""
|
1300
|
+
lunar = self.to_lunar()
|
1301
|
+
return lunar.is_leap
|
1302
|
+
|
758
1303
|
def is_holiday(self, country: str = 'CN') -> bool:
|
759
1304
|
"""是否为节假日(增强版实现)
|
760
1305
|
|
@@ -1012,6 +1557,326 @@ class Date:
|
|
1012
1557
|
"""
|
1013
1558
|
return [date.add_days(days) for date in dates]
|
1014
1559
|
|
1560
|
+
# =============================================
|
1561
|
+
# 异步批量处理方法 (v1.0.9)
|
1562
|
+
# =============================================
|
1563
|
+
|
1564
|
+
@classmethod
|
1565
|
+
async def async_batch_create(cls, date_strings: List[str]) -> List['Date']:
|
1566
|
+
"""异步批量创建Date对象 (v1.0.9)
|
1567
|
+
|
1568
|
+
适用于大量日期对象的创建,使用异步处理提升性能
|
1569
|
+
|
1570
|
+
Args:
|
1571
|
+
date_strings: 日期字符串列表
|
1572
|
+
|
1573
|
+
Returns:
|
1574
|
+
Date对象列表
|
1575
|
+
"""
|
1576
|
+
async def create_single(date_str: str) -> 'Date':
|
1577
|
+
return cls(date_str)
|
1578
|
+
|
1579
|
+
tasks = [create_single(date_str) for date_str in date_strings]
|
1580
|
+
return await asyncio.gather(*tasks)
|
1581
|
+
|
1582
|
+
@classmethod
|
1583
|
+
async def async_batch_format(cls, dates: List['Date'], format_type: str = 'iso') -> List[str]:
|
1584
|
+
"""异步批量格式化日期 (v1.0.9)
|
1585
|
+
|
1586
|
+
Args:
|
1587
|
+
dates: Date对象列表
|
1588
|
+
format_type: 格式类型
|
1589
|
+
|
1590
|
+
Returns:
|
1591
|
+
格式化后的字符串列表
|
1592
|
+
"""
|
1593
|
+
format_methods = {
|
1594
|
+
'iso': lambda d: d.format_iso(),
|
1595
|
+
'chinese': lambda d: d.format_chinese(),
|
1596
|
+
'compact': lambda d: d.format_compact(),
|
1597
|
+
'slash': lambda d: d.format_slash(),
|
1598
|
+
'dot': lambda d: d.format_dot(),
|
1599
|
+
'default': lambda d: d.format_default()
|
1600
|
+
}
|
1601
|
+
|
1602
|
+
format_func = format_methods.get(format_type, lambda d: d.format_default())
|
1603
|
+
|
1604
|
+
async def format_single(date: 'Date') -> str:
|
1605
|
+
return format_func(date)
|
1606
|
+
|
1607
|
+
tasks = [format_single(date) for date in dates]
|
1608
|
+
return await asyncio.gather(*tasks)
|
1609
|
+
|
1610
|
+
@classmethod
|
1611
|
+
async def async_batch_process(cls, dates: List['Date'], operation: str, **kwargs) -> List['Date']:
|
1612
|
+
"""异步批量处理操作 (v1.0.9)
|
1613
|
+
|
1614
|
+
Args:
|
1615
|
+
dates: Date对象列表
|
1616
|
+
operation: 操作类型 ('add_days', 'add_months', 'add_years')
|
1617
|
+
**kwargs: 操作参数
|
1618
|
+
|
1619
|
+
Returns:
|
1620
|
+
处理后的Date对象列表
|
1621
|
+
"""
|
1622
|
+
async def process_single(date: 'Date') -> 'Date':
|
1623
|
+
if operation == 'add_days':
|
1624
|
+
return date.add_days(kwargs.get('days', 0))
|
1625
|
+
elif operation == 'add_months':
|
1626
|
+
return date.add_months(kwargs.get('months', 0))
|
1627
|
+
elif operation == 'add_years':
|
1628
|
+
return date.add_years(kwargs.get('years', 0))
|
1629
|
+
else:
|
1630
|
+
return date
|
1631
|
+
|
1632
|
+
tasks = [process_single(date) for date in dates]
|
1633
|
+
return await asyncio.gather(*tasks)
|
1634
|
+
|
1635
|
+
# =============================================
|
1636
|
+
# 日期范围操作方法 (v1.0.9)
|
1637
|
+
# =============================================
|
1638
|
+
|
1639
|
+
@classmethod
|
1640
|
+
def create_range(cls, start_date: Union[str, 'Date'], end_date: Union[str, 'Date']) -> 'DateRange':
|
1641
|
+
"""创建日期范围 (v1.0.9)
|
1642
|
+
|
1643
|
+
Args:
|
1644
|
+
start_date: 开始日期
|
1645
|
+
end_date: 结束日期
|
1646
|
+
|
1647
|
+
Returns:
|
1648
|
+
DateRange对象
|
1649
|
+
"""
|
1650
|
+
if isinstance(start_date, str):
|
1651
|
+
start_date = cls(start_date)
|
1652
|
+
if isinstance(end_date, str):
|
1653
|
+
end_date = cls(end_date)
|
1654
|
+
|
1655
|
+
return DateRange(start_date, end_date)
|
1656
|
+
|
1657
|
+
def in_range(self, start_date: 'Date', end_date: 'Date') -> bool:
|
1658
|
+
"""检查是否在日期范围内 (v1.0.9)
|
1659
|
+
|
1660
|
+
Args:
|
1661
|
+
start_date: 开始日期
|
1662
|
+
end_date: 结束日期
|
1663
|
+
|
1664
|
+
Returns:
|
1665
|
+
是否在范围内
|
1666
|
+
"""
|
1667
|
+
return start_date <= self <= end_date
|
1668
|
+
|
1669
|
+
@classmethod
|
1670
|
+
def generate_range(cls, start_date: Union[str, 'Date'], days: int,
|
1671
|
+
step: int = 1, include_weekends: bool = True) -> List['Date']:
|
1672
|
+
"""生成日期范围序列 (v1.0.9)
|
1673
|
+
|
1674
|
+
Args:
|
1675
|
+
start_date: 开始日期
|
1676
|
+
days: 天数
|
1677
|
+
step: 步长
|
1678
|
+
include_weekends: 是否包含周末
|
1679
|
+
|
1680
|
+
Returns:
|
1681
|
+
日期列表
|
1682
|
+
"""
|
1683
|
+
if isinstance(start_date, str):
|
1684
|
+
start_date = cls(start_date)
|
1685
|
+
|
1686
|
+
dates = []
|
1687
|
+
current = start_date
|
1688
|
+
|
1689
|
+
for _ in range(days):
|
1690
|
+
if include_weekends or not current.is_weekend():
|
1691
|
+
dates.append(current)
|
1692
|
+
current = current.add_days(step)
|
1693
|
+
|
1694
|
+
return dates
|
1695
|
+
|
1696
|
+
@classmethod
|
1697
|
+
def date_ranges_intersect(cls, range1: 'DateRange', range2: 'DateRange') -> bool:
|
1698
|
+
"""检查两个日期范围是否有交集 (v1.0.9)"""
|
1699
|
+
return range1.intersect(range2) is not None
|
1700
|
+
|
1701
|
+
@classmethod
|
1702
|
+
def merge_date_ranges(cls, ranges: List['DateRange']) -> List['DateRange']:
|
1703
|
+
"""合并重叠的日期范围 (v1.0.9)
|
1704
|
+
|
1705
|
+
Args:
|
1706
|
+
ranges: 日期范围列表
|
1707
|
+
|
1708
|
+
Returns:
|
1709
|
+
合并后的日期范围列表
|
1710
|
+
"""
|
1711
|
+
if not ranges:
|
1712
|
+
return []
|
1713
|
+
|
1714
|
+
# 按开始日期排序
|
1715
|
+
sorted_ranges = sorted(ranges, key=lambda r: r.start)
|
1716
|
+
merged = [sorted_ranges[0]]
|
1717
|
+
|
1718
|
+
for current_range in sorted_ranges[1:]:
|
1719
|
+
last_range = merged[-1]
|
1720
|
+
|
1721
|
+
# 检查是否重叠或相邻
|
1722
|
+
if current_range.start <= last_range.end.add_days(1):
|
1723
|
+
# 合并范围
|
1724
|
+
merged[-1] = DateRange(last_range.start, max(last_range.end, current_range.end))
|
1725
|
+
else:
|
1726
|
+
# 不重叠,添加新范围
|
1727
|
+
merged.append(current_range)
|
1728
|
+
|
1729
|
+
return merged
|
1730
|
+
|
1731
|
+
# =============================================
|
1732
|
+
# 数据导入导出方法 (v1.0.9)
|
1733
|
+
# =============================================
|
1734
|
+
|
1735
|
+
@classmethod
|
1736
|
+
def from_csv(cls, file_path: str, date_column: str = 'date',
|
1737
|
+
format_hint: Optional[str] = None) -> List['Date']:
|
1738
|
+
"""从CSV文件导入日期 (v1.0.9)
|
1739
|
+
|
1740
|
+
Args:
|
1741
|
+
file_path: CSV文件路径
|
1742
|
+
date_column: 日期列名
|
1743
|
+
format_hint: 日期格式提示
|
1744
|
+
|
1745
|
+
Returns:
|
1746
|
+
Date对象列表
|
1747
|
+
"""
|
1748
|
+
dates = []
|
1749
|
+
|
1750
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
1751
|
+
reader = csv.DictReader(file)
|
1752
|
+
for row in reader:
|
1753
|
+
if date_column in row:
|
1754
|
+
try:
|
1755
|
+
date_str = row[date_column].strip()
|
1756
|
+
if date_str:
|
1757
|
+
dates.append(cls(date_str))
|
1758
|
+
except (InvalidDateFormatError, InvalidDateValueError) as e:
|
1759
|
+
cls._logger.warning(f"跳过无效日期: {row[date_column]} - {e}")
|
1760
|
+
|
1761
|
+
return dates
|
1762
|
+
|
1763
|
+
@classmethod
|
1764
|
+
def to_csv(cls, dates: List['Date'], file_path: str,
|
1765
|
+
include_metadata: bool = False, format_type: str = 'iso') -> None:
|
1766
|
+
"""导出日期到CSV文件 (v1.0.9)
|
1767
|
+
|
1768
|
+
Args:
|
1769
|
+
dates: Date对象列表
|
1770
|
+
file_path: 输出文件路径
|
1771
|
+
include_metadata: 是否包含元数据
|
1772
|
+
format_type: 日期格式类型
|
1773
|
+
"""
|
1774
|
+
with open(file_path, 'w', newline='', encoding='utf-8') as file:
|
1775
|
+
fieldnames = ['date']
|
1776
|
+
if include_metadata:
|
1777
|
+
fieldnames.extend(['weekday', 'quarter', 'is_weekend', 'format'])
|
1778
|
+
|
1779
|
+
writer = csv.DictWriter(file, fieldnames=fieldnames)
|
1780
|
+
writer.writeheader()
|
1781
|
+
|
1782
|
+
for date in dates:
|
1783
|
+
row = {'date': getattr(date, f'format_{format_type}')()}
|
1784
|
+
if include_metadata:
|
1785
|
+
row.update({
|
1786
|
+
'weekday': date.get_weekday(),
|
1787
|
+
'quarter': date.get_quarter(),
|
1788
|
+
'is_weekend': date.is_weekend(),
|
1789
|
+
'format': date._input_format
|
1790
|
+
})
|
1791
|
+
writer.writerow(row)
|
1792
|
+
|
1793
|
+
@classmethod
|
1794
|
+
def from_json_file(cls, file_path: str) -> List['Date']:
|
1795
|
+
"""从JSON文件导入日期 (v1.0.9)
|
1796
|
+
|
1797
|
+
Args:
|
1798
|
+
file_path: JSON文件路径
|
1799
|
+
|
1800
|
+
Returns:
|
1801
|
+
Date对象列表
|
1802
|
+
"""
|
1803
|
+
with open(file_path, 'r', encoding='utf-8') as file:
|
1804
|
+
data = json.load(file)
|
1805
|
+
|
1806
|
+
dates = []
|
1807
|
+
if isinstance(data, list):
|
1808
|
+
for item in data:
|
1809
|
+
try:
|
1810
|
+
if isinstance(item, str):
|
1811
|
+
dates.append(cls(item))
|
1812
|
+
elif isinstance(item, dict):
|
1813
|
+
# 支持to_dict()格式的JSON
|
1814
|
+
if 'year' in item and 'month' in item and 'day' in item:
|
1815
|
+
dates.append(cls(item['year'], item['month'], item['day']))
|
1816
|
+
elif 'date' in item:
|
1817
|
+
dates.append(cls(item['date']))
|
1818
|
+
elif 'iso_string' in item:
|
1819
|
+
dates.append(cls(item['iso_string']))
|
1820
|
+
elif 'compact_string' in item:
|
1821
|
+
dates.append(cls(item['compact_string']))
|
1822
|
+
except (InvalidDateFormatError, InvalidDateValueError) as e:
|
1823
|
+
cls._logger.warning(f"跳过无效日期数据: {item} - {e}")
|
1824
|
+
|
1825
|
+
return dates
|
1826
|
+
|
1827
|
+
@classmethod
|
1828
|
+
def to_json_file(cls, dates: List['Date'], file_path: str,
|
1829
|
+
include_metadata: bool = False) -> None:
|
1830
|
+
"""导出日期到JSON文件 (v1.0.9)
|
1831
|
+
|
1832
|
+
Args:
|
1833
|
+
dates: Date对象列表
|
1834
|
+
file_path: 输出文件路径
|
1835
|
+
include_metadata: 是否包含元数据
|
1836
|
+
"""
|
1837
|
+
data = [date.to_dict(include_metadata) for date in dates]
|
1838
|
+
|
1839
|
+
with open(file_path, 'w', encoding='utf-8') as file:
|
1840
|
+
json.dump(data, file, ensure_ascii=False, indent=2)
|
1841
|
+
|
1842
|
+
# =============================================
|
1843
|
+
# 性能优化方法 (v1.0.9)
|
1844
|
+
# =============================================
|
1845
|
+
|
1846
|
+
@classmethod
|
1847
|
+
def clear_cache(cls):
|
1848
|
+
"""清空全局缓存 (v1.0.9)"""
|
1849
|
+
_performance_cache.clear()
|
1850
|
+
|
1851
|
+
@classmethod
|
1852
|
+
def get_cache_stats(cls) -> Dict[str, Any]:
|
1853
|
+
"""获取缓存统计信息 (v1.0.9)"""
|
1854
|
+
return {
|
1855
|
+
'object_cache_size': len(_performance_cache._object_cache),
|
1856
|
+
'format_cache_size': len(_performance_cache._format_cache),
|
1857
|
+
'calculation_cache_size': len(_performance_cache._calculation_cache)
|
1858
|
+
}
|
1859
|
+
|
1860
|
+
def _optimized_format(self, format_type: str) -> str:
|
1861
|
+
"""优化的格式化方法 (v1.0.9)"""
|
1862
|
+
cache_key = f"{self._cache_key}-{format_type}"
|
1863
|
+
|
1864
|
+
def format_func():
|
1865
|
+
if format_type == 'iso':
|
1866
|
+
return self.format_iso()
|
1867
|
+
elif format_type == 'chinese':
|
1868
|
+
return self.format_chinese()
|
1869
|
+
elif format_type == 'compact':
|
1870
|
+
return self.format_compact()
|
1871
|
+
else:
|
1872
|
+
return self.format_default()
|
1873
|
+
|
1874
|
+
return _performance_cache.get_or_calculate(cache_key, format_func)
|
1875
|
+
|
1876
|
+
def get_cache_key(self) -> str:
|
1877
|
+
"""获取对象的缓存键 (v1.0.9)"""
|
1878
|
+
return self._cache_key
|
1879
|
+
|
1015
1880
|
def apply_business_rule(self, rule: str, **kwargs) -> 'Date':
|
1016
1881
|
"""应用业务规则
|
1017
1882
|
|
@@ -1088,6 +1953,58 @@ class Date:
|
|
1088
1953
|
def __hash__(self) -> int:
|
1089
1954
|
return hash(self.to_tuple())
|
1090
1955
|
|
1956
|
+
def compare_lunar(self, other: 'Date') -> int:
|
1957
|
+
"""农历日期比较 (v1.0.8)
|
1958
|
+
|
1959
|
+
Args:
|
1960
|
+
other: 另一个Date对象
|
1961
|
+
|
1962
|
+
Returns:
|
1963
|
+
-1: self < other, 0: self == other, 1: self > other
|
1964
|
+
|
1965
|
+
Example:
|
1966
|
+
>>> date1 = Date.from_lunar(2025, 1, 1) # 农历正月初一
|
1967
|
+
>>> date2 = Date.from_lunar(2025, 1, 15) # 农历正月十五
|
1968
|
+
>>> print(date1.compare_lunar(date2)) # -1
|
1969
|
+
"""
|
1970
|
+
lunar_self = self.to_lunar()
|
1971
|
+
lunar_other = other.to_lunar()
|
1972
|
+
|
1973
|
+
if lunar_self < lunar_other:
|
1974
|
+
return -1
|
1975
|
+
elif lunar_self > lunar_other:
|
1976
|
+
return 1
|
1977
|
+
else:
|
1978
|
+
return 0
|
1979
|
+
|
1980
|
+
def is_same_lunar_month(self, other: 'Date') -> bool:
|
1981
|
+
"""是否同一农历月份 (v1.0.8)
|
1982
|
+
|
1983
|
+
Args:
|
1984
|
+
other: 另一个Date对象
|
1985
|
+
|
1986
|
+
Returns:
|
1987
|
+
是否为同一农历月份
|
1988
|
+
"""
|
1989
|
+
lunar_self = self.to_lunar()
|
1990
|
+
lunar_other = other.to_lunar()
|
1991
|
+
return (lunar_self.year == lunar_other.year and
|
1992
|
+
lunar_self.month == lunar_other.month and
|
1993
|
+
lunar_self.is_leap == lunar_other.is_leap)
|
1994
|
+
|
1995
|
+
def is_same_lunar_day(self, other: 'Date') -> bool:
|
1996
|
+
"""是否同一农历日期 (v1.0.8)
|
1997
|
+
|
1998
|
+
Args:
|
1999
|
+
other: 另一个Date对象
|
2000
|
+
|
2001
|
+
Returns:
|
2002
|
+
是否为同一农历日期
|
2003
|
+
"""
|
2004
|
+
lunar_self = self.to_lunar()
|
2005
|
+
lunar_other = other.to_lunar()
|
2006
|
+
return lunar_self == lunar_other
|
2007
|
+
|
1091
2008
|
# =============================================
|
1092
2009
|
# 向后兼容的旧API
|
1093
2010
|
# =============================================
|