@@ -4572,3 +4572,308 @@ def _populate(self, year):
45724572
45734573class KE (Kenya ):
45744574 pass
4575+
4576+
4577+ class HongKong (HolidayBase ):
4578+
4579+ # https://www.gov.hk/en/about/abouthk/holiday/2020.htm
4580+ # https://en.wikipedia.org/wiki/Public_holidays_in_Hong_Kong
4581+
4582+ def __init__ (self , ** kwargs ):
4583+ self .country = "HK"
4584+ HolidayBase .__init__ (self , ** kwargs )
4585+
4586+ def _populate (self , year ):
4587+
4588+ day_following = "The day following "
4589+
4590+ # The first day of January
4591+ name = "The first day of January"
4592+ first_date = date (year , JAN , 1 )
4593+ if self .observed :
4594+ if first_date .weekday () == SUN :
4595+ self [first_date + rd (days = + 1 )] = day_following + \
4596+ self .first_lower (name )
4597+ first_date = first_date + rd (days = + 1 )
4598+ else :
4599+ self [first_date ] = name
4600+ else :
4601+ self [first_date ] = name
4602+
4603+ # Lunar New Year
4604+ name = "Lunar New Year's Day"
4605+ preceding_day_lunar = "The day preceding Lunar New Year's Day"
4606+ second_day_lunar = "The second day of Lunar New Year"
4607+ third_day_lunar = "The third day of Lunar New Year"
4608+ fourth_day_lunar = "The fourth day of Lunar New Year"
4609+ dt = self .get_solar_date (year , 1 , 1 )
4610+ new_year_date = date (dt .year , dt .month , dt .day )
4611+ if self .observed :
4612+ self [new_year_date ] = name
4613+ if new_year_date .weekday () in [MON , TUE , WED , THU ]:
4614+ self [new_year_date ] = name
4615+ self [new_year_date + rd (days = + 1 )] = second_day_lunar
4616+ self [new_year_date + rd (days = + 2 )] = third_day_lunar
4617+ elif new_year_date .weekday () == FRI :
4618+ self [new_year_date ] = name
4619+ self [new_year_date + rd (days = + 1 )] = second_day_lunar
4620+ self [new_year_date + rd (days = + 3 )] = fourth_day_lunar
4621+ elif new_year_date .weekday () == SAT :
4622+ self [new_year_date ] = name
4623+ self [new_year_date + rd (days = + 2 )] = third_day_lunar
4624+ self [new_year_date + rd (days = + 3 )] = fourth_day_lunar
4625+ elif new_year_date .weekday () == SUN :
4626+ if year in [2006 , 2007 , 2010 ]:
4627+ self [new_year_date + rd (days = - 1 )] = preceding_day_lunar
4628+ self [new_year_date + rd (days = + 1 )] = second_day_lunar
4629+ self [new_year_date + rd (days = + 2 )] = third_day_lunar
4630+ else :
4631+ self [new_year_date + rd (days = + 1 )] = second_day_lunar
4632+ self [new_year_date + rd (days = + 2 )] = third_day_lunar
4633+ self [new_year_date + rd (days = + 3 )] = fourth_day_lunar
4634+ else :
4635+ self [new_year_date ] = name
4636+ self [new_year_date + rd (days = + 1 )] = second_day_lunar
4637+ self [new_year_date + rd (days = + 2 )] = third_day_lunar
4638+
4639+ # Ching Ming Festival
4640+ name = "Ching Ming Festival"
4641+ if self .isLeapYear (year ) or (self .isLeapYear (year - 1 ) and year > 2008 ):
4642+ ching_ming_date = date (year , APR , 4 )
4643+ else :
4644+ ching_ming_date = date (year , APR , 5 )
4645+ if self .observed :
4646+ if ching_ming_date .weekday () == SUN :
4647+ self [ching_ming_date + rd (days = + 1 )] = day_following + name
4648+ ching_ming_date = ching_ming_date + rd (days = + 1 )
4649+ else :
4650+ self [ching_ming_date ] = name
4651+ else :
4652+ self [ching_ming_date ] = name
4653+
4654+ # Easter Holiday
4655+ good_friday = "Good Friday"
4656+ easter_monday = "Easter Monday"
4657+ if self .observed :
4658+ self [easter (year ) + rd (weekday = FR (- 1 ))] = good_friday
4659+ self [easter (year ) + rd (weekday = SA (- 1 ))] = day_following + \
4660+ good_friday
4661+ if ching_ming_date == easter (year ) + rd (weekday = MO ):
4662+ self [easter (year ) + rd (weekday = MO ) + rd (days = + 1 )] = \
4663+ day_following + easter_monday
4664+ else :
4665+ self [easter (year ) + rd (weekday = MO )] = easter_monday
4666+ else :
4667+ self [easter (year ) + rd (weekday = FR (- 1 ))] = good_friday
4668+ self [easter (year ) + rd (weekday = SA (- 1 ))] = day_following + \
4669+ good_friday
4670+ self [easter (year ) + rd (weekday = MO )] = easter_monday
4671+
4672+ # Birthday of the Buddha
4673+ name = "Birthday of the Buddha"
4674+ dt = self .get_solar_date (year , 4 , 8 )
4675+ buddha_date = date (dt .year , dt .month , dt .day )
4676+ if self .observed :
4677+ if buddha_date .weekday () == SUN :
4678+ self [buddha_date + rd (days = + 1 )] = day_following + name
4679+ else :
4680+ self [buddha_date ] = name
4681+ else :
4682+ self [buddha_date ] = name
4683+
4684+ # Labour Day
4685+ name = "Labour Day"
4686+ labour_date = date (year , MAY , 1 )
4687+ if self .observed :
4688+ if labour_date .weekday () == SUN :
4689+ self [labour_date + rd (days = + 1 )] = day_following + name
4690+ else :
4691+ self [labour_date ] = name
4692+ else :
4693+ self [labour_date ] = name
4694+
4695+ # Tuen Ng Festival
4696+ name = "Tuen Ng Festival"
4697+ dt = self .get_solar_date (year , 5 , 5 )
4698+ tuen_ng_date = date (dt .year , dt .month , dt .day )
4699+ if self .observed :
4700+ if tuen_ng_date .weekday () == SUN :
4701+ self [tuen_ng_date + rd (days = + 1 )] = day_following + name
4702+ else :
4703+ self [tuen_ng_date ] = name
4704+ else :
4705+ self [tuen_ng_date ] = name
4706+
4707+ # Hong Kong Special Administrative Region Establishment Day
4708+ name = "Hong Kong Special Administrative Region Establishment Day"
4709+ hksar_date = date (year , JUL , 1 )
4710+ if self .observed :
4711+ if hksar_date .weekday () == SUN :
4712+ self [hksar_date + rd (days = + 1 )] = day_following + name
4713+ else :
4714+ self [hksar_date ] = name
4715+ else :
4716+ self [hksar_date ] = name
4717+
4718+ # Special holiday on 2015 - The 70th anniversary day of the victory
4719+ # of the Chinese people's war of resistance against Japanese aggression
4720+ name = "The 70th anniversary day of the victory of the Chinese " + \
4721+ "people's war of resistance against Japanese aggression"
4722+ if year == 2015 :
4723+ self [date (year , SEP , 3 )] = name
4724+
4725+ # Chinese Mid-Autumn Festival
4726+ name = "Chinese Mid-Autumn Festival"
4727+ dt = self .get_solar_date (year , 8 , 15 )
4728+ mid_autumn_date = date (dt .year , dt .month , dt .day )
4729+ if self .observed :
4730+ if mid_autumn_date .weekday () == SAT :
4731+ self [mid_autumn_date ] = name
4732+ else :
4733+ self [mid_autumn_date + rd (days = + 1 )] = day_following + \
4734+ "the " + name
4735+ mid_autumn_date = mid_autumn_date + rd (days = + 1 )
4736+ else :
4737+ self [mid_autumn_date ] = name
4738+
4739+ # National Day
4740+ name = "National Day"
4741+ national_date = date (year , OCT , 1 )
4742+ if self .observed :
4743+ if (national_date .weekday () == SUN or
4744+ national_date == mid_autumn_date ):
4745+ self [national_date + rd (days = + 1 )] = day_following + name
4746+ else :
4747+ self [national_date ] = name
4748+ else :
4749+ self [national_date ] = name
4750+
4751+ # Chung Yeung Festival
4752+ name = "Chung Yeung Festival"
4753+ dt = self .get_solar_date (year , 9 , 9 )
4754+ chung_yeung_date = date (dt .year , dt .month , dt .day )
4755+ if self .observed :
4756+ if chung_yeung_date .weekday () == SUN :
4757+ self [chung_yeung_date + rd (days = + 1 )] = day_following + name
4758+ else :
4759+ self [chung_yeung_date ] = name
4760+ else :
4761+ self [chung_yeung_date ] = name
4762+
4763+ # Christmas Day
4764+ name = "Christmas Day"
4765+ first_after_christmas = "The first weekday after " + name
4766+ second_after_christmas = "The second weekday after " + name
4767+ christmas_date = date (year , DEC , 25 )
4768+ if self .observed :
4769+ if christmas_date .weekday () == SUN :
4770+ self [christmas_date ] = name
4771+ self [christmas_date + rd (days = + 1 )] = first_after_christmas
4772+ self [christmas_date + rd (days = + 2 )] = second_after_christmas
4773+ elif christmas_date .weekday () == SAT :
4774+ self [christmas_date ] = name
4775+ self [christmas_date + rd (days = + 2 )] = first_after_christmas
4776+ else :
4777+ self [christmas_date ] = name
4778+ self [christmas_date + rd (days = + 1 )] = first_after_christmas
4779+ else :
4780+ self [christmas_date ] = name
4781+ self [christmas_date + rd (days = + 1 )] = day_following + name
4782+
4783+ def isLeapYear (self , year ):
4784+ if year % 4 != 0 :
4785+ return False
4786+ elif year % 100 != 0 :
4787+ return True
4788+ elif year % 400 != 0 :
4789+ return False
4790+ else :
4791+ return True
4792+
4793+ def first_lower (self , s ):
4794+ return s [0 ].lower () + s [1 :]
4795+
4796+ # Store the number of days per year from 1901 to 2099, and the number of
4797+ # days from the 1st to the 13th to store the monthly (including the month
4798+ # of the month), 1 means that the month is 30 days. 0 means the month is
4799+ # 29 days. The 12th to 15th digits indicate the month of the next month.
4800+ # If it is 0x0F, it means that there is no leap month.
4801+ g_lunar_month_days = [
4802+ 0xF0EA4 , 0xF1D4A , 0x52C94 , 0xF0C96 , 0xF1536 ,
4803+ 0x42AAC , 0xF0AD4 , 0xF16B2 , 0x22EA4 , 0xF0EA4 , # 1901-1910
4804+ 0x6364A , 0xF164A , 0xF1496 , 0x52956 , 0xF055A ,
4805+ 0xF0AD6 , 0x216D2 , 0xF1B52 , 0x73B24 , 0xF1D24 , # 1911-1920
4806+ 0xF1A4A , 0x5349A , 0xF14AC , 0xF056C , 0x42B6A ,
4807+ 0xF0DA8 , 0xF1D52 , 0x23D24 , 0xF1D24 , 0x61A4C , # 1921-1930
4808+ 0xF0A56 , 0xF14AE , 0x5256C , 0xF16B4 , 0xF0DA8 ,
4809+ 0x31D92 , 0xF0E92 , 0x72D26 , 0xF1526 , 0xF0A56 , # 1931-1940
4810+ 0x614B6 , 0xF155A , 0xF0AD4 , 0x436AA , 0xF1748 ,
4811+ 0xF1692 , 0x23526 , 0xF152A , 0x72A5A , 0xF0A6C , # 1941-1950
4812+ 0xF155A , 0x52B54 , 0xF0B64 , 0xF1B4A , 0x33A94 ,
4813+ 0xF1A94 , 0x8152A , 0xF152E , 0xF0AAC , 0x6156A , # 1951-1960
4814+ 0xF15AA , 0xF0DA4 , 0x41D4A , 0xF1D4A , 0xF0C94 ,
4815+ 0x3192E , 0xF1536 , 0x72AB4 , 0xF0AD4 , 0xF16D2 , # 1961-1970
4816+ 0x52EA4 , 0xF16A4 , 0xF164A , 0x42C96 , 0xF1496 ,
4817+ 0x82956 , 0xF055A , 0xF0ADA , 0x616D2 , 0xF1B52 , # 1971-1980
4818+ 0xF1B24 , 0x43A4A , 0xF1A4A , 0xA349A , 0xF14AC ,
4819+ 0xF056C , 0x60B6A , 0xF0DAA , 0xF1D92 , 0x53D24 , # 1981-1990
4820+ 0xF1D24 , 0xF1A4C , 0x314AC , 0xF14AE , 0x829AC ,
4821+ 0xF06B4 , 0xF0DAA , 0x52D92 , 0xF0E92 , 0xF0D26 , # 1991-2000
4822+ 0x42A56 , 0xF0A56 , 0xF14B6 , 0x22AB4 , 0xF0AD4 ,
4823+ 0x736AA , 0xF1748 , 0xF1692 , 0x53526 , 0xF152A , # 2001-2010
4824+ 0xF0A5A , 0x4155A , 0xF156A , 0x92B54 , 0xF0BA4 ,
4825+ 0xF1B4A , 0x63A94 , 0xF1A94 , 0xF192A , 0x42A5C , # 2011-2020
4826+ 0xF0AAC , 0xF156A , 0x22B64 , 0xF0DA4 , 0x61D52 ,
4827+ 0xF0E4A , 0xF0C96 , 0x5192E , 0xF1956 , 0xF0AB4 , # 2021-2030
4828+ 0x315AC , 0xF16D2 , 0xB2EA4 , 0xF16A4 , 0xF164A ,
4829+ 0x63496 , 0xF1496 , 0xF0956 , 0x50AB6 , 0xF0B5A , # 2031-2040
4830+ 0xF16D4 , 0x236A4 , 0xF1B24 , 0x73A4A , 0xF1A4A ,
4831+ 0xF14AA , 0x5295A , 0xF096C , 0xF0B6A , 0x31B54 , # 2041-2050
4832+ 0xF1D92 , 0x83D24 , 0xF1D24 , 0xF1A4C , 0x614AC ,
4833+ 0xF14AE , 0xF09AC , 0x40DAA , 0xF0EAA , 0xF0E92 , # 2051-2060
4834+ 0x31D26 , 0xF0D26 , 0x72A56 , 0xF0A56 , 0xF14B6 ,
4835+ 0x52AB4 , 0xF0AD4 , 0xF16CA , 0x42E94 , 0xF1694 , # 2061-2070
4836+ 0x8352A , 0xF152A , 0xF0A5A , 0x6155A , 0xF156A ,
4837+ 0xF0B54 , 0x4174A , 0xF1B4A , 0xF1A94 , 0x3392A , # 2071-2080
4838+ 0xF192C , 0x7329C , 0xF0AAC , 0xF156A , 0x52B64 ,
4839+ 0xF0DA4 , 0xF1D4A , 0x41C94 , 0xF0C96 , 0x8192E , # 2081-2090
4840+ 0xF0956 , 0xF0AB6 , 0x615AC , 0xF16D4 , 0xF0EA4 ,
4841+ 0x42E4A , 0xF164A , 0xF1516 , 0x22936 , # 2090-2099
4842+ ]
4843+ # Define range of years
4844+ START_YEAR , END_YEAR = 1901 , 1900 + len (g_lunar_month_days )
4845+ # 1901 The 1st day of the 1st month of the Gregorian calendar is 1901/2/19
4846+ LUNAR_START_DATE , SOLAR_START_DATE = (1901 , 1 , 1 ), datetime (1901 , 2 , 19 )
4847+ # The Gregorian date for December 30, 2099 is 2100/2/8
4848+ LUNAR_END_DATE , SOLAR_END_DATE = (2099 , 12 , 30 ), datetime (2100 , 2 , 18 )
4849+
4850+ def get_leap_month (self , lunar_year ):
4851+ return (self .g_lunar_month_days [lunar_year - self .START_YEAR ] >> 16 ) \
4852+ & 0x0F
4853+
4854+ def lunar_month_days (self , lunar_year , lunar_month ):
4855+ return 29 + ((self .g_lunar_month_days [lunar_year - self .START_YEAR ] >>
4856+ lunar_month ) & 0x01 )
4857+
4858+ def lunar_year_days (self , year ):
4859+ days = 0
4860+ months_day = self .g_lunar_month_days [year - self .START_YEAR ]
4861+ for i in range (1 , 13 if self .get_leap_month (year ) == 0x0F else 14 ):
4862+ day = 29 + ((months_day >> i ) & 0x01 )
4863+ days += day
4864+ return days
4865+
4866+ # Calculate the Gregorian date according to the lunar calendar
4867+ def get_solar_date (self , year , month , day ):
4868+ span_days = 0
4869+ for y in range (self .START_YEAR , year ):
4870+ span_days += self .lunar_year_days (y )
4871+ leap_month = self .get_leap_month (year )
4872+ for m in range (1 , month + (month > leap_month )):
4873+ span_days += self .lunar_month_days (year , m )
4874+ span_days += day - 1
4875+ return self .SOLAR_START_DATE + timedelta (span_days )
4876+
4877+
4878+ class HK (HongKong ):
4879+ pass
0 commit comments