File Coverage

blib/lib/DateTime/Calendar/Chinese.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 23 25 92.0


line stmt bran cond sub pod time code
1             package DateTime::Calendar::Chinese;
2 3     3   2097 use 5.008;
  3         6  
3 3     3   9 use strict;
  3         2  
  3         46  
4 3     3   914 use utf8;
  3         18  
  3         10  
5 3     3   77 use vars qw($VERSION);
  3         3  
  3         126  
6             BEGIN {
7 3     3   40 $VERSION = '0.99999';
8             }
9              
10 3     3   2085 use DateTime;
  3         917535  
  3         132  
11 0           use DateTime::Astro qw(MEAN_TROPICAL_YEAR MEAN_SYNODIC_MONTH moment dt_from_moment new_moon_after new_moon_before
12             solar_longitude_from_moment
13 3     3   772 );
  0            
14             use DateTime::Event::Chinese qw(chinese_new_year_before);
15             use DateTime::Event::SolarTerm qw(prev_term_at no_major_term_on);
16             use Params::Validate;
17             use Math::Round qw(round);
18             use constant GREGORIAN_CHINESE_EPOCH => DateTime->new(
19             year => -2636, month => 2, day => 15, time_zone => 'UTC');
20             use constant GREGORIAN_CHINESE_EPOCH_MOMENT => moment(GREGORIAN_CHINESE_EPOCH);
21             use constant DEBUG => $ENV{PERL_DATETIME_CALENDAR_CHINESE_DEBUG};
22              
23             my %BasicValidate = (
24             cycle => {
25             default => 1,
26             },
27             cycle_year => {
28             default => 1,
29             callbacks => {
30             'is between 1 and 60' => sub { $_[0] >= 1 && $_[0] <= 60 }
31             }
32             },
33             month => {
34             default => 1,
35             callbacks => {
36             'is between 1 and 12' => sub { $_[0] >= 1 && $_[0] <= 12 }
37             }
38             },
39             leap_month => {
40             default => 0,
41             type => Params::Validate::BOOLEAN()
42             },
43             day => {
44             default => 1,
45             type => Params::Validate::SCALAR()
46             },
47             hour => {
48             type => Params::Validate::SCALAR(), default => 0,
49             callbacks => {
50             'is between 0 and 23' => sub { $_[0] >= 0 && $_[0] <= 23 },
51             },
52             },
53             minute => {
54             type => Params::Validate::SCALAR(), default => 0,
55             callbacks => {
56             'is between 0 and 59' => sub { $_[0] >= 0 && $_[0] <= 59 },
57             },
58             },
59             second => {
60             type => Params::Validate::SCALAR(), default => 0,
61             callbacks => {
62             'is between 0 and 61' => sub { $_[0] >= 0 && $_[0] <= 61 },
63             },
64             },
65             nanosecond => {
66             type => Params::Validate::SCALAR(), default => 0,
67             callbacks => {
68             'cannot be negative' => sub { $_[0] >= 0 },
69             }
70             },
71             locale => { type => Params::Validate::SCALAR() | Params::Validate::OBJECT(), optional => 1 },
72             language => { type => Params::Validate::SCALAR() | Params::Validate::OBJECT(), optional => 1 },
73             );
74              
75             my %NewValidate = (
76             %BasicValidate,
77             time_zone => { type => Params::Validate::SCALAR() | Params::Validate::OBJECT(), default => 'Asia/Shanghai' },
78             );
79             sub new
80             {
81             my $class = shift;
82             my %args = Params::Validate::validate(@_, \%NewValidate);
83              
84             # XXX - currently _calc_gregorian_components() calculates the
85             # date component only, then we set the time
86             my %hash;
87             $hash{cycle} = delete $args{cycle};
88             $hash{cycle_year} = delete $args{cycle_year};
89             $hash{month} = delete $args{month};
90             $hash{leap_month} = delete $args{leap_month};
91             $hash{day} = delete $args{day};
92              
93             my $self = bless \%hash, $class;
94             $self->_calc_gregorian_components(time_zone => delete $args{time_zone});
95             $self->{gregorian}->set(%args);
96              
97             $self;
98             }
99              
100             # XXX - these values are proxied directly to the underlying DateTime
101             # (Gregorian) object.
102             sub utc_rd_values { $_[0]->{gregorian}->utc_rd_values }
103             sub hour { $_[0]->{gregorian}->hour }
104             sub minute { $_[0]->{gregorian}->minute }
105             sub second { $_[0]->{gregorian}->second }
106             sub nanosecond { $_[0]->{gregorian}->nanosecond }
107             sub day_of_week { $_[0]->{gregorian}->day_of_week }
108             sub time_zone { $_[0]->{gregorian}->time_zone }
109             sub set_time_zone { shift->{gregorian}->set_time_zone(@_) }
110              
111             # XXX - accessors for DT::C::C specific fields
112             sub cycle { $_[0]->{cycle} }
113             sub cycle_year { $_[0]->{cycle_year} }
114             sub month { $_[0]->{month} }
115             sub leap_month { $_[0]->{leap_month} }
116             sub day { $_[0]->{day} }
117              
118             my @celestial_stems =
119             ( "甲",
120             "乙",
121             "丙",
122             "丁",
123             "戊",
124             "å·±",
125             "庚",
126             "辛",
127             "壬",
128             "癸",
129             );
130              
131             my @celestial_stems_py =
132             qw( jia3
133             yi3
134             bing3
135             ding1
136             wu4
137             ji3
138             geng1
139             xin1
140             ren2
141             gui3
142             );
143              
144             my @terrestrial_branches =
145             ( "子",
146             "丑",
147             "寅",
148             "卯",
149             "è¾°",
150             "å·³",
151             "午",
152             "未",
153             "申",
154             "酉",
155             "戌",
156             "亥",
157             );
158              
159             my @terrestrial_branches_py =
160             qw( zi
161             chou3
162             yin2
163             mao3
164             chen2
165             si4
166             wu3
167             wei4
168             shen1
169             you3
170             xu1
171             hai4
172             );
173              
174             my @zodiac_animals =
175             qw( rat
176             ox
177             tiger
178             hare
179             dragon
180             snake
181             horse
182             sheep
183             monkey
184             fowl
185             dog
186             pig
187             );
188              
189             sub celestial_stem { $celestial_stems[ ( $_[0]->cycle_year % 10 || 10) - 1 ] }
190             sub terrestrial_branch { $terrestrial_branches[ ( $_[0]->cycle_year % 12 || 12 ) - 1 ] }
191             sub year_name { $_[0]->celestial_stem . $_[0]->terrestrial_branch }
192              
193             sub celestial_stem_py { $celestial_stems_py[ ( $_[0]->cycle_year % 10 || 10 ) - 1 ] }
194             sub terrestrial_branch_py { $terrestrial_branches_py[ ( $_[0]->cycle_year % 12 || 12 ) - 1 ] }
195             sub year_name_py { $_[0]->celestial_stem_py . $_[0]->terrestrial_branch_py }
196              
197             sub zodiac_animal { $zodiac_animals[ ( $_[0]->cycle_year % 12 || 12 ) - 1 ] }
198              
199             my %SetValidate;
200             foreach my $key (keys %BasicValidate) {
201             my %hash = %{$BasicValidate{$key}};
202             delete $hash{default};
203             $hash{optional} = 1;
204             $SetValidate{$key} = \%hash;
205             }
206              
207             sub set
208             {
209             my $self = shift;
210             my %args = Params::Validate::validate(@_, \%SetValidate);
211              
212             #print STDERR
213             # "BEFORE SET ",
214             # "grgorian: ", $self->{gregorian}->datetime,
215             # " RD: ", ($self->{gregorian}->utc_rd_values)[0],
216             # " time_zone: ", $self->{gregorian}->time_zone_short_name, "\n";
217             foreach my $ch_component qw(cycle cycle_year month leap_month day) {
218             if (exists $args{$ch_component}) {
219             $self->{$ch_component} = delete $args{$ch_component};
220             }
221             }
222            
223             my $clone = $self->{gregorian}->clone;
224              
225             $self->_calc_gregorian_components(time_zone =>
226             $args{time_zone} || $clone->time_zone || 'UTC');
227              
228             # get "defaults" from the cloned dt object. we will only use these
229             # values if the field wasn't specified in the argument to set()
230             foreach my $dt_component qw(hour minute second locale) {
231             if (! exists $args{$dt_component}) {
232             $args{$dt_component} = $clone->$dt_component;
233             }
234             }
235             $self->{gregorian}->set(%args);
236              
237             #print STDERR
238             # "AFTER SET ",
239             # "grgorian: ", $self->{gregorian}->datetime,
240             # " RD: ", ($self->{gregorian}->utc_rd_values)[0],
241             # " time_zone: ", $self->{gregorian}->time_zone_short_name, "\n";
242              
243             $self;
244             }
245              
246             sub from_epoch
247             {
248             my $class = shift;
249             my $self = bless {}, $class;
250             my $dt = DateTime->from_epoch(@_);
251             $self->{gregorian} = $dt;
252             $self->_calc_local_components();
253             return $self;
254            
255             }
256             sub now { shift->from_epoch(@_, epoch => time()) }
257              
258             sub from_object
259             {
260             my $class = shift;
261             my $self = bless {}, $class;
262             my $dt = DateTime->from_object(@_);
263              
264             $self->{gregorian} = $dt;
265             $self->_calc_local_components();
266             return $self;
267             }
268              
269             sub _calc_gregorian_components
270             {
271             my $self = shift;
272              
273             my $mid_year = POSIX::floor(
274             GREGORIAN_CHINESE_EPOCH_MOMENT +
275             (($self->cycle() - 1) * 60 + $self->cycle_year() - 1 + 0.5) *
276             MEAN_TROPICAL_YEAR);
277             my $new_year = chinese_new_year_before(dt_from_moment($mid_year) );
278              
279             # XXX - I don't know why I need to do $self->month() - 2 here
280             my $p_dt = $new_year + DateTime::Duration->new(days => ($self->month() - 2) * 29);
281             my $p = new_moon_after( $p_dt );
282             my $d = DateTime::Calendar::Chinese->from_object(object => $p);
283              
284             my $prior_new_moon;
285             if ($d->month == $self->month && $d->leap_month == $self->leap_month) {
286             $prior_new_moon = $p;
287             } else {
288             $prior_new_moon = new_moon_after( $p + DateTime::Duration->new(days => 1) );
289             }
290              
291             my $tmp = $prior_new_moon + DateTime::Duration->new(days => $self->day - 1);
292             my %args = @_;
293             my %new_args = ();
294             foreach my $component qw(
295             year month day hour minute second nanosecond locale) {
296              
297             $new_args{$component} = $tmp->$component;
298             }
299             if ($args{time_zone}) {
300             $new_args{time_zone} = $args{time_zone};
301             } else {
302             $new_args{time_zone} = $tmp->time_zone;
303             }
304              
305             $self->{gregorian} = DateTime->new(%new_args);
306              
307             #print STDERR
308             # ">>>>>>>\n",
309             # " cycle: ", $self->cycle, "\n",
310             # " c_year: ", $self->cycle_year, "\n",
311             # "mid_year: ", dt_from_moment($mid_year)->datetime, "\n",
312             # "new_year: ", $new_year->datetime, "\n",
313             # " p: ", $p->datetime, "\n",
314             # " p_dt: ", $p_dt->datetime, "\n",
315             # "prior_nm: ", $prior_new_moon->datetime, "\n",
316             # " self: cycle: ", $self->cycle, " cycle_year: ", $self->cycle_year,
317             # " month: ", $self->month, " leap_month: ", $self->leap_month,
318             # " day: ", $self->day, "\n",
319             # " d: cycle: ", $d->cycle, " cycle_year: ", $d->cycle_year,
320             # " month: ", $d->month, " leap_month: ", $d->leap_month,
321             # " day: ", $d->day, "\n";
322             # "grgorian: ", $self->{gregorian}->datetime, "\n",
323             # "<<<<<<<\n";
324              
325             }
326              
327             sub _calc_local_components
328             {
329             my $self = shift;
330             my $dt = $self->{gregorian}->clone->truncate(to => 'day');
331              
332             # XXX TODO: Change these calculations to use moment, not DateTime
333              
334             # last winter solstice
335             my $s1 = prev_term_at( $dt, 270 );
336             # next winter solstice
337             my $s2 = prev_term_at( $s1 + DateTime::Duration->new(days => 370), 270 );
338              
339             # new moon after the last winter solstice (12th month in the last sui)
340             my $m12 = new_moon_after($s1 + DateTime::Duration->new(days => 1) );
341              
342             # new moon before the next winter solstice (11th month in the current sui)
343             my $next_m11 = new_moon_before($s2 + DateTime::Duration->new(days => 1));
344              
345             # new moon before now.
346             my $m = new_moon_before($dt + DateTime::Duration->new(days => 1));
347              
348             # pre-compute and save a call to moment()
349             my $m12_moment = moment($m12);
350             my $m_moment = moment($m);
351             my $m11_moment = moment($next_m11);
352              
353              
354             # if there are 12 lunar months (29.5 days) between the last 12th month
355             # and the next 11th month, then there must be a leap month some where
356             my $leap_year =
357             round(($m11_moment - $m12_moment) / MEAN_SYNODIC_MONTH) == 12;
358              
359             # XXX - hey, there are a lot of paranthesis, but it's required or
360             # else you get into some real unhappy problems
361             my $month;
362             {
363             my $x = round(($m_moment - $m12_moment) / MEAN_SYNODIC_MONTH);
364             if ($leap_year && $self->_prior_leap_month($m12, $m)) {
365             if (DEBUG) {
366             print STDERR ">>>> leap_year && prior_leap_mont $m12 : $m\n";
367             }
368             $x--;
369             }
370             $month = $x % 12 || 12;
371             }
372              
373             # XXX - tricky... we need to set month before calling elapsed_years,
374             # because it will be used by that function
375             $self->{month} = $month;
376             my $elapsed_years = $self->elapsed_years;
377              
378             $self->{cycle} = POSIX::floor( ($elapsed_years - 1) / 60) + 1;
379             $self->{cycle_year} = $elapsed_years % 60 || 60;
380             $self->{day} = POSIX::ceil(moment($dt) - $m_moment + 1);
381              
382             if ($leap_year && no_major_term_on($m)) {
383             my $end = new_moon_before($m - DateTime::Duration->new(days => 1));
384             $self->{leap_month} = ! $self->_prior_leap_month($m12, $end);
385             } else {
386             $self->{leap_month} = 0;
387             }
388              
389             if (DEBUG) {
390             print STDERR
391             ">>>>>>\n",
392             " dt: ", $dt->datetime, "\n",
393             " s1: ", $s1->datetime, "\n",
394             " s2: ", $s2->datetime, "\n",
395             " m12: ", $m12->datetime, "\n",
396             " n_m11: ", $next_m11->datetime, "\n",
397             " 11-12: ", round( (moment($next_m11) - $m12_moment) / MEAN_SYNODIC_MONTH), "\n",
398             " m: ", $m->datetime, "\n",
399             " leap year: ", $leap_year ? "yes" : "no", "\n",
400             "leap month: ", $self->{leap_month} ? "yes" : "no", "\n",
401             " m-m12: ", round(abs($m_moment - $m12_moment) / MEAN_SYNODIC_MONTH), "\n",
402             " sl_m: ", solar_longitude_from_moment($m_moment), "\n",
403             " sl_m12: ", solar_longitude_from_moment($m12_moment), "\n",
404              
405             # "pleap: ", $self->_prior_leap_month($m12, $m) ? "yes" : "no", "\n",
406             " month: ", $month, "\n",
407             " elapsed: ", $elapsed_years, "\n",
408             " cycle: ", $self->{cycle}, "\n",
409             "cycle_year: ", $self->{cycle_year}, "\n",
410             "<<<<<<\n";
411             }
412              
413             }
414              
415             sub elapsed_years
416             {
417             my $self = shift;
418             if (DEBUG) {
419             print STDERR
420             ">>>> elapsed_years\n",
421             "moment: ", moment($self->{gregorian}), "\n",
422             "epoch: ", GREGORIAN_CHINESE_EPOCH_MOMENT, "\n",
423             "month: ", $self->month, "\n",
424             "<<<<\n"
425             }
426             return POSIX::floor(
427             1.5 - $self->month / 12 + (moment($self->{gregorian}) - GREGORIAN_CHINESE_EPOCH_MOMENT) / MEAN_TROPICAL_YEAR);
428             }
429              
430             # [1] p.250
431             sub _prior_leap_month
432             {
433             my($class, $start, $end) = @_;
434              
435             if (DEBUG) {
436             print STDERR
437             ">>>> prior_leap_month\n",
438             "caller: ", join(':', (caller)[1, 2]), "\n",
439             "start: ", $start, "\n",
440             "end: ", $end, "\n",
441             "<<<<\n";
442             }
443              
444             while ($start <= $end) {
445             if (no_major_term_on($end)) {
446             if (DEBUG) {
447             print STDERR " + prior_leap_month: there are no major terms on $end\n";
448             }
449             return 1;
450             }
451              
452             $end = new_moon_before($end - DateTime::Duration->new(minutes => 30));
453             }
454             if (DEBUG) {
455             print " + prior_leap_month: nothing found, returning false\n";
456             }
457              
458             return ();
459             }
460              
461             1;
462             __END__
463              
464             =head1 NAME
465              
466             DateTime::Calendar::Chinese - Traditional Chinese Calendar Implementation
467              
468             =head1 SYNOPSIS
469              
470             use DateTime::Calendar::Chinese;
471              
472             my $dt = DateTime::Calendar::Chinese->now();
473             my $dt = DateTime::Calendar::Chinese->new(
474             cycle => $cycle,
475             cycle_year => $cycle_year,
476             month => $month,
477             leap_month => $leap_month,
478             day => $day,
479             );
480              
481             $dt->cycle;
482             $dt->cycle_year; # 1 - 60
483             $dt->month; # 1-12
484             $dt->leap_month; # true/false
485             $dt->day; # 1-30
486             $dt->elapsed_years; # years since "Chinese Epoch"
487              
488             my ($rd_days, $rd_secs, $rd_nanosecs) = $dt->utc_rd_values();
489              
490             =head1 DESCRIPTION
491              
492             This is an implementation of the Chinese calendar as described in
493             "Calendrical Calculations" [1]. Please note that the following description
494             is the description from [1], and the author has not made attempts to verify
495             the correctness of statements with other sources.
496              
497             The Chinese calendar described in [1] is expressed in terms of "cycle",
498             "cycle_year", "month", "a boolean leap_month", and "day".
499              
500             Traditional Chinese years have been counted using the "Sexagecimal Cycle
501             of Names", which is a cycle of 60 names for each year. The names are
502             the combination of a "celestial stem" (tian1 gan1), with a "terrestrial branch"
503             (di4 zhi1):
504              
505             Celestial Stems Terrestrial Branches
506             ------------------- -----------------------
507             | Jia3 | | Zi (Rat) |
508             ------------------- -----------------------
509             | Yi3 | | Chou3 (Ox) |
510             ------------------- -----------------------
511             | Bing3 | | Yin2 (Tiger) |
512             ------------------- -----------------------
513             | Ding1 | | Mao3 (Hare) |
514             ------------------- -----------------------
515             | Wu4 | | Chen2 (Dragon) |
516             ------------------- -----------------------
517             | Ji3 | | Si4 (Snake) |
518             ------------------- -----------------------
519             | Geng1 | | Wu3 (Horse) |
520             ------------------- -----------------------
521             | Xin1 | | Wei4 (Sheep) |
522             ------------------- -----------------------
523             | Ren2 | | Shen1 (Monkey) |
524             ------------------- -----------------------
525             | Gui3 | | You3 (Fowl) |
526             ------------------- -----------------------
527             | Xu1 (Dog) |
528             -----------------------
529             | Hai4 (Pig) |
530             -----------------------
531              
532             Names are assigned by running each list sequentially, so the first
533             year woud be jia1zi, then yi3chou3, bing1yin2, and so on. The numbers
534             after each syllable indicates the tone used for the syllable.
535              
536             The animal names of the Chinese "Zodiac" are I<not> translations of
537             the terrestrial branches, which have different meanings. For example,
538             the first branch, "zi", can mean "child" or "son", as well as several
539             other things.
540              
541             Chinese months are true lunar months, which starts on a new moon and runs
542             until the day before the next new moon. Therefore each month consists of
543             exactly 29 or 30 days. The month numbers are calculated based on a logic
544             that combines lunar months and solar terms (which is too hard to explain
545             here -- read "Calendrical Calculation" if you must know), and may include
546             leap months.
547              
548             Leap months can be inserted anywhere in the year, so months are numbered
549             from 1 to 12, with the boolean flag "leap_month" that indicates if the
550             month is a leap month or not.
551              
552             =head1 METHODS
553              
554             =head2 new
555              
556             This class method accepts parameters for each date and time component: "cycle",
557             "cycle_year", "month", "leap_month", "day", "hour", "minute", "second",
558             "nanosecond". It also accepts "locale" and "time_zone" parameters.
559              
560             Note that in order to avoid confusion between the official Chinese Calendar
561             which is based on Chinese time zone, the default value for time_zone is
562             *not* "floating", but is instead "Asia/Shanghai". See L<CAVEATS|/CAVEATS>.
563              
564             XXX The time zone settings may change in a few ture version such
565             XXX that the calculation is done in Asia/Shanghai, but the
566             XXX resulting object is set to "floating" time zone.
567              
568             Note that currently there's no way to verify if a given date is "correct" --
569             i.e. if you give a date as a leap_month when it in fact isn't a leap month,
570             all sorts of wacky things will happen. Perhaps there's a simple way to do
571             this. If there is, please let me know
572              
573             =head2 now
574              
575             This class method is equivalent to calling from_epoch() with the value
576             returned from Perl's time() function.
577              
578             =head2 from_object(object => ...)
579              
580             This class method can be used to construct a new DateTime::Calendar::Chinese
581             object from any object that implements the utc_rd_values() method.
582              
583             =head2 from_epoch(epoch => ...)
584              
585             This class method can be used to construct a new DateTime::Calendar::Chinese
586             object from an epoch time instead of components.
587              
588             =head2 set(...)
589              
590             This method is identical to that of DateTime, except the date components
591             that can be set are restricted to the Chinese ones ("cycle", "cycle_year",
592             "month", "leap_month", "day"). The time components are the same as
593             that of DateTime (See L<CAVEATS|/CAVEATS>).
594              
595             =head2 set_time_zone
596              
597             Sets the time zone. This method is identical to that of DateTime.
598              
599             =head2 time_zone
600              
601             Gets the time zone. This method is identical to that of DateTime.
602              
603             =head2 utc_rd_values()
604              
605             Returns the current UTC Rata Die days, seconds, and nanoseconds as a three
606             element list. This method is identical to that of L<DateTime>.
607              
608             =head2 cycle
609              
610             Returns the current cycle of the sexagecimal names since the Chinese epoch
611             (defined to be 25 Feb, -2636 gregorian).
612              
613             =head2 cycle_year
614              
615             Returns the current year in the current cycle.
616              
617             =head2 month
618              
619             Returns the current lunar month.
620              
621             =head2 leap_month
622              
623             Returns true if the current month is a leap month.
624              
625             =head2 day
626              
627             Returns the current day in the lunar month.
628              
629             =head2 elapsed_year
630              
631             This returns the number of years elapsed since the Chinese Epoch as defined
632             by [1] (Which is 15 Feb. -2646 gregorian). Some documents use different
633             epoch dates, and hence this may not match with whatever source you have.
634              
635             =head2 year_name
636              
637             Returns the name of the year (the celestial stem and the terrestrial
638             branch) as UTF8 (or a sequence of bytes in Perl 5.00503).
639              
640             =head2 celestial_stem
641              
642             Returns the celestial stem as UTF8 (or a sequence of bytes in Perl
643             5.00503).
644              
645             =head2 terrestrial_branch
646              
647             Returns the terrestrial branch as UTF8 (or a sequence of bytes in Perl
648             5.00503).
649              
650             =head2 year_name_py
651              
652             =head2 celestial_stem_py
653              
654             =head2 terrestrial_branch_py
655              
656             These methods return the various names in Pinyin, with the tones given
657             as numbers at the end of each syllable. The first terrestrial branch
658             is generally pronounced without a tone, and is returned as "zi".
659              
660             =head2 zodiac_animal
661              
662             Returns the year's Zodiac animal.
663              
664             =head2 day_of_week
665              
666             =head2 elapsed_years
667              
668             =head2 hour
669              
670             =head2 minute
671              
672             =head2 second
673              
674             =head2 nanosecond
675              
676             =head1 CAVEATS
677              
678             =head2 TIMEZONES
679              
680             Be careful with time zones! The "official" Chinese Calendar is based on
681             date/time in China, not your local time zone nor "floating" time zone.
682             This is because the Chinese Calendar is based on astronomical events,
683             but dates such as Chinese New Year are calculated in Chinese time and
684             then transferred over to wherever you're at.
685              
686             For example, the Chinese New Year in 2004 is Jan 22, but that is Jan 22
687             in China time. The same time is Jan 21 UTC, and now you'd be off by one day.
688              
689             So when you're calculating Chinese Calendars, always set the time zone to
690             something like 'Asia/Hong_Kong', 'Asia/Shanghai', 'Asia/Taipei'
691              
692             =head2 TIME
693              
694             Because "Calendrical Calculations" did not go much in detail about the
695             Chinese time system, this module simply uses the time components from the
696             underlying DateTime module (XXX - Note: we may implement this later, so
697             be careful not to use the time components too much for now)
698              
699             =head2 PERFORMANCE
700              
701             Yes, this module is slow, because the underlying calculations are slow.
702             If you can contribute to the speed, please let me know. So far I've
703             concentrated on porting the algorithms from [1] straight over, and not
704             on performance. I'm sure there's a lot that could be done.
705              
706             =head1 AUTHOR
707              
708             (c) Copyright 2004-2007 Daisuke Maki E<lt>daisuke@endeworks.jpE<gt>.
709              
710             =head1 LICENSE
711              
712             This program is free software; you can redistribute it and/or modify it
713             under the same terms as Perl itself.
714              
715             See http://www.perl.com/perl/misc/Artistic.html
716              
717             =head1 REFERENCES
718              
719             [1] Edward M. Reingold, Nachum Dershowitz
720             "Calendrical Calculations (Millenium Edition)", 2nd ed.
721             Cambridge University Press, Cambridge, UK 2002
722              
723             =head1 SEE ALSO
724              
725             L<DateTime>
726             L<DateTime::Event::Chinese>
727              
728             =cut
729