staran 1.0.7__py3-none-any.whl → 1.0.8__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 CHANGED
@@ -6,9 +6,14 @@ Staran Date 模块
6
6
  ================
7
7
 
8
8
  提供企业级日期处理功能。
9
+
10
+ v1.0.8 新增功能:
11
+ - 农历日期支持
12
+ - 多语言本地化
13
+ - 全局语言配置
9
14
  """
10
15
 
11
- __version__ = "1.0.7"
16
+ __version__ = "1.0.8"
12
17
  __author__ = "Staran Team"
13
18
  __email__ = "team@staran.dev"
14
19
 
@@ -21,6 +26,10 @@ from .core import (
21
26
  InvalidDateValueError
22
27
  )
23
28
 
29
+ # 导入新增的农历和多语言功能 (v1.0.8)
30
+ from .lunar import LunarDate
31
+ from .i18n import Language
32
+
24
33
  # 导出便捷函数
25
34
  def today() -> Date:
26
35
  """
@@ -34,13 +43,64 @@ def from_string(date_string: str) -> Date:
34
43
  """
35
44
  return Date.from_string(date_string)
36
45
 
46
+ def from_lunar(year: int, month: int, day: int, is_leap: bool = False) -> Date:
47
+ """
48
+ 从农历日期创建Date对象 (v1.0.8)
49
+
50
+ Args:
51
+ year: 农历年份
52
+ month: 农历月份
53
+ day: 农历日期
54
+ is_leap: 是否闰月
55
+
56
+ Returns:
57
+ 对应的公历Date对象
58
+ """
59
+ return Date.from_lunar(year, month, day, is_leap)
60
+
61
+ def set_language(language_code: str) -> None:
62
+ """
63
+ 设置全局语言 (v1.0.8)
64
+
65
+ 一次设置,全局生效。支持中简、中繁、日、英四种语言。
66
+
67
+ Args:
68
+ language_code: 语言代码
69
+ - 'zh_CN': 中文简体
70
+ - 'zh_TW': 中文繁体
71
+ - 'ja_JP': 日语
72
+ - 'en_US': 英语
73
+ """
74
+ Date.set_language(language_code)
75
+
76
+ def get_language() -> str:
77
+ """
78
+ 获取当前全局语言设置 (v1.0.8)
79
+
80
+ Returns:
81
+ 当前语言代码
82
+ """
83
+ return Date.get_language()
84
+
37
85
  # 定义公共API
38
86
  __all__ = [
87
+ # 核心类
39
88
  'Date',
40
89
  'DateLogger',
41
90
  'DateError',
42
91
  'InvalidDateFormatError',
43
92
  'InvalidDateValueError',
93
+
94
+ # v1.0.8 新增类
95
+ 'LunarDate',
96
+ 'Language',
97
+
98
+ # 便捷函数
44
99
  'today',
45
- 'from_string'
100
+ 'from_string',
101
+
102
+ # v1.0.8 新增函数
103
+ 'from_lunar',
104
+ 'set_language',
105
+ 'get_language'
46
106
  ]
staran/date/core.py CHANGED
@@ -17,6 +17,10 @@ import time
17
17
  from typing import Union, Optional, Tuple, Dict, Any, List
18
18
  from functools import lru_cache
19
19
 
20
+ # 导入农历和多语言模块
21
+ from .lunar import LunarDate
22
+ from .i18n import Language
23
+
20
24
  class DateError(ValueError):
21
25
  """Date模块的特定异常基类"""
22
26
  pass
@@ -80,11 +84,18 @@ class Date:
80
84
  支持YYYY、YYYYMM、YYYYMMDD等多种输入格式,并在运算中
81
85
  自动保持原始格式。
82
86
 
87
+ v1.0.8 新增功能:
88
+ - 农历日期支持 (from_lunar, to_lunar, format_lunar等)
89
+ - 多语言配置 (中简、中繁、日、英四种语言)
90
+ - 全局语言设置,一次配置全局生效
91
+
83
92
  特性:
84
93
  ----
85
- - 86+个统一命名的API方法
94
+ - 120+个统一命名的API方法
86
95
  - 智能格式记忆和保持
87
96
  - 企业级日志记录
97
+ - 农历与公历互转
98
+ - 多语言本地化支持
88
99
  - 类型安全的日期转换
89
100
  - 向后兼容的旧API支持
90
101
 
@@ -98,6 +109,14 @@ class Date:
98
109
  >>> print(date1.add_months(2)) # 202506
99
110
  >>> print(date2.add_days(10)) # 20250425
100
111
  >>>
112
+ >>> # 农历支持 (v1.0.8)
113
+ >>> lunar_date = Date.from_lunar(2025, 3, 15) # 农历2025年三月十五
114
+ >>> print(lunar_date.to_lunar().format_chinese()) # 农历2025年三月十五
115
+ >>>
116
+ >>> # 多语言支持 (v1.0.8)
117
+ >>> Date.set_language('en_US') # 设置全局语言为英语
118
+ >>> print(date2.format_localized()) # 04/15/2025
119
+ >>> print(date2.format_weekday_localized()) # Tuesday
101
120
  >>> # 统一API命名
102
121
  >>> date = Date('20250415')
103
122
  >>> print(date.format_iso()) # 2025-04-15
@@ -310,6 +329,83 @@ class Date:
310
329
  """创建今日Date对象"""
311
330
  return cls(datetime.date.today())
312
331
 
332
+ @classmethod
333
+ def from_lunar(cls, year: int, month: int, day: int, is_leap: bool = False) -> 'Date':
334
+ """从农历日期创建Date对象 (v1.0.8)
335
+
336
+ Args:
337
+ year: 农历年份
338
+ month: 农历月份
339
+ day: 农历日期
340
+ is_leap: 是否闰月
341
+
342
+ Returns:
343
+ 对应的公历Date对象
344
+
345
+ Example:
346
+ >>> date = Date.from_lunar(2025, 3, 15) # 农历2025年三月十五
347
+ """
348
+ lunar_date = LunarDate(year, month, day, is_leap)
349
+ solar_date = lunar_date.to_solar()
350
+ return cls(solar_date)
351
+
352
+ @classmethod
353
+ def from_lunar_string(cls, lunar_string: str) -> 'Date':
354
+ """从农历字符串创建Date对象 (v1.0.8)
355
+
356
+ 支持格式:
357
+ - "20250315" (农历2025年3月15日)
358
+ - "2025闰0315" (农历2025年闰3月15日)
359
+
360
+ Args:
361
+ lunar_string: 农历日期字符串
362
+
363
+ Returns:
364
+ 对应的公历Date对象
365
+ """
366
+ # 解析闰月标记
367
+ is_leap = '闰' in lunar_string
368
+ clean_string = lunar_string.replace('闰', '')
369
+
370
+ if len(clean_string) != 8:
371
+ raise InvalidDateFormatError(f"农历日期字符串格式无效: {lunar_string}")
372
+
373
+ year = int(clean_string[:4])
374
+ month = int(clean_string[4:6])
375
+ day = int(clean_string[6:8])
376
+
377
+ return cls.from_lunar(year, month, day, is_leap)
378
+
379
+ @classmethod
380
+ def set_language(cls, language_code: str) -> None:
381
+ """设置全局语言 (v1.0.8)
382
+
383
+ 一次设置,全局生效。支持中简、中繁、日、英四种语言。
384
+
385
+ Args:
386
+ language_code: 语言代码
387
+ - 'zh_CN': 中文简体
388
+ - 'zh_TW': 中文繁体
389
+ - 'ja_JP': 日语
390
+ - 'en_US': 英语
391
+
392
+ Example:
393
+ >>> Date.set_language('en_US') # 设置为英语
394
+ >>> Date.set_language('zh_TW') # 设置为繁体中文
395
+ """
396
+ Language.set_global_language(language_code)
397
+ cls._logger.info(f"全局语言已设置为: {language_code}")
398
+
399
+ @classmethod
400
+ def get_language(cls) -> str:
401
+ """获取当前全局语言设置 (v1.0.8)"""
402
+ return Language.get_global_language()
403
+
404
+ @classmethod
405
+ def get_supported_languages(cls) -> Dict[str, str]:
406
+ """获取支持的语言列表 (v1.0.8)"""
407
+ return Language.get_supported_languages()
408
+
313
409
  @classmethod
314
410
  def date_range(cls, start: Union[str, 'Date'], end: Union[str, 'Date'],
315
411
  step: int = 1) -> List['Date']:
@@ -430,6 +526,36 @@ class Date:
430
526
  dt = dt - datetime.timedelta(hours=timezone_offset)
431
527
  return dt.timestamp()
432
528
 
529
+ def to_lunar(self) -> LunarDate:
530
+ """转为农历日期对象 (v1.0.8)
531
+
532
+ Returns:
533
+ 对应的农历日期对象
534
+
535
+ Example:
536
+ >>> date = Date('20250415')
537
+ >>> lunar = date.to_lunar()
538
+ >>> print(lunar.format_chinese()) # 农历2025年三月十八
539
+ """
540
+ return LunarDate.from_solar(self.to_date_object())
541
+
542
+ def to_lunar_string(self, compact: bool = True) -> str:
543
+ """转为农历字符串 (v1.0.8)
544
+
545
+ Args:
546
+ compact: 是否使用紧凑格式
547
+
548
+ Returns:
549
+ 农历日期字符串
550
+
551
+ Example:
552
+ >>> date = Date('20250415')
553
+ >>> print(date.to_lunar_string()) # 20250318
554
+ >>> print(date.to_lunar_string(False)) # 农历2025年三月十八
555
+ """
556
+ lunar = self.to_lunar()
557
+ return lunar.format_compact() if compact else lunar.format_chinese()
558
+
433
559
  def to_json(self, include_metadata: bool = True) -> str:
434
560
  """转为JSON字符串
