procedure ThxCalendar.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure ThxCalendar.MouseToCell(X, Y: Integer; var ACol, ARow: Longint);
var
Coord: TGridCoord;
begin
Coord := MouseCoord(X, Y);
ACol := Coord.X;
ARow := Coord.Y;
end;
{ AcceptDropped override }
procedure ThxCalendar.AcceptDropped(Sender, Source: TObject; X, Y: integer);
var
ACol, ARow: LongInt;
Value: string;
begin
{ convert X and Y to Col and Row for convenience }
MouseToCell(X, Y, ACol, ARow);
{ let user respond to event }
if Assigned(FOnDroppedCell) then FOnDroppedCell(Source, ACol, ARow, Value);
{ if user returns a string add it to the cells list }
if Value <> '' then SetCellString(ACol, ARow, 0, Value);
{ set focus to hxCalendar }
SetFocus;
{ force redraw }
Invalidate;
end;
{ CellDragOver override }
procedure ThxCalendar.CellDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
ACol, ARow: LongInt;
begin
{ convert X and Y to Col and Row for convenience }
MouseToCell(X, Y, ACol, ARow);
{ allow user to set Accept the way they want }
if Assigned(FOnCellDragOver) then FOnCellDragOver(Sender, Source, ACol, ARow, State, Accept);
{ if Accept = true then apply further logic else leave Accept = false }
if Accept = true then
if (not FUpdating) and (not FReadOnly) and (CellText[ACol, ARow] <> '') then
Accept := true
else Accept := false;
end;
{ SetCellString - adds a string to the cells stringlist based on Col
or Row or Day of month. }
procedure ThxCalendar.SetCellString(ACol, ARow, ADay: Integer; Value: string);
var
i, j: integer;
TheCellText: string;
begin
if (not FUpdating) and (not FReadOnly) and (CellText[ACol, ARow] <> '') then
begin
{ if ADay is being used calc ACol and ARow. Doesn't matter if
ACol and ARow are <> 0 we just calc them anyway }
if ADay <> 0 then
begin
for i := 0 to 6 do
for j := 1 to 6 do
begin
TheCellText := CellText[i, j];
if (TheCellText <> '') and (ADay = StrToInt(TheCellText)) then
begin
ACol := i;
ARow := j;
end;
end;
end;
{ if no StringList assigned then create one }
if FCalStrings[ACol, ARow] = nil then
FCalStrings[ACol, ARow] := TStringList.Create;
{ add the line of text }
FCalStrings[ACol, ARow].Add(Value);
end;
end;
procedure ThxCalendar.Click;
var
TheCellText: string;
begin
inherited Click;
TheCellText := CellText[Col, Row];
if TheCellText <> '' then Day := StrToInt(TheCellText);
end;
function ThxCalendar.IsLeapYear(AYear: Integer): Boolean;
begin
Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
end;
function ThxCalendar.DaysPerMonth(AYear, AMonth: Integer): Integer;
const
DaysInMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
begin
Result := DaysInMonth[AMonth];
if (AMonth = 2) and IsLeapYear(AYear) then Inc(Result); { leap-year Feb is special }
end;
function ThxCalendar.DaysThisMonth: Integer;
begin
Result := DaysPerMonth(Year, Month);
end;
{procedure ThxCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
TheText: string;
begin
TheText := CellText[ACol, ARow];
with ARect, Canvas do
TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
end;}
procedure ThxCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
HzDate:THzDate;
TheText,ry,dz,hzdaystr,sf: string;
MyDate:tdate;
begin
TheText := CellText[ACol, ARow];
// DecodeDate(FDate, AYear, AMonth, ADay);
if (TheText<>'') and (ARow<>0) then
begin
MyDate := EncodeDate(year, month, strtoint(TheText));
HzDate := ToLunar(MyDate);
dz:= GetJQ(MyDate);
if dz = '' Then
if HzDate.Day = 1 then
ry:=FormatLunarMonth(HzDate.Month,HzDate.isLeap)
else
ry := FormatLunarDay(Hzdate.Day);
if GetsFtv(MyDate)<>'' then sf :=GetsFtv(MyDate);
if GetlFtv(hzDate)<>'' then sf :=sf+GetlFtv(hzDate);
end
else MyDate := 0;
with ARect, Canvas do
begin
if dz<>'' then
begin
Font.Color := $000000FF ;
TextRect(ARect, Left +2,
Top +2, TheText+sf);
TextOut(ARect.Left + Font.Size+10, ARect.Top + 25, dz);
end
else
begin
if sf<>'' then
begin
Font.Color :=$000000FF;
TextRect(ARect, Left + 2,
Top +2, TheText+sf);
end
else
begin
Font.Color :=clBlue;
TextRect(ARect, Left + 2,
Top +2, TheText+sf);
end;
Font.Color :=clBlue;
TextOut(ARect.Left + Font.Size+10, ARect.Top + 25, ry);
end;
end;
end;
function ThxCalendar.GetCellText(ACol, ARow: Integer): string;
var
DayNum: Integer;
begin
if ARow = 0 then { day names at tops of columns }
Result := ShortDayNames[(StartOfWeek + ACol) mod 7 + 1]
else
begin
DayNum := FMonthOffset + ACol + (ARow - 1) * 7;
if (DayNum < 1) or (DayNum > DaysThisMonth) then Result := ''
else Result := IntToStr(DayNum);
end;
end;
function ThxCalendar.SelectCell(ACol, ARow: Longint): Boolean;
begin
if ((not FUpdating) and FReadOnly) or (CellText[ACol, ARow] = '') then
Result := False
else Result := inherited SelectCell(ACol, ARow);
end;
procedure ThxCalendar.SetCalendarDate(Value: TDate);
begin
FDate := Value;
UpdateCalendar;
Change;
end;
function ThxCalendar.StoreCalendarDate: Boolean;
begin
Result := not FUseCurrentDate;
end;
function ThxCalendar.GetDateElement(Index: Integer): Integer;
var
AYear, AMonth, ADay: Word;
begin
DecodeDate(FDate, AYear, AMonth, ADay);
case Index of
1: Result := AYear;
2: Result := AMonth;
3: Result := ADay;
else Result := -1;
end;
end;
procedure ThxCalendar.SetDateElement(Index: Integer; Value: Integer);
var
AYear, AMonth, ADay: Word;
begin
if Value > 0 then
begin
DecodeDate(FDate, AYear, AMonth, ADay);
case Index of
1: if AYear <> Value then AYear := Value else Exit;
2: if (Value <= 12) and (Value <> AMonth) then AMonth := Value else Exit;
3: if (Value <= DaysThisMonth) and (Value <> ADay) then ADay := Value else Exit;
else Exit;
end;
FDate := EncodeDate(AYear, AMonth, ADay);
FUseCurrentDate := False;
UpdateCalendar;
Change;
end;
end;
procedure ThxCalendar.SetStartOfWeek(Value: TDayOfWeek);
begin
if Value <> FStartOfWeek then
begin
FStartOfWeek := Value;
UpdateCalendar;
end;
end;
procedure ThxCalendar.SetUseCurrentDate(Value: Boolean);
begin
if Value <> FUseCurrentDate then
begin
FUseCurrentDate := Value;
if Value then
begin
FDate := Date; { use the current date, then }
UpdateCalendar;
end;
end;
end;
{ Given a value of 1 or -1, moves to Next or Prev month accordingly }
procedure ThxCalendar.ChangeMonth(Delta: Integer);
var
AYear, AMonth, ADay: Word;
NewDate: TDate;
CurDay: Integer;
begin
DecodeDate(FDate, AYear, AMonth, ADay);
CurDay := ADay;
if Delta > 0 then ADay := DaysPerMonth(AYear, AMonth)
else ADay := 1;
NewDate := EncodeDate(AYear, AMonth, ADay);
NewDate := NewDate + Delta;
DecodeDate(NewDate, AYear, AMonth, ADay);
if DaysPerMonth(AYear, AMonth) > CurDay then ADay := CurDay
else ADay := DaysPerMonth(AYear, AMonth);
CalendarDate := EncodeDate(AYear, AMonth, ADay);
end;
procedure ThxCalendar.PrevMonth;
begin
ChangeMonth(-1);
end;
procedure ThxCalendar.NextMonth;
begin
ChangeMonth(1);
end;
procedure ThxCalendar.NextYear;
begin
if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
Year := Year + 1;
end;
procedure ThxCalendar.PrevYear;
begin
if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
Year := Year - 1;
end;
procedure ThxCalendar.UpdateCalendar;
var
AYear, AMonth, ADay: Word;
FirstDate: TDate;
begin
FUpdating := True;
try
DecodeDate(FDate, AYear, AMonth, ADay);
FirstDate := EncodeDate(AYear, AMonth, 1);
FMonthOffset := 2 - ((DayOfWeek(FirstDate) - StartOfWeek + 7) mod 7); { day of week for 1st of month }
if FMonthOffset = 2 then FMonthOffset := -5;
MoveColRow((ADay - FMonthOffset) mod 7, (ADay - FMonthOffset) div 7 + 1,
False, False);
Invalidate;
finally
FUpdating := False;
end;
end;
procedure ThxCalendar.WMSize(var Message: TWMSize);
var
GridLines: Integer;
begin
GridLines := 6 * GridLineWidth;
DefaultColWidth := (Message.Width - GridLines) div 7;
DefaultRowHeight := (Message.Height - GridLines) div 7;
end;
function ThxCalendar.DaysOfLunarYear(y: integer): integer;
var
i, sum: integer;
begin
sum:= 348; //29 * 12
i:= $8000;
while i > $8 do
begin
if (lunarInfo[y - 1900] and i) > 0 then sum := sum + 1 ;
i:= i shr 1;
end;
Result:= sum + DaysOfLeapMonth(y);
end;
// 返回农历 y年闰月的天数
function ThxCalendar.DaysOfLeapMonth(y: integer): integer;
begin
if leapMonth(y) > 0 then
if (lunarInfo[y - 1899] and $f) = $f then
Result := 30
else
Result := 29
else
Result := 0;
end;
//返回农历 y年闰哪个月 1-12 , 没闰返回 0
function ThxCalendar.leapMonth(y: integer): integer;
var
lm: Word;
begin
lm:= lunarInfo[y - 1900] and $f;
if lm = $f then Result:= 0 else Result:= lm;
end;
//返回农历 y年m月的天数
function ThxCalendar.DaysOfMonth(y, m: integer): integer;
var
temp1, temp2, temp3: Word;
begin
temp1:= lunarInfo[y - 1900];
temp2:= $8000;
if m > 1 then temp2:= $8000 shr (m - 1);
temp3:= temp1 and temp2;
if temp3 > 0 then
Result:= 30
else Result:= 29;
end;
//算出农历, 传入公历日期, 返回农历日期
function ThxCalendar.ToLunar(TheDate: TDate): THzDate;
var
TheYear, TheMonth,leap, temp, offset: integer;
begin
if (32 > TheDate) or (TheDate >= 73416) then //73415=EncodeDate(2100,12,31)
begin //32 = EncodeDate(1900,1,31) 农历1900年1月1日
Result.Year := 0;
Result.Month:= 0;
Result.Day := 0;
Result.isLeap := False;
exit;
end;
offset:= DaysBetween(32,TheDate);
TheYear:= 1900;
while offset > 0 do
begin
temp:= DaysOfLunarYear(TheYear);
TheYear := theYear + 1;
offset:= offset - temp;
end;
if offset < 0 then
begin
offset:= offset + temp;
TheYear:= TheYear - 1;
end;
leap:= leapMonth(TheYear); //闰哪个月
result.isLeap := False;
TheMonth := 0;
while offset >= 0 do
begin
TheMonth:= TheMonth + 1;
temp:= DaysOfMonth(TheYear, TheMonth);
offset:= offset - temp; //减去该月天数
if (offset >= 0) and (TheMonth = Leap) then //如果还有剩余天数且本月有闰
begin //减去闰月天数;
temp:= DaysOfLeapMonth(TheYear);
offset:= offset - temp;
if offset < 0 then
result.isLeap := True; //置闰月标志为真;
end;
end;
if offset < 0 then
begin
offset:= offset + temp;
end;
Result.Year := TheYear;
Result.Month:= TheMonth;
Result.Day:= offset + 1;
end;
// 算出公历, 传入农历日期控件, 返回公历
function ThxCalendar.ToGreg(objDate: THzDate): TDate;
var
i, j, t, leap, temp, offset: integer;
isLeap: Boolean;
y, m: integer;
begin
Result:= EncodeDate(1,1,1);
if not ChkHzDate(objDate) then exit;
//本年内从大年初一过来的天数
offset:= 0;
i:= 1;
while i < m do
begin
if i = leap then
begin
if isLeap then
begin
temp:= DaysOfleapMonth(y);
isLeap:= False;
end
else begin
temp:= daysOfmonth(y, i);
isLeap:= True;
i:= i - 1;
end;
end else
temp:= daysofmonth(y, i);
offset:= offset + temp;
Inc(i);
end;
offset:= offset + objDate.Day - 1;
if (m = leap) and objDate.isLeap then //若为闰月,再加上前一个非闰月天数
offset:= offset + DaysOfMonth(y, m);
// 该年到 2000.1.1 这几年的天数
if y > 2000 then
begin
i:= 2000;
j:= y - 1;
end
else begin
i:= y;
j:= 1999;
end;
temp:= 0;
for t:= i to j do
begin
temp:= temp + DaysOfLunarYear(t);
end;
if y > 1999 then offset:= offset + temp
else offset:= offset - temp;
// 检查农历日期是否合法
function ThxCalendar.ChkHzDate(objDate: THzDate): Boolean;
begin
if (objDate.Year > 2099) or (objDate.Year < 1901)
or (objDate.Month > 12) or (objDate.Day > 30) then
begin
Result:= False;
exit;
end;
Result:= True;
if objDate.isLeap then
begin
if leapMonth(objDate.Year) = objDate.Month then
begin
if DaysOfleapMonth(objDate.Year) < objDate.Day then
Result:= False;
end else Result:= False;
end else
begin
if DaysOfMonth(objDate.Year,objDate.Month) < objDate.Day then
Result:= False;
end;
end;
// 某年的第n个节气为几日(从0小寒起算)
function ThxCalendar.sTerm(y, n: integer): TDateTime;
var
temp: TDateTime;
t: real;
i: Int64;
begin
t:= sTermInfo[n];
t:= t * 60000;
t:= t + 31556925974.7 * (y - 1900);
i:= Round(t);
Temp:= IncMilliSecond(EncodeDateTime(1900,1,6,2,5,0,0),i);
Result:= temp;
end;
// 传入 offset 返回干支, 0=甲子
function ThxCalendar.cyclical(num: integer): string;
begin
Result:= Gan[num mod 10] + Zhi[num mod 12]+'('+Animals[num mod 12]+'年)';
end;
function ThxCalendar.FormatLunarDay(day:integer): string;
begin
case day of
1..10: Result:= nStr2[0] + nStr1[day];
11..19: Result:= nStr2[1] + nStr1[day - 10];
20: Result:= nStr1[2] + nStr1[10];
21..29: Result:= nStr2[2] + nStr1[day - 20];
30: Result:= nStr1[3] + nStr1[10];
else Result :='';
end;
end;
function ThxCalendar.FormatLunarMonth(month:integer;isLeap:boolean): string;
begin
case month of
1..10: Result:= nStr1[month];
11..12: Result:= nStr1[10] + nStr1[month - 10];
else result :='';
end;
if isLeap then Result:= '闰' + Result;
Result:= Result + '月';
end;
function ThxCalendar.FormatLunarYear(year:integer): string;
var
temp: integer;
zero: string;
begin
zero:= '零';
temp:= year div 1000;
Result:= nStr1[temp];
year:= year - temp * 1000;
if year >= 100 then
begin
temp:= year div 100;
Result:= Result + nStr1[temp];
year:= year - temp * 100;
end
else
Result:= Result + zero;
if year >= 10 then
begin
temp:= year div 10;
Result:= Result + nStr1[temp];
year:= year - temp * 10;
end
else
Result:= Result + zero;
if year = 0 then Result:= Result + zero else
Result:= Result + nStr1[year];
Result:= Result + '年';
end;
// 取得指定日期的节气
function ThxCalendar.GetJQ(TheDate: TDate): string;
var
jq: Integer;
term: TDateTime;
begin
Result:= '';
jq:= (MonthOf(TheDate) - 1) * 2;
term:= sTerm(Yearof(TheDate), jq); //节气时间
if DateOf(term) = TheDate then Result:= solarTerm[jq]
else
begin
term:= sTerm(Yearof(TheDate), jq + 1); //中气时间
if DateOf(term) = TheDate then Result:= solarTerm[jq+1];
end;
end;
function ThxCalendar.GetsFtv(TheDate: TDate): string;
var
sf:string;
jlsl:integer;
begin
for jlsl :=0 to 22 do
begin
sf:=formatdatetime('mmdd',TheDate);
if sf=copy(sFtv[jlsl],1,4)then
begin
Result:=copy(sFtv[jlsl],5,length(sFtv[jlsl])-4);
end;
end;
end;
function ThxCalendar.GetlFtv(TheDate: ThzDate): string;
var
sf,m,d:string;
jlsl:integer;
begin
//HzDate := ToLunar(MyDate);
for jlsl :=0 to 9 do
begin
if TheDate.month<10 then m:='0'+inttostr(TheDate.month)else m:=inttostr(TheDate.month);
if TheDate.day=0 then d:='01';
if TheDate.day<10 then d:='0'+inttostr(TheDate.day)else d:=inttostr(TheDate.day);
sf:=m+d;
if sf=copy(lFtv[jlsl],1,4)then
begin
Result:=copy(lFtv[jlsl],5,length(lFtv[jlsl])-4);
end;
end;
end;
end.