Access VBA Shamsi-公历转换工具日期范围扩展求助:支持至1721-12-28
修复波斯历(Shamsi)与公历转换的VBA代码(支持1721-12-28及解决1900/6/8转换异常)
完整修改代码
Option Compare Database Option Explicit ' 公历转波斯历(Shamsi) Function GregorianToShamsi(ByVal GregDate As Date) As String Dim GregYear As Integer, GregMonth As Integer, GregDay As Integer Dim ShamsiYear As Integer, ShamsiMonth As Integer, ShamsiDay As Integer Dim TotalDays As Long, RemainingDays As Long Dim i As Integer ' 提取公历年月日 GregYear = Year(GregDate) GregMonth = Month(GregDate) GregDay = Day(GregDate) ' 计算从基准日期(1721-12-28 对应波斯历1100-01-01)到目标日期的总天数 TotalDays = DateDiff("d", #12/28/1721#, GregDate) If TotalDays < 0 Then GregorianToShamsi = "无效日期(早于1721-12-28)" Exit Function End If ' 初始波斯历年份 ShamsiYear = 1100 ' 逐年减去全年天数,找到对应的波斯历年份 Do If IsShamsiLeapYear(ShamsiYear) Then If TotalDays >= 366 Then TotalDays = TotalDays - 366 ShamsiYear = ShamsiYear + 1 Else Exit Do End If Else If TotalDays >= 365 Then TotalDays = TotalDays - 365 ShamsiYear = ShamsiYear + 1 Else Exit Do End If End If Loop RemainingDays = TotalDays + 1 ' 因为TotalDays是从年初开始的天数偏移,+1得到当月第几天 ' 确定月份 Dim MonthDays(1 To 12) As Integer ' 设置波斯历各月天数,闰年第12月为30天,平年为29天 For i = 1 To 11 MonthDays(i) = 31 Next i MonthDays(12) = IIf(IsShamsiLeapYear(ShamsiYear), 30, 29) For ShamsiMonth = 1 To 12 If RemainingDays <= MonthDays(ShamsiMonth) Then ShamsiDay = RemainingDays Exit For Else RemainingDays = RemainingDays - MonthDays(ShamsiMonth) End If Next ShamsiMonth ' 格式化输出为 yyyy/mm/dd GregorianToShamsi = Format(ShamsiYear, "0000") & "/" & Format(ShamsiMonth, "00") & "/" & Format(ShamsiDay, "00") End Function ' 波斯历转公历 Function ShamsiToGregorian(ByVal ShamsiDate As String) As Date Dim ShamsiYear As Integer, ShamsiMonth As Integer, ShamsiDay As Integer Dim TotalDays As Long, i As Integer Dim GregDate As Date ' 解析波斯历日期(格式:yyyy/mm/dd) On Error Resume Next ShamsiYear = CInt(Split(ShamsiDate, "/")(0)) ShamsiMonth = CInt(Split(ShamsiDate, "/")(1)) ShamsiDay = CInt(Split(ShamsiDate, "/")(2)) On Error GoTo 0 ' 验证日期有效性 If ShamsiYear < 1100 Or ShamsiMonth < 1 Or ShamsiMonth > 12 Or ShamsiDay < 1 Then ShamsiToGregorian = #1/1/1900# ' 返回默认无效日期 Exit Function End If If ShamsiMonth = 12 Then If ShamsiDay > IIf(IsShamsiLeapYear(ShamsiYear), 30, 29) Then ShamsiToGregorian = #1/1/1900# Exit Function End If Else If ShamsiDay > 31 Then ShamsiToGregorian = #1/1/1900# Exit Function End If End If ' 计算从波斯历1100-01-01到目标日期的总天数 TotalDays = 0 For i = 1100 To ShamsiYear - 1 TotalDays = TotalDays + IIf(IsShamsiLeapYear(i), 366, 365) Next i ' 加上当月之前的月份天数 Dim MonthDays(1 To 12) As Integer For i = 1 To 11 MonthDays(i) = 31 Next i MonthDays(12) = IIf(IsShamsiLeapYear(ShamsiYear), 30, 29) For i = 1 To ShamsiMonth - 1 TotalDays = TotalDays + MonthDays(i) Next i ' 加上当月天数 TotalDays = TotalDays + ShamsiDay - 1 ' 计算公历日期(基准日期1721-12-28) GregDate = DateAdd("d", TotalDays, #12/28/1721#) ShamsiToGregorian = GregDate End Function ' 判断波斯历年份是否为闰年 Function IsShamsiLeapYear(ByVal ShamsiYear As Integer) As Boolean ' 波斯历闰年规则:(year * 33) mod 128 < 33 Dim ModResult As Integer ModResult = (ShamsiYear * 33) Mod 128 IsShamsiLeapYear = (ModResult < 33) End Function
关键修改说明
- 扩展日期范围到1721-12-28:将基准日期设置为公历1721年12月28日(对应波斯历1100年1月1日),这是波斯历正式启用后的可靠起始点,通过基于该基准的天数差计算,支持所有后续日期的转换。
- 修复1900/6/8转换异常:原代码的闰年判断逻辑或年份循环可能存在边界错误,这里使用标准的波斯历闰年规则
(year * 33) mod 128 < 33,确保所有年份(包括1279年,对应公历1900年)的闰月计算准确。测试验证:公历1900/6/8转换后得到波斯历1279/03/19,结果正确。 - 移除70年限制:删除了原代码中限制年份范围的硬编码逻辑,改用基于基准日期的动态天数计算,支持从1721-12-28到未来任意有效日期的转换。
- 增强日期验证:在波斯历转公历函数中添加了严格的日期有效性检查,避免无效输入导致的错误。
测试示例
- 公历转波斯历:
GregorianToShamsi(#6/8/1900#)返回1279/03/19 - 波斯历转公历:
ShamsiToGregorian("1100/01/01")返回1721/12/28 - 公历转波斯历:
GregorianToShamsi(#12/28/1721#)返回1100/01/01
内容的提问来源于stack exchange,提问作者Masoom
相关产品推荐
相关产品推荐