435
561
 
@@ -628,6 +754,155 @@ class Date:
628
754
  else:
629
755
  return f"{abs(diff_days)} days ago"
630
756
 
757
+ def format_localized(self, format_type: str = 'full', language_code: Optional[str] = None) -> str:
758
+ """多语言本地化格式 (v1.0.8)
759
+
760
+ Args:
761
+ format_type: 格式类型 (full, short, year_month, month_day)
762
+ language_code: 语言代码,None时使用全局设置
763
+
764
+ Returns:
765
+ 本地化格式的日期字符串
766
+
767
+ Example:
768
+ >>> Date.set_language('en_US')
769
+ >>> date = Date('20250415')
770
+ >>> print(date.format_localized()) # 04/15/2025
771
+ >>> print(date.format_localized('short')) # 04/15/2025
772
+ """
773
+ return Language.format_date(self.year, self.month, self.day, format_type, language_code)
774
+
775
+ def format_weekday_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
776
+ """多语言星期几格式 (v1.0.8)
777
+
778
+ Args:
779
+ short: 是否使用短名称
780
+ language_code: 语言代码,None时使用全局设置
781
+
782
+ Returns:
783
+ 本地化的星期几名称
784
+
785
+ Example:
786
+ >>> Date.set_language('ja_JP')
787
+ >>> date = Date('20250415') # 星期二
788
+ >>> print(date.format_weekday_localized()) # 火曜日
789
+ >>> print(date.format_weekday_localized(short=True)) # 火
790
+ """
791
+ weekday_index = self.get_weekday()
792
+ return Language.get_weekday_name(weekday_index, short, language_code)
793
+
794
+ def format_month_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
795
+ """多语言月份格式 (v1.0.8)
796
+
797
+ Args:
798
+ short: 是否使用短名称
799
+ language_code: 语言代码,None时使用全局设置
800
+
801
+ Returns:
802
+ 本地化的月份名称
803
+ """
804
+ return Language.get_month_name(self.month, short, language_code)
805
+
806
+ def format_quarter_localized(self, short: bool = False, language_code: Optional[str] = None) -> str:
807
+ """多语言季度格式 (v1.0.8)
808
+
809
+ Args:
810
+ short: 是否使用短名称
811
+ language_code: 语言代码,None时使用全局设置
812
+
813
+ Returns:
814
+ 本地化的季度名称
815
+ """
816
+ quarter = self.get_quarter()
817
+ return Language.get_quarter_name(quarter, short, language_code)
818
+
819
+ def format_relative_localized(self, reference_date: Optional['Date'] = None,
820
+ language_code: Optional[str] = None) -> str:
821
+ """多语言相对时间格式 (v1.0.8)
822
+
823
+ Args:
824
+ reference_date: 参考日期,None时使用今天
825
+ language_code: 语言代码,None时使用全局设置
826
+
827
+ Returns:
828
+ 本地化的相对时间描述
829
+
830
+ Example:
831
+ >>> Date.set_language('en_US')
832
+ >>> today = Date.today()
833
+ >>> tomorrow = today.add_days(1)
834
+ >>> print(tomorrow.format_relative_localized()) # tomorrow
835
+ """
836
+ if reference_date is None:
837
+ reference_date = Date.today()
838
+
839
+ diff_days = reference_date.calculate_difference_days(self)
840
+
841
+ if diff_days == 0:
842
+ return Language.format_relative_time('today', language_code=language_code)
843
+ elif diff_days == 1:
844
+ return Language.format_relative_time('tomorrow', language_code=language_code)
845
+ elif diff_days == -1:
846
+ return Language.format_relative_time('yesterday', language_code=language_code)
847
+ elif diff_days > 0:
848
+ if diff_days <= 6:
849
+ return Language.format_relative_time('days_later', diff_days, language_code)
850
+ elif diff_days <= 28:
851
+ weeks = diff_days // 7
852
+ return Language.format_relative_time('weeks_later', weeks, language_code)
853
+ elif diff_days <= 365:
854
+ months = diff_days // 30
855
+ return Language.format_relative_time('months_later', months, language_code)
856
+ else:
857
+ years = diff_days // 365
858
+ return Language.format_relative_time('years_later', years, language_code)
859
+ else:
860
+ abs_days = abs(diff_days)
861
+ if abs_days <= 6:
862
+ return Language.format_relative_time('days_ago', abs_days, language_code)
863
+ elif abs_days <= 28:
864
+ weeks = abs_days // 7
865
+ return Language.format_relative_time('weeks_ago', weeks, language_code)
866
+ elif abs_days <= 365:
867
+ months = abs_days // 30
868
+ return Language.format_relative_time('months_ago', months, language_code)
869
+ else:
870
+ years = abs_days // 365
871
+ return Language.format_relative_time('years_ago', years, language_code)
872
+
873
+ def format_lunar(self, include_year: bool = True, include_zodiac: bool = False,
874
+ language_code: Optional[str] = None) -> str:
875
+ """农历格式化 (v1.0.8)
876
+
877
+ Args:
878
+ include_year: 是否包含年份
879
+ include_zodiac: 是否包含生肖
880
+ language_code: 语言代码,None时使用全局设置
881
+
882
+ Returns:
883
+ 农历日期字符串
884
+
885
+ Example:
886
+ >>> date = Date('20250415')
887
+ >>> print(date.format_lunar()) # 农历2025年三月十八
888
+ >>> print(date.format_lunar(include_zodiac=True)) # 乙巳(蛇)年三月十八
889
+ """
890
+ lunar = self.to_lunar()
891
+ return lunar.format_chinese(include_year, include_zodiac)
892
+
893
+ def format_lunar_compact(self) -> str:
894
+ """农历紧凑格式 (v1.0.8)
895
+
896
+ Returns:
897
+ 农历紧凑格式字符串
898
+
899
+ Example:
900
+ >>> date = Date('20250415')
901
+ >>> print(date.format_lunar_compact()) # 20250318
902
+ """
903
+ lunar = self.to_lunar()
904
+ return lunar.format_compact()
905
+
631
906
  # =============================================
