staran 1.0.8__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/core.py +563 -9
- staran/date/examples/v109_features_demo.py +302 -0
- staran/date/tests/run_tests.py +77 -6
- staran/date/tests/test_v109_features.py +316 -0
- staran-1.0.9.dist-info/METADATA +214 -0
- {staran-1.0.8.dist-info → staran-1.0.9.dist-info}/RECORD +9 -7
- staran-1.0.8.dist-info/METADATA +0 -371
- {staran-1.0.8.dist-info → staran-1.0.9.dist-info}/WHEEL +0 -0
- {staran-1.0.8.dist-info → staran-1.0.9.dist-info}/licenses/LICENSE +0 -0
- {staran-1.0.8.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,13 @@ 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
|
19
31
|
|
20
32
|
# 导入农历和多语言模块
|
21
33
|
from .lunar import LunarDate
|
@@ -34,6 +46,149 @@ class InvalidDateValueError(DateError):
|
|
34
46
|
pass
|
35
47
|
|
36
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
|
+
|
37
192
|
class DateLogger:
|
38
193
|
"""企业级日志记录器
|
39
194
|
|
@@ -78,17 +233,24 @@ class DateLogger:
|
|
78
233
|
|
79
234
|
|
80
235
|
class Date:
|
81
|
-
"""企业级日期处理类
|
236
|
+
"""企业级日期处理类 v1.0.9
|
82
237
|
|
83
238
|
提供智能格式记忆、统一API命名和完整的日期处理功能。
|
84
239
|
支持YYYY、YYYYMM、YYYYMMDD等多种输入格式,并在运算中
|
85
240
|
自动保持原始格式。
|
86
241
|
|
87
|
-
v1.0.8
|
242
|
+
v1.0.8 功能:
|
88
243
|
- 农历日期支持 (from_lunar, to_lunar, format_lunar等)
|
89
244
|
- 多语言配置 (中简、中繁、日、英四种语言)
|
90
245
|
- 全局语言设置,一次配置全局生效
|
91
246
|
|
247
|
+
v1.0.9 新增功能:
|
248
|
+
- 智能日期推断 (smart_parse, infer_date等)
|
249
|
+
- 异步批量处理 (async_batch_create, async_batch_format等)
|
250
|
+
- 高级业务规则 (扩展的业务规则引擎)
|
251
|
+
- 日期范围操作 (DateRange, 交集、并集等)
|
252
|
+
- 性能优化 (多级缓存、内存优化)
|
253
|
+
|
92
254
|
特性:
|
93
255
|
----
|
94
256
|
- 120+个统一命名的API方法
|
@@ -98,6 +260,8 @@ class Date:
|
|
98
260
|
- 多语言本地化支持
|
99
261
|
- 类型安全的日期转换
|
100
262
|
- 向后兼容的旧API支持
|
263
|
+
- 异步处理支持
|
264
|
+
- 智能推断和自动修复
|
101
265
|
|
102
266
|
Examples:
|
103
267
|
---------
|
@@ -117,6 +281,14 @@ class Date:
|
|
117
281
|
>>> Date.set_language('en_US') # 设置全局语言为英语
|
118
282
|
>>> print(date2.format_localized()) # 04/15/2025
|
119
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'])
|
120
292
|
>>> # 统一API命名
|
121
293
|
>>> date = Date('20250415')
|
122
294
|
>>> print(date.format_iso()) # 2025-04-15
|
@@ -132,7 +304,7 @@ class Date:
|
|
132
304
|
当日期值超出有效范围时抛出
|
133
305
|
"""
|
134
306
|
|
135
|
-
__slots__ = ('year', 'month', 'day', '_input_format')
|
307
|
+
__slots__ = ('year', 'month', 'day', '_input_format', '_cache_key')
|
136
308
|
|
137
309
|
# 类级别的日志记录器
|
138
310
|
_logger = DateLogger()
|
@@ -155,6 +327,7 @@ class Date:
|
|
155
327
|
self.month: int = 1
|
156
328
|
self.day: int = 1
|
157
329
|
self._input_format: str = 'iso' # 默认ISO格式
|
330
|
+
self._cache_key: Optional[str] = None # 缓存键(v1.0.9)
|
158
331
|
|
159
332
|
# 根据参数类型进行初始化
|
160
333
|
if len(args) == 1 and not kwargs:
|
@@ -176,8 +349,15 @@ class Date:
|
|
176
349
|
else:
|
177
350
|
raise InvalidDateValueError("无效的参数组合")
|
178
351
|
|
352
|
+
# 生成缓存键 (v1.0.9)
|
353
|
+
self._generate_cache_key()
|
354
|
+
|
179
355
|
self._logger.info(f"创建Date对象: {self.year}-{self.month:02d}-{self.day:02d}, 格式: {self._input_format}")
|
180
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
|
+
|
181
361
|
def _init_from_string(self, date_string: str):
|
182
362
|
"""从字符串初始化"""
|
183
363
|
# 移除分隔符并清理字符串
|
@@ -329,6 +509,60 @@ class Date:
|
|
329
509
|
"""创建今日Date对象"""
|
330
510
|
return cls(datetime.date.today())
|
331
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
|
+
|
332
566
|
@classmethod
|
333
567
|
def from_lunar(cls, year: int, month: int, day: int, is_leap: bool = False) -> 'Date':
|
334
568
|
"""从农历日期创建Date对象 (v1.0.8)
|
@@ -580,7 +814,7 @@ class Date:
|
|
580
814
|
'weekday': self.get_weekday(),
|
581
815
|
'is_weekend': self.is_weekend(),
|
582
816
|
'quarter': self.get_quarter(),
|
583
|
-
'version': '1.0.
|
817
|
+
'version': '1.0.9'
|
584
818
|
})
|
585
819
|
|
586
820
|
return json.dumps(data, ensure_ascii=False)
|
@@ -1323,6 +1557,326 @@ class Date:
|
|
1323
1557
|
"""
|
1324
1558
|
return [date.add_days(days) for date in dates]
|
1325
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
|
+
|
1326
1880
|
def apply_business_rule(self, rule: str, **kwargs) -> 'Date':
|
1327
1881
|
"""应用业务规则
|
1328
1882
|
|