1
/* $Id: lunar.c,v 1.6 2001/10/29 05:55:39 fflee Exp $ */
3
char version[] = "Lunar Version 2.2 (October 28, 2001)";
5
/*----------------------------------------------------------------------------
8
Lunar 2.2: A Calendar Conversion Program
10
Gregorian Solar Calendar and Chinese Lunar Calendar
11
---------------------------------------------------
13
# Copyright (C) 1988,1989,1991,1992,2001 Fung F. Lee and Ricky Yeung
16
# This program is free software; you can redistribute it and/or
17
# modify it under the terms of the GNU General Public License
18
# as published by the Free Software Foundation; either version 2
19
# of the License, or any later version.
21
# This program is distributed in the hope that it will be useful,
22
# but WITHOUT ANY WARRANTY; without even the implied warranty of
23
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
# GNU General Public License for more details.
26
# You should have received a copy of the GNU General Public License
27
# along with this program; if not, write to the Free Software Foundation,
28
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32
# The last version of this program was released on July 23, 1992 as
33
# version 2.1a. This program was first released under the terms of
34
# GNU GPL on October 28, 2001 as version 2.2. Both versions are identical
35
# except for the license text.
38
# Please send your comments and suggestions to the authors:
39
# Fung F. Lee lee@umunhum.stanford.edu
40
# Ricky Yeung cryeung@hotmail.com
42
# The special "bitmap" file "lunar.bitmap" was contributed
43
# by Weimin Liu (weimin@alpha.ece.jhu.edu).
45
# Special thanks to Hwei Chen Ti (chetihc@nuscc.nus.sg or
46
# chetihc@nusvm.bitnet) who extended the tables from 2001 to 2049.
48
----------------------------------------------------------------------------*/
51
This document contains Highest-bit-set GuoBiao (HGB) code, as adopted by
52
CCDOS on IBM PC compatibles, ChineseTalk 6.0 (GB version) on Macintosh,
53
and cxterm on UNIX and X window. Hence, one may need to transfer this
54
document as a **binary** file.
57
1. "Zhong1guo2 yin1yang2 ri4yue4 dui4zhao4 wan4nian2li4" by Lin2 Qi3yuan2.
58
���й��������¶���������������
59
2. "Ming4li3 ge2xin1 zi3ping2 cui4yan2" by Xu2 Le4wu2.
60
������������ƽ���ԡ�����
61
3. Da1zhong4 wan4nian2li4.
69
/* "Bitmap" constants */
70
#define BMRow 7 /* number of rows for each bitmap */
71
#define BMCol 11 /* number of columns for each bitmap */
72
#define NBM 26 /* number of bitmaps */
81
int year, month, day, hour, weekday;
82
int leap; /* the lunar month is a leap month */
89
static int daysInSolarMonth[13] = {
90
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
92
static int moon[2] = {29,30}; /* a short (long) lunar month has 29 (30) days */
94
static char *Gan[] = {
95
"Jia3", "Yi3", "Bing3", "Ding1", "Wu4",
96
"Ji3", "Geng1", "Xin1", "Ren2", "Gui3"
99
static char *Zhi[] = {
100
"Zi3", "Chou3", "Yin2", "Mao3", "Chen2", "Si4",
101
"Wu3", "Wei4", "Shen1", "You3", "Xu1", "Hai4"
104
static char *ShengXiao[] = {
105
"Mouse", "Ox", "Tiger", "Rabbit", "Dragon", "Snake",
106
"Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"
109
static char *weekday[] = {
110
"Sunday", "Monday", "Tuesday", "Wednesday",
111
"Thursday", "Friday", "Saturday"
114
static char *GanGB[] = {
115
"��", "��", "��", "��", "��",
116
"��", "��", "��", "��", "��"
119
static char *ZhiGB[] = {
120
"��", "��", "��", "î", "��", "��",
121
"��", "δ", "��", "��", "��", "��"
124
static char *ShengXiaoGB[] = {
125
"��", "ţ", "��", "��", "��", "��",
126
"��", "��", "��", "��", "��", "��"
129
static char *weekdayGB[] = {
130
"��", "һ", "��", "��",
135
Date solar, lunar, gan, zhi, gan2, zhi2, lunar2;
137
int ymonth[Nyear]; /* number of lunar months in the years */
138
int yday[Nyear]; /* number of lunar days in the years */
139
int mday[Nmonth+1]; /* number of days in the months of the lunar year */
140
int jieAlert; /* if there is uncertainty in JieQi calculation */
142
int showHZ = 0; /* output in hanzi */
143
int showBM = 0; /* output in bitmap */
144
char BMfile[] = "lunar.bitmap"; /* bit map file */
145
char GZBM[NBM][BMRow][BMCol]; /* the bitmap array */
148
void Solar2Lunar(), Lunar2Solar();
149
long Solar2Day(), Solar2Day1(), Lunar2Day();
150
void Day2Lunar(), Day2Solar();
151
int make_yday(), make_mday(), GZcycle();
153
int JieDate(), JieDate();
154
void readBM(), display3();
155
void Report(), ReportE(), ReportBM(), ReportGB();
156
void usage(), Error();
163
int year, month, day, hour, i, k, option, inverse=0, leap=0;
167
printf("%s\n\n", version);
169
for (k=1; k<argc && argv[k][0]=='-'; k++)
174
case 'i': inverse = 1; break;
175
case 'l': if (inverse) leap=1; else usage(); break;
176
case 'h': showHZ = 1; break;
177
case 'b': showBM = 1; break;
178
default: usage(); break;
181
if (showBM) readBM();
182
if (!((argc - k >= 3) && (argc - k <= 4))) usage();
184
for (i=0; k<argc && i<4; k++, i++)
186
if (sscanf(argv[k], "%d", &dateInfo[i]) != 1)
193
if (!(year>=Cyear && year<Cyear+Nyear))
194
Error("Year out of range.");
195
if (!(month>=1 && month<=12))
196
Error("Month out of range.");
197
if (!(day>=1 && day<=31) || (inverse && day>30))
198
Error("Day out of range.");
199
if (!(hour>=0 && hour<=23))
200
Error("Hour out of range.");
202
if (!inverse && year==SolarFirstDate.year &&
203
CmpDate(month, day, SolarFirstDate.month, SolarFirstDate.day)<0)
204
Error("Date out of range.");
229
printf("Usage:\n\n");
230
printf("Solar->Lunar:\t%s [-h] [-b] year month day [hour]\n", progname);
231
printf("\t\t(in Solar Calendar, 24 hour clock)\n\n");
232
printf("Lunar->Solar:\t%s [-h] [-b] -i [-l] year month day [hour]\n",
234
printf("\t\t(in Lunar Calendar, 24 hour clock)\n");
235
printf("\t\t-l means the month is a leap month (\"run4 yue4\")\n\n");
236
printf("\t\t-h means output in hanzi (GB)\n");
237
printf("\t\t-b means output in \"bitmap\"\n\n");
238
printf("Date range: about %d years from the Solar Date %d.%d.%d\n", Nyear,
239
SolarFirstDate.year, SolarFirstDate.month, SolarFirstDate.day);
250
offset = Solar2Day(&solar);
251
solar.weekday = (offset + SolarFirstDate.weekday) % 7;
253
/* A lunar day begins at 11 p.m. */
254
if (solar.hour == 23)
257
Day2Lunar(offset, &lunar);
258
lunar.hour = solar.hour;
259
CalGZ(offset, &lunar, &gan, &zhi);
261
jieAlert = JieDate(&solar, &lunar2);
262
lunar2.day = lunar.day;
263
lunar2.hour = lunar.hour;
264
CalGZ(offset, &lunar2, &gan2, &zhi2);
274
/* A solar day begins at 12 a.m. */
275
adj = (lunar.hour == 23)? -1 : 0;
276
offset = Lunar2Day(&lunar);
277
solar.weekday = (offset+ adj + SolarFirstDate.weekday) % 7;
278
Day2Solar(offset + adj, &solar);
279
solar.hour = lunar.hour;
280
CalGZ(offset, &lunar, &gan, &zhi);
282
jieAlert = JieDate(&solar, &lunar2);
283
lunar2.day = lunar.day;
284
lunar2.hour = lunar.hour;
285
CalGZ(offset, &lunar2, &gan2, &zhi2);
289
#define LeapYear(y) (((y)%4==0) && ((y)%100!=0) || ((y)%400==0))
291
/* BYEAR % 4 == 1 and BYEAR % 400 == 1 for easy calculation of leap years */
292
/* assert(BYEAR <= SolarFirstDate.year) */
297
return (Solar2Day1(d) - Solar2Day1(&SolarFirstDate));
301
/* Compute the number of days from the Solar date BYEAR.1.1 */
308
delta = d->year - BYEAR;
309
if (delta<0) Error("Internal error: pick a larger constant for BYEAR.");
310
offset = delta * 365 + delta / 4 - delta / 100 + delta / 400;
311
for (i=1; i< d->month; i++)
312
offset += daysInSolarMonth[i];
313
if ((d->month > 2) && LeapYear(d->year))
315
offset += d->day - 1;
317
if ((d->month == 2) && LeapYear(d->year))
319
if (d->day > 29) Error("Day out of range.");
321
else if (d->day > daysInSolarMonth[d->month]) Error("Day out of range.");
326
/* Compute offset days of a lunar date from the beginning of the table */
331
int year, i, m, nYear, leapMonth;
334
year = d->year - LunarFirstDate.year;
335
for (i=0; i<year; i++)
338
leapMonth = make_mday(year);
339
if ((d->leap) && (leapMonth!=d->month))
341
printf("%d is not a leap month in year %d.\n", d->month, d->year);
344
for (m=1; m<d->month; m++)
347
((d->month>leapMonth) || (d->leap && (d->month==leapMonth))))
349
offset += d->day - 1;
351
if (d->day > mday[m]) Error("Day out of range.");
357
void Day2Lunar(offset, d)
362
int i, m, nYear, leapMonth;
365
for (i=0; i<nYear && offset > 0; i++)
369
if (i==Nyear) Error("Year out of range.");
370
d->year = i + LunarFirstDate.year;
372
leapMonth = make_mday(i);
373
for (m=1; m<=Nmonth && offset>0; m++)
378
d->leap = 0; /* don't know leap or not yet */
380
if (leapMonth>0) /* has leap month */
382
/* if preceeding month number is the leap month,
383
this month is the actual extra leap month */
384
d->leap = (leapMonth == (m - 1));
386
/* month > leapMonth is off by 1, so adjust it */
387
if (m > leapMonth) --m;
395
void Day2Solar(offset, d)
401
/* offset is the number of days from SolarFirstDate */
402
offset -= Solar2Day(&LunarFirstDate); /* the argument is negative */
403
/* offset is now the number of days from SolarFirstDate.year.1.1 */
405
for (i=SolarFirstDate.year;
406
(i<SolarFirstDate.year+Nyear) && (offset > 0); i++)
407
offset -= 365 + LeapYear(i);
410
--i; /* LeapYear is a macro */
411
offset += 365 + LeapYear(i);
413
if (i==(SolarFirstDate.year + Nyear)) Error("Year out of range.");
416
/* assert(offset<(365+LeapYear(i))); */
417
for (m=1; m<=12; m++)
419
days = daysInSolarMonth[m];
420
if ((m==2) && LeapYear(i)) /* leap February */
438
for (gz = z; gz % 10 != g && gz < 60; gz += 12) ;
439
if (gz >= 60) printf("internal error\n");
444
void CalGZ(offset, d, g, z)
450
year = d->year - LunarFirstDate.year;
451
month = year * 12 + d->month - 1; /* leap months do not count */
453
g->year = (GanFirstDate.year + year) % 10;
454
z->year = (ZhiFirstDate.year + year) % 12;
455
g->month = (GanFirstDate.month + month) % 10;
456
z->month = (ZhiFirstDate.month + month) % 12;
457
g->day = (GanFirstDate.day + offset) % 10;
458
z->day = (ZhiFirstDate.day + offset) % 12;
459
z->hour = ((d->hour + 1) / 2) % 12;
460
g->hour = (g->day * 12 + z->hour) % 10;
472
/* Compare two dates and return <,=,> 0 if the 1st is <,=,> the 2nd */
473
int CmpDate(month1, day1, month2, day2)
474
int month1, day1, month2, day2;
476
if (month1!=month2) return(month1-month2);
477
if (day1!=day2) return(day1-day2);
483
Given a solar date, find the "lunar" date for the purpose of
484
calculating the "4-columns" by taking jie into consideration.
493
flag = CmpDate(ds->month, ds->day,
494
1, fest[ds->year - SolarFirstDate.year - 1][11]);
495
if (flag<0) dl->month = 11;
496
else if (flag>0) dl->month = 12;
497
dl->year = ds->year - 1;
500
for (m=2; m<=12; m++)
502
flag = CmpDate(ds->month, ds->day,
503
m, fest[ds->year - SolarFirstDate.year][m-2]);
507
dl->month = (m-2) % 12;
511
dl->year = ds->year - 1;
518
/* Compute the number of days in each lunar year in the table */
524
for (year = 0; year < Nyear; year++)
526
code = yearInfo[year];
531
i = (code >> 16) & 0x1;
532
yday[year] += moon[i];
535
for (i = 0; i < Nmonth-1; i++)
537
yday[year] += moon[code&0x1];
541
if (leap != 0) ymonth[year]++;
547
/* Compute the days of each month in the given lunar year */
554
code = yearInfo[year];
555
leapMonth = code & 0xf;
556
/* leapMonth == 0 means no leap month */
561
for (i = Nmonth-1; i >= 1; i--)
563
mday[i] = moon[code&0x1];
570
There is a leap month (run4 yue4) L in this year.
571
mday[1] contains the number of days in the 1-st month;
572
mday[L] contains the number of days in the L-th month;
573
mday[L+1] contains the number of days in the L-th leap month;
574
mday[L+2] contains the number of days in the L+1 month, etc.
576
cf. yearInfo[]: info about the leap month is encoded differently.
578
i = (yearInfo[year] >> 16) & 0x1;
579
mday[leapMonth+1] = moon[i];
580
for (i = Nmonth; i >= 1; i--)
582
if (i == leapMonth+1) i--;
583
mday[i] = moon[code&0x1];
604
printf("%s%d%s%2d%s%2d%s%2d%s%s%s\n", "��������",
605
solar.year, "��", solar.month, "��", solar.day,
606
"��", solar.hour, "ʱ��",
607
"����", weekdayGB[solar.weekday]);
608
printf("%s%d%s%s%2d%s%2d%s%s%s%s%s\n", "��������",
609
lunar.year, "��", (lunar.leap? "��":""),
610
lunar.month, "��", lunar.day, "��",
611
ZhiGB[zhi.hour], "ʱ��",
612
"����", ShengXiaoGB[zhi.year]);
613
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s\n", "��֧����",
614
GanGB[gan.year], ZhiGB[zhi.year], "�ꡡ",
615
GanGB[gan.month], ZhiGB[zhi.month], "�¡�",
616
GanGB[gan.day], ZhiGB[zhi.day], "�ա�",
617
GanGB[gan.hour], ZhiGB[zhi.hour], "ʱ��");
618
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
619
"��������������֮ʱ�����֣���",
620
GanGB[gan2.year], ZhiGB[zhi2.year], "�ꡡ",
621
GanGB[gan2.month], ZhiGB[zhi2.month], "�¡�",
622
GanGB[gan2.day], ZhiGB[zhi2.day], "�ա�",
623
GanGB[gan2.hour], ZhiGB[zhi2.hour], "ʱ��");
626
printf("* %s, %s\n", "������",
629
printf("* %s\n", "���������Ҫ��");
630
printf("* %s\n", "����н���ʱ��֮������");
637
printf("Solar : %d.%d.%d.%d\t%s\n", solar.year, solar.month, solar.day,
638
solar.hour, weekday[solar.weekday]);
639
printf("Lunar : %d.%d%s.%d.%d\tShengXiao: %s\n",
640
lunar.year, lunar.month, (lunar.leap?"Leap":""), lunar.day,
641
lunar.hour, ShengXiao[zhi.year] );
642
printf("GanZhi: %s-%s.%s-%s.%s-%s.%s-%s\n",
643
Gan[gan.year], Zhi[zhi.year], Gan[gan.month], Zhi[zhi.month],
644
Gan[gan.day], Zhi[zhi.day], Gan[gan.hour], Zhi[zhi.hour]);
645
printf(" (GanZhi Order)\t%d-%d.%d-%d.%d-%d.%d-%d\n",
646
gan.year+1, zhi.year+1, gan.month+1, zhi.month+1,
647
gan.day+1, zhi.day+1, gan.hour+1, zhi.hour+1);
648
printf(" (JiaZi Cycle)\t%d.%d.%d.%d\n\n",
649
GZcycle(gan.year, zhi.year), GZcycle(gan.month, zhi.month),
650
GZcycle(gan.day, zhi.day), GZcycle(gan.hour, zhi.hour));
651
printf("BaZi (8-characters) according to 'Four Column Calculation':\n");
652
printf(" %s-%s.%s-%s.%s-%s.%s-%s\n",
653
Gan[gan2.year], Zhi[zhi2.year], Gan[gan2.month], Zhi[zhi2.month],
654
Gan[gan2.day], Zhi[zhi2.day], Gan[gan2.hour], Zhi[zhi2.hour]);
655
printf(" (GanZhi Order)\t%d-%d.%d-%d.%d-%d.%d-%d\n",
656
gan2.year+1, zhi2.year+1, gan2.month+1, zhi2.month+1,
657
gan2.day+1, zhi2.day+1, gan2.hour+1, zhi2.hour+1);
658
printf(" (JiaZi Cycle)\t%d.%d.%d.%d\n\n",
659
GZcycle(gan2.year, zhi2.year), GZcycle(gan2.month, zhi2.month),
660
GZcycle(gan2.day, zhi2.day), GZcycle(gan2.hour, zhi2.hour));
663
printf("* The month column may need adjustment because the date falls on a jie.\n");
665
printf("* The day column may need adjustment, too.\n");
666
printf("* Please consult a detailed conversion table.\n");
673
printf("Solar : %d.%d.%d.%d\t%s\n", solar.year, solar.month, solar.day,
674
solar.hour, weekday[solar.weekday]);
675
printf("Lunar : %d.%d%s.%d.%d\tShengXiao: %s\n",
676
lunar.year, lunar.month, (lunar.leap?"Leap":""), lunar.day,
677
lunar.hour, ShengXiao[zhi.year] );
678
printf("GanZhi: \n\n");
679
display3(gan.year+GanBM, zhi.year+ZhiBM, NianBM);
680
display3(gan.month+GanBM, zhi.month+ZhiBM, YueBM);
681
display3(gan.day+GanBM, zhi.day+ZhiBM, RiBM);
682
display3(gan.hour+GanBM, zhi.hour+ZhiBM, ShiBM);
683
printf("\nBaZi : \n\n");
684
display3(gan2.year+GanBM, zhi2.year+ZhiBM, NianBM);
685
display3(gan2.month+GanBM, zhi2.month+ZhiBM, YueBM);
686
display3(gan2.day+GanBM, zhi2.day+ZhiBM, RiBM);
687
display3(gan2.hour+GanBM, zhi2.hour+ZhiBM, ShiBM);
690
printf("* The month column may need adjustment because the date falls on a jie.\n");
692
printf("* The day column may need adjustment, too.\n");
693
printf("* Please consult a detailed conversion table.\n");
703
if ((fp=fopen(BMfile,"r"))==NULL)
705
printf("Bitmap file '%s' not found.\n",BMfile);
708
for (i=0; i<NBM; i++)
709
for (j=0; j<BMRow; j++)
715
if ((c==EOF) || (c=='\n')) break;
718
for (m=k; m<BMCol; m++) GZBM[i][j][m] = ' ';
724
/* Display three ganzhi characters in a row */
725
void display3(i, j, k)
730
for (r=0; r<BMRow; r++)
732
for (c=0; c<BMCol; c++) putchar(GZBM[i][r][c]);
734
for (c=0; c<BMCol; c++) putchar(GZBM[j][r][c]);
736
for (c=0; c<BMCol; c++) putchar(GZBM[k][r][c]);