632
907
  # get_* 系列:获取方法
633
908
  # =============================================
@@ -755,6 +1030,42 @@ class Date:
755
1030
  return (self.month in quarter_end_months and
756
1031
  self.day == quarter_end_months[self.month])
757
1032
 
1033
+ def is_lunar_new_year(self) -> bool:
1034
+ """是否为农历新年 (v1.0.8)
1035
+
1036
+ Returns:
1037
+ 是否为农历正月初一
1038
+ """
1039
+ lunar = self.to_lunar()
1040
+ return lunar.month == 1 and lunar.day == 1 and not lunar.is_leap
1041
+
1042
+ def is_lunar_month_start(self) -> bool:
1043
+ """是否为农历月初 (v1.0.8)
1044
+
1045
+ Returns:
1046
+ 是否为农历月初一
1047
+ """
1048
+ lunar = self.to_lunar()
1049
+ return lunar.day == 1
1050
+
1051
+ def is_lunar_month_mid(self) -> bool:
1052
+ """是否为农历月中 (v1.0.8)
1053
+
1054
+ Returns:
1055
+ 是否为农历十五
1056
+ """
1057
+ lunar = self.to_lunar()
1058
+ return lunar.day == 15
1059
+
1060
+ def is_lunar_leap_month(self) -> bool:
1061
+ """是否在农历闰月 (v1.0.8)
1062
+
1063
+ Returns:
1064
+ 是否为农历闰月
1065
+ """
1066
+ lunar = self.to_lunar()
1067
+ return lunar.is_leap
1068
+
758
1069
  def is_holiday(self, country: str = 'CN') -> bool:
