BaiduFMX
885 строк · 36.1 Кб
1unit qcndate;
2
3interface
4
5{
6The algorithm data of this unit uses the lunar calendar data from the early UCDOS.
7}
8
9uses
10System.Classes,
11System.SysUtils,
12System.Math,
13System.DateUtils;
14
15type
16{$HPPEMIT '#pragma link "qcndate"'}
17TCalendarDate = record
18case Integer of
191:
20// 16-значный год, 8-значный месяц, 8-значный день
21(Year: Word; Month: Shortint; Day: Byte); // 16位年,8位月,8位日
222: // 32-битное целое значение
23(Value: Integer); // 32位的整数值
24end;
25
26const
27CnDayNames: array [1 .. 30] of String = ('初一', '初二', '初三', '初四', '初五', '初六',
28'初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八',
29'十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十');
30RuDayNames: array [1 .. 30] of String = ('Первый', 'Второй', 'Третий', 'Четвёртый', 'Пятый', 'Шестой',
31'Седьмой', 'Восьмой', 'Девятый', 'Десятый', 'Одиннадцатый', 'Двенадцатый', 'Тринадцатый',
32'Четырнадцатый', 'Пятнадцатый', 'Шестнадцатый', 'Семьнадцатый', 'Восемьнадцатый', 'Девятнадцатый',
33'Двадцатый', 'Двадцатьпервый', 'Двадцатьвторой', 'Двадцатьтретий', 'Двадцатьчетвёртый', 'Двадцатьпятый',
34'Двадцатьшестой', 'Двадцатьседьмой', 'Двадцатьвосьмой', 'Двадцатьдевятый', 'Тридцатый');
35CnMonthNames: array [1 .. 12] of String = ('正月', '二月', '三月', '四月', '五月', '六月',
36'七月', '八月', '九月', '十月', '冬月', '腊月');
37CnSkyNames: array [1 .. 10] of String = ('甲', '乙', '丙', '丁', '戊', '己', '庚',
38'辛', '壬', '癸');
39CnEarthNames: array [1 .. 12] of String = ('子', '丑', '寅', '卯', '辰', '巳', '午',
40'未', '申', '酉', '戌', '亥');
41CnAnimals: array [1 .. 12] of String = ('鼠', '牛', '虎', '兔', '龙', '蛇', '马',
42'羊', '猴', '鸡', '狗', '猪');
43CnSolarTerms: array [0 .. 24] of String = ('', '小寒', '大寒', '立春', '雨水', '惊蛰',
44'春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露',
45'秋分', '寒露', '霜降', '立冬', '小雪', '大雪', '冬至');
46CnWeekNames: array [1 .. 7] of String = ('一', '二', '三', '四', '五', '六', '日');
47
48/// <summary>
49/// 将指定的农历年月日编码为一个TCalendarDate变量
50/// </summary>
51/// <params>
52/// <param name="Y">农历年份,取值范围为1901-2050 </param>
53/// <param name="M">农历月份,负数代表是闰月</param>
54/// <param name="D">农历日数,取值范围为1-30</param>
55/// </params>
56/// <returns>返回编码后日期</returns>
57function CnDate(Y: Word; M, D: Shortint): TCalendarDate; inline;
58
59/// <summary>将指定的日期转换为农历日期</summary>
60/// <params>
61/// <param name=“ADate">阳历日期</param>
62/// </params>
63/// <returns>返回转换后的日期</returns>
64function ToCnDate(ADate: TDateTime): TCalendarDate; overload;
65
66/// <summary>将指定的日期转换为农历日期</summary>
67/// <params>
68/// <param name=“yyyy">阳历年份</param>
69/// <param name="mm">阳历月份</param>
70/// <param name="dd">阳历日</param>
71/// </params>
72/// <returns>返回转换后的日期</returns>
73function ToCnDate(yyyy, mm, dd: Word): TCalendarDate; overload;
74
75/// <summary>Convert the specified lunar calendar date to the solar calendar date</summary>
76/// <params>
77/// <param name="ADate">Lunar calendar date</param>
78/// </params>
79/// <returns>Return the converted solar calendar date</returns>
80function ToEnDate(ADate: TCalendarDate): TDateTime;
81
82/// <summary>获取指定阳历日期的农历年份</summary>
83/// <params>
84/// <param name="ADate">阳历日期</param>
85/// </params>
86/// <returns>返回指定阳历日期的农历年份</returns>
87function CnYearOf(ADate: TDateTime): Smallint; overload;
88
89/// <summary>获取指定农历日期的年份的天干地支名称</summary>
90/// <params>
91/// <param name="ADate">阴历日期</param>
92/// </params>
93/// <returns>返回天干地支名称,如"甲午"</reutrns>
94function CnYearName(ADate: TCalendarDate): String; overload;
95
96/// <summary>获取指定阳历日期对应的农历年份的天干地支名称</summary>
97/// <params>
98/// <param name="ADate">阴历日期</param>
99/// </params>
100/// <returns>返回天干地支名称,如"甲午"</reutrns>
101function CnYearName(ADate: TDateTime): String; overload;
102/// <summary>获取指定阳历日期对应的月份数</summary>
103/// <params>
104/// <param name="ADate">阳历日期</param>
105/// </params>
106/// <returns>返回月份数,取值范围为1-12,-12~-1,负数代表是闰月</returns>
107function CnMonthOf(ADate: TDateTime): Shortint; overload;
108/// <summary>获取指定农历日期对应的月份名称</summary>
109/// <params>
110/// <param name="ADate">农历日期</param>
111/// </params>
112/// <returns>返回农历月份名称,如“腊月”</param>
113function CnMonthName(ADate: TCalendarDate): String; overload;
114/// <summary>获取指定阳历日期对应农历日期对应的月份名称</summary>
115/// <params>
116/// <param name="ADate">农历日期</param>
117/// </params>
118/// <returns>返回农历月份名称,如“腊月”</param>
119function CnMonthName(ADate: TDateTime): String; overload;
120/// <summary>获取指定农历年月的天数</summary>
121/// <params>
122/// <param name="Y">农历年份</param>
123/// <param name="M">农历月份,负数代表闰月</param>
124/// </params>
125/// <returns>返回指定农历年月的天数</returns>
126function CnMonthDays(Y: Word; M: Shortint): Byte;
127/// <summary>获取指定阳历日期的农历日</summary>
128/// <params>
129/// <param name="ADate">阳历日期</param>
130/// </params>
131/// <returns>返回指定的农历日</returns>
132function CnDayOf(ADate: TDateTime): Shortint; overload;
133/// <summary>获取指定农历日期的中文名称</summary>
134/// <params>
135/// <param name="ADate">农历日期</param>
136/// </params>
137/// <returns>返回指定日期的日的中文名称</returns>
138function CnDayName(ADate: TCalendarDate): String; overload;
139//function RuDayName(ADate: TCalendarDate): String; overload;
140
141/// <summary>获取指定阳历日期对应农历日期日的中文名称</summary>
142/// <params>
143/// <param name="ADate">阳历日期</param>
144/// </params>
145/// <returns>返回指定日期的日的中文名称</returns>
146function CnDayName(ADate: TDateTime): String; overload;
147/// <summary>获取指定小时的时刻名称</summary>
148/// <params>
149/// <param name="AHour">小时数,取值范围0-23</param>
150/// </params>
151/// <returns>返回中文的小时对应的时刻名称(子、丑等)</returns>
152function CnHourName(AHour: Byte): String; overload;
153/// <summary>获取指定的农历日期的属相名称</summary>
154/// <params>
155/// <param name="ADate">农历日期</param>
156/// </params>
157/// <returns>返回指定的农历年份对应的属相名称</returns>
158function CnAnimalOf(ADate: TCalendarDate): String; overload;
159/// <summary>获取指定的阳历日期的属相名称</summary>
160/// <params>
161/// <param name="ADate">阳历日期</param>
162/// </params>
163/// <returns>返回指定的阳历年份对应的属相名称</returns>
164function CnAnimalOf(ADate: TDateTime): String; overload;
165/// <summary>获取指定年份相应节气对应的阳历日期</summary>
166/// <params>
167/// <param name="AYear">农历年份</param>
168/// <param name="AName">节气名称</param>
169/// </params>
170/// <returns>返回对应的阳历日期</returns>
171function DateOfCnSolarTerm(AYear: Smallint; AName: String): TDateTime; overload;
172/// <summary>获取指定阳历日期对应的节气序号</summary>
173/// <params>
174/// <param name="AYear">阳历年份</param>
175/// <param name="AMonth">阳历月份</param>
176/// <param name="ADay">阳历日</param>
177/// </params>
178/// <returns>返回节气序列,如果不是任何节气,返回0</returns>
179function CnSolarTerm(AYear, AMonth, ADay: Word): Shortint; overload;
180/// <summary>获取指定农历日期对应的节气序号</summary>
181/// <params>
182/// <param name="AYear">农历年份</param>
183/// <param name="AMonth">农历月份</param>
184/// <param name="ADay">农历日</param>
185/// </params>
186/// <returns>返回节气序列,如果不是任何节气,返回0</returns>
187function CnSolarTerm(ADate: TCalendarDate): Shortint; overload;
188/// <summary>获取指定阳历日期对应的节气序号</summary>
189/// <params>
190/// <param name="ADate">阳历日期</param>
191/// </params>
192/// <returns>返回节气序列,如果不是任何节气,返回0</returns>
193function CnSolarTerm(ADate: TDateTime): Shortint; overload;
194/// <summary>获取指定农历日期对应的节气名称</summary>
195/// <params>
196/// <param name="ADate">农历日期</param>
197/// </params>
198/// <returns>返回节气名称,如果不属于任何节气,返回空字符串</returns>
199function CnSolarTermName(ADate: TCalendarDate): String; overload;
200/// <summary>获取指定阳历日期对应的节气名称</summary>
201/// <params>
202/// <param name="ADate">阳历日期</param>
203/// </params>
204/// <returns>返回节气名称,如果不属于任何节气,返回空字符串</returns>
205function CnSolarTermName(ADate: TDateTime): String; overload;
206/// <summary>获取指定日期在相应的农历年份中的周次</summary>
207/// <params>
208/// <param name="ADate">阳历日期</param>
209/// </params>
210/// <returns>返回周次</returns>
211function CnWeeksInYear(const ADate: TDateTime): Byte;
212/// <summary>增加指定的农历的年数</summary>
213/// <params>
214/// <param name="ADate">当前日期</param>
215/// <param name="ADelta">增量</param>
216/// </params>
217/// <returns>返回增加或减少(负增量)的年数后的农历日期</returns>
218function CnIncYear(ADate: TCalendarDate; ADelta: Integer = 1): TCalendarDate;
219/// <summary>增加指定的农历的月数</summary>
220/// <params>
221/// <param name="ADate">当前日期</param>
222/// <param name="ADelta">增量</param>
223/// </params>
224/// <returns>返回增加或减少(负增量)的月数后的农历日期</returns>
225function CnIncMonth(ADate: TCalendarDate; ADelta: Integer = 1): TCalendarDate;
226/// <summary>增加指定的农历的天数</summary>
227/// <params>
228/// <param name="ADate">当前日期</param>
229/// <param name="ADelta">增量</param>
230/// </params>
231/// <returns>返回增加或减少(负增量)的月数后的农历日期</returns>
232function CnIncDay(ADate: TCalendarDate; ADelta: Integer = 1): TCalendarDate;
233
234implementation //--------------------------------------------------------------
235
236const
237// 农历月份数据,每年4字节,从1901年开始,共150年
238// 数据来源:UCDOS 6.0 UCT.COM
239// 分析整理:Copyright (c) 1996-1998, Randolph
240// 数据解析:
241// 如果第一字节的bit7为1,则该年1月1日位于农历12月,否则位于11月
242// 第一字节去除bit7为该年1月1日的农历日期
243// 第二字节 第三字节
244// bit: 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
245// 农历月份:16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
246// 农历月份指的是从该年1月1日的农历月份算起的顺序号
247// 农历月份对应的bit为1则该月为30日,否则为29日
248// 第四字节为闰月月份
249// BaseDate='2000/02/04';//2000立春
250BaseAnimalDate = 1972; // 1972年支为子(是鼠年)
251BaseSkyStemDate = 1974; // 1974年干为甲
252START_YEAR = 1901;
253END_YEAR = 2050;
254
255gLunarHolDay: array [0 .. 1799] of Byte = ($96, $B4, $96, $A6, $97, $97, $78,
256$79, $79, $69, $78, $77, // 1901
257$96, $A4, $96, $96, $97, $87, $79, $79, $79, $69, $78, $78, // 1902
258$96, $A5, $87, $96, $87, $87, $79, $69, $69, $69, $78, $78, // 1903
259$86, $A5, $96, $A5, $96, $97, $88, $78, $78, $79, $78, $87, // 1904
260$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1905
261$96, $A4, $96, $96, $97, $97, $79, $79, $79, $69, $78, $78, // 1906
262$96, $A5, $87, $96, $87, $87, $79, $69, $69, $69, $78, $78, // 1907
263$86, $A5, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1908
264$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1909
265$96, $A4, $96, $96, $97, $97, $79, $79, $79, $69, $78, $78, // 1910
266$96, $A5, $87, $96, $87, $87, $79, $69, $69, $69, $78, $78, // 1911
267$86, $A5, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1912
268$95, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1913
269$96, $B4, $96, $A6, $97, $97, $79, $79, $79, $69, $78, $78, // 1914
270$96, $A5, $97, $96, $97, $87, $79, $79, $69, $69, $78, $78, // 1915
271$96, $A5, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1916
272$95, $B4, $96, $A6, $96, $97, $78, $79, $78, $69, $78, $87, // 1917
273$96, $B4, $96, $A6, $97, $97, $79, $79, $79, $69, $78, $77, // 1918
274$96, $A5, $97, $96, $97, $87, $79, $79, $69, $69, $78, $78, // 1919
275$96, $A5, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1920
276$95, $B4, $96, $A5, $96, $97, $78, $79, $78, $69, $78, $87, // 1921
277$96, $B4, $96, $A6, $97, $97, $79, $79, $79, $69, $78, $77, // 1922
278$96, $A4, $96, $96, $97, $87, $79, $79, $69, $69, $78, $78, // 1923
279$96, $A5, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1924
280$95, $B4, $96, $A5, $96, $97, $78, $79, $78, $69, $78, $87, // 1925
281$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1926
282$96, $A4, $96, $96, $97, $87, $79, $79, $79, $69, $78, $78, // 1927
283$96, $A5, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1928
284$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1929
285$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1930
286$96, $A4, $96, $96, $97, $87, $79, $79, $79, $69, $78, $78, // 1931
287$96, $A5, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1932
288$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1933
289$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1934
290$96, $A4, $96, $96, $97, $97, $79, $79, $79, $69, $78, $78, // 1935
291$96, $A5, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1936
292$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1937
293$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1938
294$96, $A4, $96, $96, $97, $97, $79, $79, $79, $69, $78, $78, // 1939
295$96, $A5, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1940
296$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1941
297$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1942
298$96, $A4, $96, $96, $97, $97, $79, $79, $79, $69, $78, $78, // 1943
299$96, $A5, $96, $A5, $A6, $96, $88, $78, $78, $78, $87, $87, // 1944
300$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1945
301$95, $B4, $96, $A6, $97, $97, $78, $79, $78, $69, $78, $77, // 1946
302$96, $B4, $96, $A6, $97, $97, $79, $79, $79, $69, $78, $78, // 1947
303$96, $A5, $A6, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 1948
304$A5, $B4, $96, $A5, $96, $97, $88, $79, $78, $79, $77, $87, // 1949
305$95, $B4, $96, $A5, $96, $97, $78, $79, $78, $69, $78, $77, // 1950
306$96, $B4, $96, $A6, $97, $97, $79, $79, $79, $69, $78, $78, // 1951
307$96, $A5, $A6, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 1952
308$A5, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1953
309$95, $B4, $96, $A5, $96, $97, $78, $79, $78, $68, $78, $87, // 1954
310$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1955
311$96, $A5, $A5, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 1956
312$A5, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1957
313$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1958
314$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1959
315$96, $A4, $A5, $A5, $A6, $96, $88, $88, $88, $78, $87, $87, // 1960
316$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1961
317$96, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1962
318$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1963
319$96, $A4, $A5, $A5, $A6, $96, $88, $88, $88, $78, $87, $87, // 1964
320$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1965
321$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1966
322$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1967
323$96, $A4, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 1968
324$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1969
325$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1970
326$96, $B4, $96, $A6, $97, $97, $78, $79, $79, $69, $78, $77, // 1971
327$96, $A4, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 1972
328$A5, $B5, $96, $A5, $A6, $96, $88, $78, $78, $78, $87, $87, // 1973
329$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1974
330$96, $B4, $96, $A6, $97, $97, $78, $79, $78, $69, $78, $77, // 1975
331$96, $A4, $A5, $B5, $A6, $A6, $88, $89, $88, $78, $87, $87, // 1976
332$A5, $B4, $96, $A5, $96, $96, $88, $88, $78, $78, $87, $87, // 1977
333$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $78, $87, // 1978
334$96, $B4, $96, $A6, $96, $97, $78, $79, $78, $69, $78, $77, // 1979
335$96, $A4, $A5, $B5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 1980
336$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $77, $87, // 1981
337$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1982
338$95, $B4, $96, $A5, $96, $97, $78, $79, $78, $69, $78, $77, // 1983
339$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $87, // 1984
340$A5, $B4, $A6, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 1985
341$A5, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 1986
342$95, $B4, $96, $A5, $96, $97, $88, $79, $78, $69, $78, $87, // 1987
343$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 1988
344$A5, $B4, $A5, $A5, $A6, $96, $88, $88, $88, $78, $87, $87, // 1989
345$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $79, $77, $87, // 1990
346$95, $B4, $96, $A5, $86, $97, $88, $78, $78, $69, $78, $87, // 1991
347$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 1992
348$A5, $B3, $A5, $A5, $A6, $96, $88, $88, $88, $78, $87, $87, // 1993
349$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1994
350$95, $B4, $96, $A5, $96, $97, $88, $76, $78, $69, $78, $87, // 1995
351$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 1996
352$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 1997
353$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 1998
354$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 1999
355$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2000
356$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2001
357$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 2002
358$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 2003
359$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2004
360$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2005
361$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2006
362$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $69, $78, $87, // 2007
363$96, $B4, $A5, $B5, $A6, $A6, $87, $88, $87, $78, $87, $86, // 2008
364$A5, $B3, $A5, $B5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2009
365$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2010
366$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $78, $87, // 2011
367$96, $B4, $A5, $B5, $A5, $A6, $87, $88, $87, $78, $87, $86, // 2012
368$A5, $B3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $87, // 2013
369$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2014
370$95, $B4, $96, $A5, $96, $97, $88, $78, $78, $79, $77, $87, // 2015
371$95, $B4, $A5, $B4, $A5, $A6, $87, $88, $87, $78, $87, $86, // 2016
372$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $87, // 2017
373$A5, $B4, $A6, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2018
374$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $79, $77, $87, // 2019
375$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $86, // 2020
376$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2021
377$A5, $B4, $A5, $A5, $A6, $96, $88, $88, $88, $78, $87, $87, // 2022
378$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $79, $77, $87, // 2023
379$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $96, // 2024
380$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2025
381$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2026
382$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 2027
383$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $96, // 2028
384$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2029
385$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2030
386$A5, $B4, $96, $A5, $96, $96, $88, $78, $78, $78, $87, $87, // 2031
387$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $96, // 2032
388$A5, $C3, $A5, $B5, $A6, $A6, $88, $88, $88, $78, $87, $86, // 2033
389$A5, $B3, $A5, $A5, $A6, $A6, $88, $78, $88, $78, $87, $87, // 2034
390$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2035
391$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $96, // 2036
392$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $86, // 2037
393$A5, $B3, $A5, $A5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2038
394$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2039
395$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $78, $87, $96, // 2040
396$A5, $C3, $A5, $B5, $A5, $A6, $87, $88, $87, $78, $87, $86, // 2041
397$A5, $B3, $A5, $B5, $A6, $A6, $88, $88, $88, $78, $87, $87, // 2042
398$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2043
399$95, $B4, $A5, $B4, $A5, $A6, $97, $87, $87, $88, $87, $96, // 2044
400$A5, $C3, $A5, $B4, $A5, $A6, $87, $88, $87, $78, $87, $86, // 2045
401$A5, $B3, $A5, $B5, $A6, $A6, $87, $88, $88, $78, $87, $87, // 2046
402$A5, $B4, $96, $A5, $A6, $96, $88, $88, $78, $78, $87, $87, // 2047
403$95, $B4, $A5, $B4, $A5, $A5, $97, $87, $87, $88, $86, $96, // 2048
404$A4, $C3, $A5, $A5, $A5, $A6, $97, $87, $87, $78, $87, $86, // 2049
405$A5, $C3, $A5, $B5, $A6, $A6, $87, $88, $78, $78, $87, $87); // 2050
406
407CnData: array [0 .. 599] of Byte = ($0B, $52, $BA, $00, $16, $A9, $5D, $00,
408$83, $A9, $37, $05, $0E, $74, $9B, $00, $1A, $B6, $55, $00, $87, $B5, $55,
409$04, $11, $55, $AA, $00, $1C, $A6, $B5, $00, $8A, $A5, $75, $02, $14, $52,
410$BA, $00, $81, $52, $6E, $06, $0D, $E9, $37, $00, $18, $74, $97, $00, $86,
411$EA, $96, $05, $10, $6D, $55, $00, $1A, $35, $AA, $00, $88, $4B, $6A, $02,
412$13, $A5, $6D, $00, $1E, $D2, $6E, $07, $0B, $D2, $5E, $00, $17, $E9, $2E,
413$00, $84, $D9, $2D, $05, $0F, $DA, $95, $00, $19, $5B, $52, $00, $87, $56,
414$D4, $04, $11, $4A, $DA, $00, $1C, $A5, $5D, $00, $89, $A4, $BD, $02, $15,
415$D2, $5D, $00, $82, $B2, $5B, $06, $0D, $B5, $2B, $00, $18, $BA, $95, $00,
416$86, $B6, $A5, $05, $10, $56, $B4, $00, $1A, $4A, $DA, $00, $87, $49, $BA,
417$03, $13, $A4, $BB, $00, $1E, $B2, $5B, $07, $0B, $72, $57, $00, $16, $75,
418$2B, $00, $84, $6D, $2A, $06, $0F, $AD, $55, $00, $19, $55, $AA, $00, $86,
419$55, $6C, $04, $12, $C9, $76, $00, $1C, $64, $B7, $00, $8A, $E4, $AE, $02,
420$15, $EA, $56, $00, $83, $DA, $55, $07, $0D, $5B, $2A, $00, $18, $AD, $55,
421$00, $85, $AA, $D5, $05, $10, $53, $6A, $00, $1B, $A9, $6D, $00, $88, $A9,
422$5D, $03, $13, $D4, $AE, $00, $81, $D4, $AB, $08, $0C, $BA, $55, $00, $16,
423$5A, $AA, $00, $83, $56, $AA, $06, $0F, $AA, $D5, $00, $19, $52, $DA, $00,
424$86, $52, $BA, $04, $11, $A9, $5D, $00, $1D, $D4, $9B, $00, $8A, $74, $9B,
425$03, $15, $B6, $55, $00, $82, $AD, $55, $07, $0D, $55, $AA, $00, $18, $A5,
426$B5, $00, $85, $A5, $75, $05, $0F, $52, $B6, $00, $1B, $69, $37, $00, $89,
427$E9, $37, $04, $13, $74, $97, $00, $81, $EA, $96, $08, $0C, $6D, $52, $00,
428$16, $2D, $AA, $00, $83, $4B, $6A, $06, $0E, $A5, $6D, $00, $1A, $D2, $6E,
429$00, $87, $D2, $5E, $04, $12, $E9, $2E, $00, $1D, $EC, $96, $0A, $0B, $DA,
430$95, $00, $15, $5B, $52, $00, $82, $56, $D2, $06, $0C, $2A, $DA, $00, $18,
431$A4, $DD, $00, $85, $A4, $BD, $05, $10, $D2, $5D, $00, $1B, $D9, $2D, $00,
432$89, $B5, $2B, $03, $14, $BA, $95, $00, $81, $B5, $95, $08, $0B, $56, $B2,
433$00, $16, $2A, $DA, $00, $83, $49, $B6, $05, $0E, $64, $BB, $00, $19, $B2,
434$5B, $00, $87, $6A, $57, $04, $12, $75, $2B, $00, $1D, $B6, $95, $00, $8A,
435$AD, $55, $02, $15, $55, $AA, $00, $82, $55, $6C, $07, $0D, $C9, $76, $00,
436$17, $64, $B7, $00, $86, $E4, $AE, $05, $11, $EA, $56, $00, $1B, $6D, $2A,
437$00, $88, $5A, $AA, $04, $14, $AD, $55, $00, $81, $AA, $D5, $09, $0B, $52,
438$EA, $00, $16, $A9, $6D, $00, $84, $A9, $5D, $06, $0F, $D4, $AE, $00, $1A,
439$EA, $4D, $00, $87, $BA, $55, $04, $12, $5A, $AA, $00, $1D, $AB, $55, $00,
440$8A, $A6, $D5, $02, $14, $52, $DA, $00, $82, $52, $BA, $06, $0D, $A9, $3B,
441$00, $18, $B4, $9B, $00, $85, $74, $9B, $05, $11, $B5, $4D, $00, $1C, $D6,
442$A9, $00, $88, $35, $AA, $03, $13, $A5, $B5, $00, $81, $A5, $75, $0B, $0B,
443$52, $B6, $00, $16, $69, $37, $00, $84, $E9, $2F, $06, $10, $F4, $97, $00,
444$1A, $75, $4B, $00, $87, $6D, $52, $05, $11, $2D, $69, $00, $1D, $95, $B5,
445$00, $8A, $A5, $6D, $02, $15, $D2, $6E, $00, $82, $D2, $5E, $07, $0E, $E9,
446$2E, $00, $19, $EA, $96, $00, $86, $DA, $95, $05, $10, $5B, $4A, $00, $1C,
447$AB, $69, $00, $88, $2A, $D8, $03);
448
449function CnDate(Y: Word; M, D: Shortint): TCalendarDate;
450begin
451if (Y < 1901) or (Y > 2050) then
452raise EConvertError.CreateFmt('农历年份范围无效,只支持 1901-2050 年间的农历范围。', [Y]);
453if (M > 12) or (M = 0) or (M < -12) then
454raise EConvertError.CreateFmt('农历月份范围无效,只支持 -12~-1,1-12 之间的有效值。', [M]);
455if (D < 1) or (D > 30) then
456raise EConvertError.CreateFmt('农历日值范围无效,只支持 1-30 之间的有效值。', [M]);
457Result.Year := Y;
458Result.Day := CnMonthDays(Y, M);
459if Result.Day = 0 then
460raise Exception.CreateFmt('指定的年份 %d 不存在 %s 月', [Y, CnMonthName(M)]);
461Result.Month := M;
462if Byte(D) > Result.Day then
463raise Exception.CreateFmt('指定的 %d 年 %s 月 %d 日无效,该月只有 %d 天',
464[Y, CnMonthName(M), Result.Day]);
465Result.Day := D;
466end;
467
468// 日期是该年的第几天,1月1日为第一天
469function DaysNumberOfDate(yyyy, mm, dd: Word): Integer; overload;
470var
471I: Integer;
472begin
473Result := 0;
474for I := 1 to mm - 1 do
475Inc(Result, MonthDays[IsLeapYear(yyyy), I]);
476Inc(Result, dd);
477end;
478
479function DaysNumberOfDate(ADate: TDateTime): Integer; overload;
480var
481yyyy, mm, dd: Word;
482begin
483DecodeDate(ADate, yyyy, mm, dd);
484Result := DaysNumberOfDate(yyyy, mm, dd);
485end;
486
487function ToCnDate(ADate: TDateTime): TCalendarDate;
488var
489yyyy, mm, dd: Word;
490begin
491DecodeDate(ADate, yyyy, mm, dd);
492Result := ToCnDate(yyyy, mm, dd);
493end;
494
495function CnMonthDays(Y: Word; M: Shortint): Byte;
496var
497CnMonth, CnMonthDays: array [0 .. 15] of Integer;
498Bytes: array [0 .. 3] of Byte;
499I: Integer;
500LeapMonth: Integer;
501CnMonthData: Word;
502begin
503Result := 0;
504if (Y < 1901) or (Y > 2050) then
505Exit;
506Bytes[0] := CnData[(Y - 1901) * 4];
507Bytes[1] := CnData[(Y - 1901) * 4 + 1];
508Bytes[2] := CnData[(Y - 1901) * 4 + 2];
509Bytes[3] := CnData[(Y - 1901) * 4 + 3];
510if (Bytes[0] and $80) <> 0 then
511CnMonth[0] := 12
512else
513CnMonth[0] := 11;
514CnMonthData := Bytes[1];
515CnMonthData := CnMonthData shl 8;
516CnMonthData := CnMonthData or Bytes[2];
517LeapMonth := Bytes[3];
518for I := 15 downto 0 do
519begin
520CnMonthDays[15 - I] := 29;
521if ((1 shl I) and CnMonthData) <> 0 then
522Inc(CnMonthDays[15 - I]);
523if CnMonth[15 - I] = LeapMonth then
524CnMonth[15 - I + 1] := -LeapMonth
525else
526begin
527if CnMonth[15 - I] < 0 then // 上月为闰月
528CnMonth[15 - I + 1] := -CnMonth[15 - I] + 1
529else
530CnMonth[15 - I + 1] := CnMonth[15 - I] + 1;
531if CnMonth[15 - I + 1] > 12 then
532CnMonth[15 - I + 1] := 1;
533end;
534end;
535I := 0;
536while I < 16 do
537begin
538if CnMonth[I] = 1 then // 1月
539begin
540while I < 16 do
541begin
542if CnMonth[I] = M then
543begin
544Result := CnMonthDays[I];
545Break;
546end;
547Inc(I);
548end;
549Break;
550end;
551Inc(I);
552end;
553end;
554
555function ToCnDate(yyyy, mm, dd: Word): TCalendarDate;
556var
557CnMonth, CnMonthDays: array [0 .. 15] of Integer;
558CnBeginDay, LeapMonth: Integer;
559Bytes: array [0 .. 3] of Byte;
560I: Integer;
561CnMonthData: Word;
562ACnEnd: TCalendarDate;
563DaysCount, CnDaysCount, ResultMonth, ResultDay: Integer;
564begin
565if (yyyy < 1901) or (yyyy > 2050) then
566begin
567Result.Value := 0;
568Exit;
569end;
570Bytes[0] := CnData[(yyyy - 1901) * 4];
571Bytes[1] := CnData[(yyyy - 1901) * 4 + 1];
572Bytes[2] := CnData[(yyyy - 1901) * 4 + 2];
573Bytes[3] := CnData[(yyyy - 1901) * 4 + 3];
574if (Bytes[0] and $80) <> 0 then
575CnMonth[0] := 12
576else
577CnMonth[0] := 11;
578CnBeginDay := (Bytes[0] and $7F);
579CnMonthData := Bytes[1];
580CnMonthData := CnMonthData shl 8;
581CnMonthData := CnMonthData or Bytes[2];
582LeapMonth := Bytes[3];
583for I := 15 downto 0 do
584begin
585CnMonthDays[15 - I] := 29;
586if ((1 shl I) and CnMonthData) <> 0 then
587Inc(CnMonthDays[15 - I]);
588if CnMonth[15 - I] = LeapMonth then
589CnMonth[15 - I + 1] := -LeapMonth
590else
591begin
592if CnMonth[15 - I] < 0 then // 上月为闰月
593CnMonth[15 - I + 1] := -CnMonth[15 - I] + 1
594else
595CnMonth[15 - I + 1] := CnMonth[15 - I] + 1;
596if CnMonth[15 - I + 1] > 12 then
597CnMonth[15 - I + 1] := 1;
598end;
599end;
600DaysCount := DaysNumberOfDate(yyyy, mm, dd) - 1;
601if DaysCount <= (CnMonthDays[0] - CnBeginDay) then
602begin
603ACnEnd := ToCnDate(EncodeDate(yyyy - 1, 12, 31));
604if (yyyy > 1901) and (ACnEnd.Month < 0) then
605ResultMonth := -CnMonth[0]
606else
607ResultMonth := CnMonth[0];
608ResultDay := CnBeginDay + DaysCount;
609end
610else
611begin
612CnDaysCount := CnMonthDays[0] - CnBeginDay;
613I := 1;
614while (CnDaysCount < DaysCount) and
615(CnDaysCount + CnMonthDays[I] < DaysCount) do
616begin
617Inc(CnDaysCount, CnMonthDays[I]);
618Inc(I);
619end;
620ResultMonth := CnMonth[I];
621ResultDay := DaysCount - CnDaysCount;
622end;
623if (ResultMonth in [11, 12]) and (mm in [1, 2]) then
624Dec(yyyy);
625Result.Year := yyyy;
626Result.Month := ResultMonth;
627Result.Day := ResultDay;
628end;
629
630//---------------------------------------------------------------------------
631
632function ToEnDate(ADate: TCalendarDate): TDateTime;
633var
634tempDate: TDateTime;
635ADelta: Integer;
636ACnTemp: TCalendarDate;
637begin
638if ADate.Month > 11 then
639tempDate := EncodeDate(ADate.Year - 1, ADate.Month, ADate.Day)
640else
641tempDate := EncodeDate(ADate.Year, abs(ADate.Month), ADate.Day);
642Result := 0;
643while Result = 0 do
644begin
645ACnTemp := ToCnDate(tempDate);
646if ACnTemp.Value = ADate.Value then
647begin
648Result := tempDate;
649Break;
650end
651else
652begin
653ADelta := 0;
654if ADate.Year <> ACnTemp.Year then
655ADelta := (abs(ADate.Month) + 11 - abs(ACnTemp.Month)) * 29
656else if ADate.Month <> ACnTemp.Month then
657begin
658if ADate.Month <> -ACnTemp.Month then
659ADelta := (abs(ADate.Month) - abs(ACnTemp.Month) - 1) * 29;
660end;
661if ADelta = 0 then
662ADelta := 1;
663tempDate := IncDay(tempDate, ADelta);
664end;
665end;
666end;
667
668function CnYearOf(ADate: TDateTime): Smallint;
669begin
670Result := ToCnDate(ADate).Year;
671end;
672
673function CnYearName(ADate: TCalendarDate): String;
674begin
675Result := CnSkyNames[(ADate.Year - BaseSkyStemDate) mod 10 + 1] + CnEarthNames
676[(ADate.Year - BaseAnimalDate) mod 12 + 1];
677end;
678
679function CnYearName(ADate: TDateTime): String;
680begin
681Result := CnYearName(ToCnDate(ADate));
682end;
683
684function CnMonthOf(ADate: TDateTime): Shortint;
685begin
686Result := ToCnDate(ADate).Month;
687end;
688
689function CnMonthName(ADate: TCalendarDate): String;
690begin
691if ADate.Month < 0 then
692Result := '闰' + CnMonthNames[-ADate.Month]
693else
694Result := CnMonthNames[ADate.Month]
695end;
696
697function CnMonthName(ADate: TDateTime): String;
698begin
699Result := CnMonthName(ToCnDate(ADate));
700end;
701
702function CnDayOf(ADate: TDateTime): Shortint;
703begin
704Result := ToCnDate(ADate).Day;
705end;
706
707function CnDayName(ADate: TCalendarDate): String;
708begin
709Result := CnDayNames[ADate.Day];
710end;
711
712function CnDayName(ADate: TDateTime): String;
713begin
714Result := CnDayNames[ToCnDate(ADate).Day];
715end;
716
717function CnHourName(AHour: Byte): String;
718begin
719AHour := ((AHour + 1) shr 1) mod 12; //
720Result := CnEarthNames[AHour + 1];
721end;
722
723function CnAnimalOf(ADate: TCalendarDate): String;
724begin
725Result := CnEarthNames[(ADate.Year - BaseAnimalDate) mod 12 + 1] +
726CnAnimals[(ADate.Year - BaseAnimalDate) mod 12 + 1];
727end;
728
729function CnAnimalOf(ADate: TDateTime): String;
730begin
731Result := CnAnimalOf(ToCnDate(ADate));
732end;
733
734function DateOfCnSolarTerm(AYear: Smallint; AName: String): TDateTime;
735var
736AIndex: Integer;
737AFlags: Byte;
738ADay: Word;
739I: Integer;
740begin
741AIndex := (AYear - START_YEAR) * 12;
742Result := 0;
743for I := 1 to 12 do
744begin
745AFlags := gLunarHolDay[AIndex + I - 1];
746ADay := 15 - (AFlags shr 4) and $0F;
747if CnSolarTerms[((I - 1) shl 1) + 1] = AName then
748begin
749Result := EncodeDate(AYear, I, ADay);
750Break;
751end
752else
753begin
754ADay := 15 + (AFlags and $0F);
755if CnSolarTerms[I shl 1] = AName then
756begin
757Result := EncodeDate(AYear, I, ADay);
758Break;
759end
760end;
761end;
762end;
763
764function CnSolarTerm(ADate: TCalendarDate): Shortint;
765begin
766Result := CnSolarTerm(ToEnDate(ADate));
767end;
768
769function CnSolarTerm(ADate: TDateTime): Shortint;
770var
771iYear, iMonth, iDay: Word;
772begin
773DecodeDate(ADate, iYear, iMonth, iDay);
774Result := CnSolarTerm(iYear, iMonth, iDay);
775end;
776
777function CnSolarTermName(ADate: TCalendarDate): String;
778begin
779Result := CnSolarTerms[CnSolarTerm(ADate)];
780end;
781
782function CnSolarTermName(ADate: TDateTime): String;
783begin
784Result := CnSolarTerms[CnSolarTerm(ADate)];
785end;
786
787function CnSolarTerm(AYear, AMonth, ADay: Word): Shortint;
788var
789Flag: Byte;
790Day: Word;
791AIndex: Integer;
792begin
793AIndex := (AYear - START_YEAR) * 12 + AMonth - 1;
794Flag := gLunarHolDay[AIndex];
795if ADay < 15 then
796Day := 15 - ((Flag shr 4) and $0F)
797else
798Day := (Flag and $0F) + 15;
799if ADay = Day then
800if ADay > 15 then
801Result := (AMonth - 1) * 2 + 2
802else
803Result := (AMonth - 1) * 2 + 1
804else
805Result := 0;
806end;
807
808function CalcCnFirstDay(ADate: TDateTime): TDateTime;
809var
810ATemp: TCalendarDate;
811begin
812ATemp.Year := CnYearOf(ADate);
813ATemp.Month := 1;
814ATemp.Day := 1;
815Result := ToEnDate(ATemp);
816end;
817
818function CnWeeksInYear(const ADate: TDateTime): Byte;
819var
820CnStartYearDate, CnEndYearDate: TDateTime;
821begin
822CnStartYearDate := CalcCnFirstDay(ADate);
823CnEndYearDate := CalcCnFirstDay(IncYear(ADate));
824Result := Trunc(CnEndYearDate - CnStartYearDate) div 7;
825if DayOfTheWeek(CnStartYearDate) <> 1 then
826Inc(Result, 1);
827if DayOfTheWeek(CnEndYearDate) <> 7 then
828Inc(Result, 1);
829end;
830
831function CnIncYear(ADate: TCalendarDate; ADelta: Integer): TCalendarDate;
832var
833Days: Integer;
834begin
835Inc(ADate.Year, ADelta);
836if (ADate.Year < 1901) or (ADate.Year > 2050) then
837raise Exception.Create('指定的年份范围越界,农历只支持 1901-2050年范围。');
838Result.Year := ADate.Year;
839if ADate.Month < 0 then
840Result.Month := -ADate.Month
841else
842Result.Month := ADate.Month;
843Days := CnMonthDays(Result.Year, Result.Month);
844if ADate.Day <= Days then
845Result.Day := ADate.Day
846else
847raise Exception.CreateFmt('编码日期时出错,无法获取 %d 年 %s 月的天数。',
848[Result.Year, CnMonthName(Result.Month)]);
849end;
850
851function CnIncDay(ADate: TCalendarDate; ADelta: Integer): TCalendarDate;
852begin
853
854end;
855
856function CnIncMonth(ADate: TCalendarDate; ADelta: Integer): TCalendarDate;
857var
858D: Byte;
859begin
860Result.Value := ADate.Value;
861while ADelta > 0 do
862begin
863Result.Day := 1;
864Result := ToCnDate(IncDay(ToEnDate(Result), 31));
865D := CnMonthDays(Result.Year, Result.Month);
866if ADate.Day > D then
867Result.Day := D
868else
869Result.Day := ADate.Day;
870Dec(ADelta);
871end;
872while ADelta < 0 do
873begin
874Result.Day := 1;
875Result := ToCnDate(IncDay(ToEnDate(Result), -2));
876D := CnMonthDays(Result.Year, Result.Month);
877if ADate.Day > D then
878Result.Day := D
879else
880Result.Day := ADate.Day;
881Inc(ADelta);
882end;
883end;
884
885end.
886