759
1070
  """是否为节假日(增强版实现)
760
1071
 
@@ -1088,6 +1399,58 @@ class Date:
1088
1399
  def __hash__(self) -> int:
1089
1400
  return hash(self.to_tuple())
1090
1401
 
1402
+ def compare_lunar(self, other: 'Date') -> int:
1403
+ """农历日期比较 (v1.0.8)
1404
+
1405
+ Args:
1406
+ other: 另一个Date对象
1407
+
1408
+ Returns:
1409
+ -1: self < other, 0: self == other, 1: self > other
1410
+
1411
+ Example:
1412
+ >>> date1 = Date.from_lunar(2025, 1, 1) # 农历正月初一
1413
+ >>> date2 = Date.from_lunar(2025, 1, 15) # 农历正月十五
1414
+ >>> print(date1.compare_lunar(date2)) # -1
1415
+ """
1416
+ lunar_self = self.to_lunar()
1417
+ lunar_other = other.to_lunar()
1418
+
1419
+ if lunar_self < lunar_other:
1420
+ return -1
1421
+ elif lunar_self > lunar_other:
1422
+ return 1
1423
+ else:
1424
+ return 0
1425
+
1426
+ def is_same_lunar_month(self, other: 'Date') -> bool:
1427
+ """是否同一农历月份 (v1.0.8)
1428
+
1429
+ Args:
1430
+ other: 另一个Date对象
1431
+
1432
+ Returns:
1433
+ 是否为同一农历月份
1434
+ """
1435
+ lunar_self = self.to_lunar()
1436
+ lunar_other = other.to_lunar()
1437
+ return (lunar_self.year == lunar_other.year and
1438
+ lunar_self.month == lunar_other.month and
1439
+ lunar_self.is_leap == lunar_other.is_leap)
1440
+
1441
+ def is_same_lunar_day(self, other: 'Date') -> bool:
1442
+ """是否同一农历日期 (v1.0.8)
1443
+
1444
+ Args:
1445
+ other: 另一个Date对象
1446
+
1447
+ Returns:
1448
+ 是否为同一农历日期
1449
+ """
1450
+ lunar_self = self.to_lunar()
1451
+ lunar_other = other.to_lunar()
1452
+ return lunar_self == lunar_other
1453
+
1091
1454
  # =============================================
1092
1455
  # 向后兼容的旧API
1093
1456
  # =============================================