| Total Complexity | 178 |
| Total Lines | 1153 |
| Duplicated Lines | 0 % |
| Changes | 3 | ||
| Bugs | 0 | Features | 0 |
Complex classes like Calendar often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Calendar, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 19 | class Calendar |
||
| 20 | { |
||
| 21 | /** |
||
| 22 | * 农历 1900-2100 的润大小信息. |
||
| 23 | * |
||
| 24 | * @var array |
||
| 25 | */ |
||
| 26 | protected $lunars = [ |
||
| 27 | 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909 |
||
| 28 | 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919 |
||
| 29 | 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929 |
||
| 30 | 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939 |
||
| 31 | 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949 |
||
| 32 | 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959 |
||
| 33 | 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969 |
||
| 34 | 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979 |
||
| 35 | 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989 |
||
| 36 | 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999 |
||
| 37 | 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009 |
||
| 38 | 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019 |
||
| 39 | 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029 |
||
| 40 | 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039 |
||
| 41 | 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049 |
||
| 42 | 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059 |
||
| 43 | 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069 |
||
| 44 | 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079 |
||
| 45 | 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089 |
||
| 46 | 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099 |
||
| 47 | 0x0d520, // 2100 |
||
| 48 | ]; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * 公历每个月份的天数表. |
||
| 52 | * |
||
| 53 | * @var array |
||
| 54 | */ |
||
| 55 | protected $solarMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * 天干地支之天干速查表. |
||
| 59 | * |
||
| 60 | * @var array |
||
| 61 | */ |
||
| 62 | protected $gan = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; |
||
| 63 | |||
| 64 | /** |
||
| 65 | * 天干地支之天干速查表 <=> 色彩. |
||
| 66 | * |
||
| 67 | * @var array |
||
| 68 | */ |
||
| 69 | protected $colors = ['青', '青', '红', '红', '黄', '黄', '白', '白', '黑', '黑']; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * 天干地支之天干速查表 <=> 五行. |
||
| 73 | * |
||
| 74 | * @var array |
||
| 75 | */ |
||
| 76 | protected $wuXing = ['木', '木', '火', '火', '土', '土', '金', '金', '水', '水']; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * 地支 <=> 五行. |
||
| 80 | * |
||
| 81 | * @var array |
||
| 82 | */ |
||
| 83 | protected $zhiWuxing = ['水', '土', '木', '木', '土', '火', '火', '土', '金', '金', '土', '水']; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * 天干地支之地支速查表. |
||
| 87 | * |
||
| 88 | * @var array |
||
| 89 | */ |
||
| 90 | protected $zhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; |
||
| 91 | |||
| 92 | /** |
||
| 93 | * 天干地支之地支速查表 <=> 生肖. |
||
| 94 | * |
||
| 95 | * @var array |
||
| 96 | */ |
||
| 97 | protected $animals = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']; |
||
| 98 | |||
| 99 | /** |
||
| 100 | * 24节气速查表. |
||
| 101 | * |
||
| 102 | * @var array |
||
| 103 | */ |
||
| 104 | protected $solarTerm = [ |
||
| 105 | '小寒', '大寒', '立春', '雨水', '惊蛰', '春分', |
||
| 106 | '清明', '谷雨', '立夏', '小满', '芒种', '夏至', |
||
| 107 | '小暑', '大暑', '立秋', '处暑', '白露', '秋分', |
||
| 108 | '寒露', '霜降', '立冬', '小雪', '大雪', '冬至', |
||
| 109 | ]; |
||
| 110 | |||
| 111 | /** |
||
| 112 | * 1900-2100 各年的 24 节气日期速查表. |
||
| 113 | * |
||
| 114 | * @var array |
||
| 115 | */ |
||
| 116 | protected $solarTerms = [ |
||
| 117 | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', |
||
| 118 | '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||
| 119 | '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', |
||
| 120 | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', |
||
| 121 | 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', |
||
| 122 | '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', |
||
| 123 | '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', |
||
| 124 | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', |
||
| 125 | '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||
| 126 | '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 127 | '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', |
||
| 128 | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', |
||
| 129 | '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||
| 130 | '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 131 | '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', |
||
| 132 | '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', |
||
| 133 | '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
||
| 134 | '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
||
| 135 | '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', |
||
| 136 | '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||
| 137 | '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 138 | '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||
| 139 | '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', |
||
| 140 | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||
| 141 | '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 142 | '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||
| 143 | '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', |
||
| 144 | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
||
| 145 | '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
||
| 146 | '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
||
| 147 | '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
||
| 148 | '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 149 | '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||
| 150 | '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||
| 151 | '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
||
| 152 | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||
| 153 | '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||
| 154 | '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||
| 155 | '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', |
||
| 156 | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', |
||
| 157 | '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
||
| 158 | '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||
| 159 | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', |
||
| 160 | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||
| 161 | '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||
| 162 | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||
| 163 | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', |
||
| 164 | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||
| 165 | '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||
| 166 | '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', |
||
| 167 | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', |
||
| 168 | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', |
||
| 169 | '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||
| 170 | '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
||
| 171 | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', |
||
| 172 | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||
| 173 | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', |
||
| 174 | '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', |
||
| 175 | '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', |
||
| 176 | '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||
| 177 | '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', |
||
| 178 | '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', |
||
| 179 | '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', |
||
| 180 | '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||
| 181 | '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
||
| 182 | '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', |
||
| 183 | '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||
| 184 | ]; |
||
| 185 | |||
| 186 | /** |
||
| 187 | * 数字转中文速查表. |
||
| 188 | * |
||
| 189 | * @var array |
||
| 190 | */ |
||
| 191 | protected $weekdayAlias = ['日', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']; |
||
| 192 | |||
| 193 | /** |
||
| 194 | * 日期转农历称呼速查表. |
||
| 195 | * |
||
| 196 | * @var array |
||
| 197 | */ |
||
| 198 | protected $dateAlias = ['初', '十', '廿', '卅']; |
||
| 199 | |||
| 200 | /** |
||
| 201 | * 月份转农历称呼速查表. |
||
| 202 | * |
||
| 203 | * @var array |
||
| 204 | */ |
||
| 205 | protected $monthAlias = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊']; |
||
| 206 | |||
| 207 | /** |
||
| 208 | * 传入阳历年月日获得详细的公历、农历信息. |
||
| 209 | * |
||
| 210 | * @param int $year |
||
| 211 | * @param int $month |
||
| 212 | * @param int $day |
||
| 213 | * @param int $hour |
||
| 214 | * |
||
| 215 | * @return array |
||
| 216 | */ |
||
| 217 | public function solar($year, $month, $day, $hour = null) |
||
| 218 | { |
||
| 219 | $date = $this->makeDate("{$year}-{$month}-{$day}"); |
||
| 220 | $lunar = $this->solar2lunar($year, $month, $day, $hour); |
||
| 221 | $week = abs((int)$date->format('w')); // 0 ~ 6 修正 星期七 为 星期日 |
||
| 222 | |||
| 223 | return array_merge( |
||
| 224 | $lunar, |
||
| 225 | [ |
||
| 226 | 'gregorian_year' => (string) $year, |
||
| 227 | 'gregorian_month' => sprintf('%02d', $month), |
||
| 228 | 'gregorian_day' => sprintf('%02d', $day), |
||
| 229 | 'gregorian_hour' => !is_numeric($hour) || $hour < 0 || $hour > 23 ? null : sprintf('%02d', $hour), |
||
| 230 | 'week_no' => $week, // 在周日时将会传回 0 |
||
| 231 | 'week_name' => '星期'.$this->weekdayAlias[$week], |
||
| 232 | 'is_today' => 0 === $this->makeDate('now')->diff($date)->days, |
||
| 233 | 'constellation' => $this->toConstellation($month, $day), |
||
| 234 | 'is_same_year' => $lunar['lunar_year'] == $year ?: false, |
||
| 235 | ] |
||
| 236 | ); |
||
| 237 | } |
||
| 238 | |||
| 239 | /** |
||
| 240 | * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历信息. |
||
| 241 | * |
||
| 242 | * @param int $year lunar year |
||
| 243 | * @param int $month lunar month |
||
| 244 | * @param int $day lunar day |
||
| 245 | * @param bool $isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] |
||
| 246 | * @param int $hour birth hour.[0~23] |
||
| 247 | * |
||
| 248 | * @return array |
||
| 249 | */ |
||
| 250 | public function lunar($year, $month, $day, $isLeapMonth = false, $hour = null) |
||
| 251 | { |
||
| 252 | $solar = $this->lunar2solar($year, $month, $day, $isLeapMonth); |
||
| 253 | |||
| 254 | return $this->solar($solar['solar_year'], $solar['solar_month'], $solar['solar_day'], $hour); |
||
| 255 | } |
||
| 256 | |||
| 257 | /** |
||
| 258 | * 返回农历指定年的总天数. |
||
| 259 | * |
||
| 260 | * @param int $year |
||
| 261 | * |
||
| 262 | * @return int |
||
| 263 | */ |
||
| 264 | public function daysOfYear($year) |
||
| 265 | { |
||
| 266 | $sum = 348; |
||
| 267 | |||
| 268 | for ($i = 0x8000; $i > 0x8; $i >>= 1) { |
||
| 269 | $sum += ($this->lunars[$year - 1900] & $i) ? 1 : 0; |
||
| 270 | } |
||
| 271 | |||
| 272 | return $sum + $this->leapDays($year); |
||
| 273 | } |
||
| 274 | |||
| 275 | /** |
||
| 276 | * 返回农历指定年的总月数. |
||
| 277 | * |
||
| 278 | * @param int $year |
||
| 279 | * |
||
| 280 | * @return int |
||
| 281 | */ |
||
| 282 | public function monthsOfYear($year) |
||
| 283 | { |
||
| 284 | return 0 < $this->leapMonth($year) ? 13 : 12; |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * 返回农历 y 年闰月是哪个月;若 y 年没有闰月 则返回0. |
||
| 289 | * |
||
| 290 | * @param int $year |
||
| 291 | * |
||
| 292 | * @return int |
||
| 293 | */ |
||
| 294 | public function leapMonth($year) |
||
| 295 | { |
||
| 296 | // 闰字编码 \u95f0 |
||
| 297 | return $this->lunars[$year - 1900] & 0xf; |
||
| 298 | } |
||
| 299 | |||
| 300 | /** |
||
| 301 | * 返回农历y年闰月的天数 若该年没有闰月则返回 0. |
||
| 302 | * |
||
| 303 | * @param int $year |
||
| 304 | * |
||
| 305 | * @return int |
||
| 306 | */ |
||
| 307 | public function leapDays($year) |
||
| 308 | { |
||
| 309 | if ($this->leapMonth($year)) { |
||
| 310 | return ($this->lunars[$year - 1900] & 0x10000) ? 30 : 29; |
||
| 311 | } |
||
| 312 | |||
| 313 | return 0; |
||
| 314 | } |
||
| 315 | |||
| 316 | /** |
||
| 317 | * 返回农历 y 年 m 月(非闰月)的总天数,计算 m 为闰月时的天数请使用 leapDays 方法. |
||
| 318 | * |
||
| 319 | * @param int $year |
||
| 320 | * @param int $month |
||
| 321 | * |
||
| 322 | * @return int |
||
| 323 | */ |
||
| 324 | public function lunarDays($year, $month) |
||
| 325 | { |
||
| 326 | // 月份参数从 1 至 12,参数错误返回 -1 |
||
| 327 | if ($month > 12 || $month < 1) { |
||
| 328 | return -1; |
||
| 329 | } |
||
| 330 | |||
| 331 | return ($this->lunars[$year - 1900] & (0x10000 >> $month)) ? 30 : 29; |
||
| 332 | } |
||
| 333 | |||
| 334 | /** |
||
| 335 | * 返回公历 y 年 m 月的天数. |
||
| 336 | * |
||
| 337 | * @param int $year |
||
| 338 | * @param int $month |
||
| 339 | * |
||
| 340 | * @return int |
||
| 341 | */ |
||
| 342 | public function solarDays($year, $month) |
||
| 356 | } |
||
| 357 | |||
| 358 | /** |
||
| 359 | * 农历年份转换为干支纪年. |
||
| 360 | * |
||
| 361 | * @param int $lunarYear |
||
| 362 | * @param null|int $termIndex |
||
| 363 | * |
||
| 364 | * @return string |
||
| 365 | */ |
||
| 366 | public function ganZhiYear($lunarYear, $termIndex = null) |
||
| 367 | { |
||
| 368 | /** |
||
| 369 | * 据维基百科干支词条:『在西历新年后,华夏新年或干支历新年之前,则续用上一年之干支』 |
||
| 370 | * 所以干支年份应该不需要根据节气校正,为免影响现有系统,此处暂时保留原有逻辑 |
||
| 371 | * https://zh.wikipedia.org/wiki/%E5%B9%B2%E6%94%AF. |
||
| 372 | * |
||
| 373 | * 即使考虑节气,有的年份没有立春,有的年份有两个立春,此处逻辑仍不能处理该特殊情况 |
||
| 374 | */ |
||
| 375 | $adjust = null !== $termIndex && 3 > $termIndex ? 1 : 0; |
||
| 376 | |||
| 377 | $ganKey = ($lunarYear + $adjust - 4) % 10; |
||
| 378 | $zhiKey = ($lunarYear + $adjust - 4) % 12; |
||
| 379 | |||
| 380 | return $this->gan[$ganKey].$this->zhi[$zhiKey]; |
||
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * 公历月、日判断所属星座. |
||
| 385 | * |
||
| 386 | * @param int $gregorianMonth |
||
| 387 | * @param int $gregorianDay |
||
| 388 | * |
||
| 389 | * @return string |
||
| 390 | */ |
||
| 391 | public function toConstellation($gregorianMonth, $gregorianDay) |
||
| 392 | { |
||
| 393 | $constellations = '魔羯水瓶双鱼白羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯'; |
||
| 394 | $arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]; |
||
| 395 | |||
| 396 | return mb_substr( |
||
| 397 | $constellations, |
||
| 398 | $gregorianMonth * 2 - ($gregorianDay < $arr[$gregorianMonth - 1] ? 2 : 0), |
||
| 399 | 2, |
||
| 400 | 'UTF-8' |
||
| 401 | ); |
||
| 402 | } |
||
| 403 | |||
| 404 | /** |
||
| 405 | * 传入offset偏移量返回干支. |
||
| 406 | * |
||
| 407 | * @param int $offset 相对甲子的偏移量 |
||
| 408 | * |
||
| 409 | * @return string |
||
| 410 | */ |
||
| 411 | public function toGanZhi($offset) |
||
| 412 | { |
||
| 413 | return $this->gan[$offset % 10].$this->zhi[$offset % 12]; |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * 传入公历年获得该年第n个节气的公历日期 |
||
| 418 | * |
||
| 419 | * @param int $year 公历年(1900-2100); |
||
| 420 | * @param int $no 二十四节气中的第几个节气(1~24);从n=1(小寒)算起 |
||
| 421 | * |
||
| 422 | * @return int |
||
| 423 | * |
||
| 424 | * @example |
||
| 425 | * <pre> |
||
| 426 | * $_24 = $this->getTerm(1987,3) ;// _24 = 4; 意即 1987 年 2 月 4 日立春 |
||
| 427 | * </pre> |
||
| 428 | */ |
||
| 429 | public function getTerm($year, $no) |
||
| 430 | { |
||
| 431 | if ($year < 1900 || $year > 2100) { |
||
| 432 | return -1; |
||
| 433 | } |
||
| 434 | if ($no < 1 || $no > 24) { |
||
| 435 | return -1; |
||
| 436 | } |
||
| 437 | $solarTermsOfYear = array_map('hexdec', (array)str_split($this->solarTerms[$year - 1900], 5)); |
||
| 438 | $positions = [ |
||
| 439 | 0 => [0, 1], |
||
| 440 | 1 => [1, 2], |
||
| 441 | 2 => [3, 1], |
||
| 442 | 3 => [4, 2], |
||
| 443 | ]; |
||
| 444 | $group = intval(($no - 1) / 4); |
||
| 445 | list($offset, $length) = $positions[($no - 1) % 4]; |
||
| 446 | |||
| 447 | return substr($solarTermsOfYear[$group], $offset, $length); |
||
| 448 | } |
||
| 449 | |||
| 450 | public function toChinaYear($year) |
||
| 451 | { |
||
| 452 | if (!is_numeric($year)) { |
||
| 453 | throw new InvalidArgumentException("错误的年份:{$year}"); |
||
| 454 | } |
||
| 455 | $lunarYear = ''; |
||
| 456 | $year = (string) $year; |
||
| 457 | for ($i = 0, $l = strlen($year); $i < $l; ++$i) { |
||
| 458 | $lunarYear .= '0' !== $year[$i] ? $this->weekdayAlias[$year[$i]] : '零'; |
||
| 459 | } |
||
| 460 | |||
| 461 | return $lunarYear; |
||
| 462 | } |
||
| 463 | |||
| 464 | /** |
||
| 465 | * 传入农历数字月份返回汉语通俗表示法. |
||
| 466 | * |
||
| 467 | * @param int $month |
||
| 468 | * |
||
| 469 | * @return string |
||
| 470 | */ |
||
| 471 | public function toChinaMonth($month) |
||
| 472 | { |
||
| 473 | // 若参数错误 返回 -1 |
||
| 474 | if ($month > 12 || $month < 1) { |
||
| 475 | throw new InvalidArgumentException("错误的月份:{$month}"); |
||
| 476 | } |
||
| 477 | |||
| 478 | return $this->monthAlias[abs($month) - 1].'月'; |
||
| 479 | } |
||
| 480 | |||
| 481 | /** |
||
| 482 | * 传入农历日期数字返回汉字表示法. |
||
| 483 | * |
||
| 484 | * @param int $day |
||
| 485 | * |
||
| 486 | * @return string |
||
| 487 | */ |
||
| 488 | public function toChinaDay($day) |
||
| 489 | { |
||
| 490 | switch ($day) { |
||
| 491 | case 10: |
||
| 492 | return '初十'; |
||
| 493 | case 20: |
||
| 494 | return '二十'; |
||
| 495 | case 30: |
||
| 496 | return '三十'; |
||
| 497 | default: |
||
| 498 | return $this->dateAlias[intval($day / 10)].$this->weekdayAlias[$day % 10]; |
||
| 499 | } |
||
| 500 | } |
||
| 501 | |||
| 502 | /** |
||
| 503 | * 年份转生肖. |
||
| 504 | * |
||
| 505 | * 仅能大致转换, 精确划分生肖分界线是 “立春”. |
||
| 506 | * |
||
| 507 | * @param int $year |
||
| 508 | * @param null|int $termIndex |
||
| 509 | * |
||
| 510 | * @return string |
||
| 511 | */ |
||
| 512 | public function getAnimal($year, $termIndex = null) |
||
| 513 | { |
||
| 514 | // 认为此逻辑不需要,详情参见 ganZhiYear 相关注释 |
||
| 515 | $adjust = null !== $termIndex && 3 > $termIndex ? 1 : 0; |
||
| 516 | |||
| 517 | $animalIndex = ($year + $adjust - 4) % 12; |
||
| 518 | |||
| 519 | return $this->animals[$animalIndex]; |
||
| 520 | } |
||
| 521 | |||
| 522 | /** |
||
| 523 | * 干支转色彩. |
||
| 524 | * |
||
| 525 | * @param $ganZhi |
||
| 526 | * |
||
| 527 | * @return string |
||
| 528 | */ |
||
| 529 | protected function getColor($ganZhi) |
||
| 530 | { |
||
| 531 | if (!$ganZhi) { |
||
| 532 | return null; |
||
| 533 | } |
||
| 534 | |||
| 535 | $gan = substr($ganZhi, 0, 3); |
||
| 536 | |||
| 537 | if (!$gan) { |
||
| 538 | return null; |
||
| 539 | } |
||
| 540 | |||
| 541 | return $this->colors[array_search($gan, $this->gan)]; |
||
| 542 | } |
||
| 543 | |||
| 544 | /** |
||
| 545 | * 干支转五行. |
||
| 546 | * |
||
| 547 | * @param $ganZhi |
||
| 548 | * |
||
| 549 | * @return string |
||
| 550 | */ |
||
| 551 | protected function getWuXing($ganZhi) |
||
| 552 | { |
||
| 553 | if (!$ganZhi) { |
||
| 554 | return null; |
||
| 555 | } |
||
| 556 | |||
| 557 | $gan = substr($ganZhi, 0, 3); |
||
| 558 | $zhi = substr($ganZhi, 3); |
||
| 559 | |||
| 560 | if (!$gan || !$zhi) { |
||
| 561 | return null; |
||
| 562 | } |
||
| 563 | |||
| 564 | $wGan = $this->wuXing[array_search($gan, $this->gan)]; |
||
| 565 | $wZhi = $this->zhiWuxing[array_search($zhi, $this->zhi)]; |
||
| 566 | |||
| 567 | return $wGan.$wZhi; |
||
| 568 | } |
||
| 569 | |||
| 570 | /** |
||
| 571 | * 阳历转阴历. |
||
| 572 | * |
||
| 573 | * @param int $year |
||
| 574 | * @param int $month |
||
| 575 | * @param int $day |
||
| 576 | * @param int $hour |
||
| 577 | * |
||
| 578 | * @return array |
||
| 579 | */ |
||
| 580 | public function solar2lunar($year, $month, $day, $hour = null) |
||
| 581 | { |
||
| 582 | if (23 == $hour) { |
||
| 583 | // 23点过后算子时,农历以子时为一天的起始 |
||
| 584 | $date = $this->makeDate("{$year}-{$month}-{$day} +1day"); |
||
| 585 | } else { |
||
| 586 | $date = $this->makeDate("{$year}-{$month}-{$day}"); |
||
| 587 | } |
||
| 588 | |||
| 589 | list($year, $month, $day) = explode('-', $date->format('Y-n-j')); |
||
| 590 | |||
| 591 | // 参数区间1900.1.31~2100.12.31 |
||
| 592 | if ($year < 1900 || $year > 2100) { |
||
| 593 | throw new InvalidArgumentException("不支持的年份:{$year}"); |
||
| 594 | } |
||
| 595 | |||
| 596 | // 年份限定、上限 |
||
| 597 | if (1900 == $year && 1 == $month && $day < 31) { |
||
| 598 | throw new InvalidArgumentException("不支持的日期:{$year}-{$month}-{$day}"); |
||
| 599 | } |
||
| 600 | |||
| 601 | $offset = (int)$this->dateDiff($date, '1900-01-31')->days; |
||
| 602 | |||
| 603 | for ($i = 1900; $i < 2101 && $offset > 0; ++$i) { |
||
| 604 | $daysOfYear = $this->daysOfYear($i); |
||
| 605 | $offset -= $daysOfYear; |
||
| 606 | } |
||
| 607 | |||
| 608 | if ($offset < 0) { |
||
| 609 | $offset += $daysOfYear; |
||
|
|
|||
| 610 | --$i; |
||
| 611 | } |
||
| 612 | |||
| 613 | // 农历年 |
||
| 614 | $lunarYear = $i; |
||
| 615 | |||
| 616 | $leap = $this->leapMonth($i); // 闰哪个月 |
||
| 617 | $isLeap = false; |
||
| 618 | |||
| 619 | // 用当年的天数 offset,逐个减去每月(农历)的天数,求出当天是本月的第几天 |
||
| 620 | for ($i = 1; $i < 13 && $offset > 0; ++$i) { |
||
| 621 | // 闰月 |
||
| 622 | if ($leap > 0 && $i == ($leap + 1) && !$isLeap) { |
||
| 623 | --$i; |
||
| 624 | $isLeap = true; |
||
| 625 | $daysOfMonth = $this->leapDays($lunarYear); // 计算农历月天数 |
||
| 626 | } else { |
||
| 627 | $daysOfMonth = $this->lunarDays($lunarYear, $i); // 计算农历普通月天数 |
||
| 628 | } |
||
| 629 | |||
| 630 | // 解除闰月 |
||
| 631 | if (true === $isLeap && $i == ($leap + 1)) { |
||
| 632 | $isLeap = false; |
||
| 633 | } |
||
| 634 | |||
| 635 | $offset -= $daysOfMonth; |
||
| 636 | } |
||
| 637 | // offset为0时,并且刚才计算的月份是闰月,要校正 |
||
| 638 | if (0 === $offset && $leap > 0 && $i == $leap + 1) { |
||
| 639 | if ($isLeap) { |
||
| 640 | $isLeap = false; |
||
| 641 | } else { |
||
| 642 | $isLeap = true; |
||
| 643 | --$i; |
||
| 644 | } |
||
| 645 | } |
||
| 646 | |||
| 647 | if ($offset < 0) { |
||
| 648 | $offset += $daysOfMonth; |
||
| 649 | --$i; |
||
| 650 | } |
||
| 651 | |||
| 652 | // 农历月 |
||
| 653 | $lunarMonth = $i; |
||
| 654 | |||
| 655 | // 农历日 |
||
| 656 | $lunarDay = $offset + 1; |
||
| 657 | |||
| 658 | // 月柱 1900 年 1 月小寒以前为 丙子月(60进制12) |
||
| 659 | $firstNode = $this->getTerm($year, ($month * 2 - 1)); // 返回当月「节气」为几日开始 |
||
| 660 | $secondNode = $this->getTerm($year, ($month * 2)); // 返回当月「节气」为几日开始 |
||
| 661 | |||
| 662 | // 依据 12 节气修正干支月 |
||
| 663 | $ganZhiMonth = $this->toGanZhi(($year - 1900) * 12 + $month + 11); |
||
| 664 | |||
| 665 | if ($day >= $firstNode) { |
||
| 666 | $ganZhiMonth = $this->toGanZhi(($year - 1900) * 12 + $month + 12); |
||
| 667 | } |
||
| 668 | |||
| 669 | // 获取该天的节气 |
||
| 670 | $termIndex = null; |
||
| 671 | if ($firstNode == $day) { |
||
| 672 | $termIndex = $month * 2 - 2; |
||
| 673 | } |
||
| 674 | |||
| 675 | if ($secondNode == $day) { |
||
| 676 | $termIndex = $month * 2 - 1; |
||
| 677 | } |
||
| 678 | |||
| 679 | $term = null !== $termIndex ? $this->solarTerm[$termIndex] : null; |
||
| 680 | |||
| 681 | // 日柱 当月一日与 1900/1/1 相差天数 |
||
| 682 | $dayCyclical = $this->dateDiff("{$year}-{$month}-01", '1900-01-01')->days + 10; |
||
| 683 | $dayCyclical += $day - 1; |
||
| 684 | $ganZhiDay = $this->toGanZhi($dayCyclical); |
||
| 685 | |||
| 686 | // 时柱和时辰 |
||
| 687 | list($ganZhiHour, $lunarHour, $hour) = $this->ganZhiHour($hour, $dayCyclical); |
||
| 688 | |||
| 689 | $ganZhiYear = $this->ganZhiYear($lunarYear, $termIndex); |
||
| 690 | |||
| 691 | return [ |
||
| 692 | 'lunar_year' => (string) $lunarYear, |
||
| 693 | 'lunar_month' => sprintf('%02d', $lunarMonth), |
||
| 694 | 'lunar_day' => sprintf('%02d', $lunarDay), |
||
| 695 | 'lunar_hour' => $hour, |
||
| 696 | 'lunar_year_chinese' => $this->toChinaYear($lunarYear), |
||
| 697 | 'lunar_month_chinese' => ($isLeap ? '闰' : '').$this->toChinaMonth($lunarMonth), |
||
| 698 | 'lunar_day_chinese' => $this->toChinaDay($lunarDay), |
||
| 699 | 'lunar_hour_chinese' => $lunarHour, |
||
| 700 | 'ganzhi_year' => $ganZhiYear, |
||
| 701 | 'ganzhi_month' => $ganZhiMonth, |
||
| 702 | 'ganzhi_day' => $ganZhiDay, |
||
| 703 | 'ganzhi_hour' => $ganZhiHour, |
||
| 704 | 'wuxing_year' => $this->getWuXing($ganZhiYear), |
||
| 705 | 'wuxing_month' => $this->getWuXing($ganZhiMonth), |
||
| 706 | 'wuxing_day' => $this->getWuXing($ganZhiDay), |
||
| 707 | 'wuxing_hour' => $this->getWuXing($ganZhiHour), |
||
| 708 | 'color_year' => $this->getColor($ganZhiYear), |
||
| 709 | 'color_month' => $this->getColor($ganZhiMonth), |
||
| 710 | 'color_day' => $this->getColor($ganZhiDay), |
||
| 711 | 'color_hour' => $this->getColor($ganZhiHour), |
||
| 712 | 'animal' => $this->getAnimal($lunarYear, $termIndex), |
||
| 713 | 'term' => $term, |
||
| 714 | 'is_leap' => $isLeap, |
||
| 715 | ]; |
||
| 716 | } |
||
| 717 | |||
| 718 | /** |
||
| 719 | * 阴历转阳历. |
||
| 720 | * |
||
| 721 | * @param int $year |
||
| 722 | * @param int $month |
||
| 723 | * @param int $day |
||
| 724 | * @param bool $isLeapMonth |
||
| 725 | * |
||
| 726 | * @return array|int |
||
| 727 | */ |
||
| 728 | public function lunar2solar($year, $month, $day, $isLeapMonth = false) |
||
| 729 | { |
||
| 730 | // 参数区间 1900.1.3 1 ~2100.12.1 |
||
| 731 | $leapMonth = $this->leapMonth($year); |
||
| 732 | |||
| 733 | // 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同 |
||
| 734 | if ($isLeapMonth && ($leapMonth != $month)) { |
||
| 735 | $isLeapMonth = false; |
||
| 736 | } |
||
| 737 | |||
| 738 | // 超出了最大极限值 |
||
| 739 | if (2100 == $year && 12 == $month && $day > 1 || 1900 == $year && 1 == $month && $day < 31) { |
||
| 740 | return -1; |
||
| 741 | } |
||
| 742 | |||
| 743 | $maxDays = $days = $this->lunarDays($year, $month); |
||
| 744 | |||
| 745 | // if month is leap, _day use leapDays method |
||
| 746 | if ($isLeapMonth) { |
||
| 747 | $maxDays = $this->leapDays($year, $month); |
||
| 748 | } |
||
| 749 | |||
| 750 | // 参数合法性效验 |
||
| 751 | if ($year < 1900 || $year > 2100 || $day > $maxDays) { |
||
| 752 | throw new InvalidArgumentException('传入的参数不合法'); |
||
| 753 | } |
||
| 754 | |||
| 755 | // 计算农历的时间差 |
||
| 756 | $offset = 0; |
||
| 757 | |||
| 758 | for ($i = 1900; $i < $year; ++$i) { |
||
| 759 | $offset += $this->daysOfYear($i); |
||
| 760 | } |
||
| 761 | |||
| 762 | $isAdd = false; |
||
| 763 | for ($i = 1; $i < $month; ++$i) { |
||
| 764 | $leap = $this->leapMonth($year); |
||
| 765 | if (!$isAdd) {// 处理闰月 |
||
| 766 | if ($leap <= $i && $leap > 0) { |
||
| 767 | $offset += $this->leapDays($year); |
||
| 768 | $isAdd = true; |
||
| 769 | } |
||
| 770 | } |
||
| 771 | $offset += $this->lunarDays($year, $i); |
||
| 772 | } |
||
| 773 | |||
| 774 | // 转换闰月农历 需补充该年闰月的前一个月的时差 |
||
| 775 | if ($isLeapMonth) { |
||
| 776 | $offset += $days; |
||
| 777 | } |
||
| 778 | |||
| 779 | // 1900 年农历正月一日的公历时间为 1900 年 1 月 30 日 0 时 0 分 0 秒 (该时间也是本农历的最开始起始点) |
||
| 780 | // XXX: 部分 windows 机器不支持负时间戳,所以这里就写死了,哈哈哈哈... |
||
| 781 | $startTimestamp = -2206483200; |
||
| 782 | $date = date('Y-m-d', ($offset + $day) * 86400 + $startTimestamp); |
||
| 783 | |||
| 784 | list($solarYear, $solarMonth, $solarDay) = explode('-', $date); |
||
| 785 | |||
| 786 | return [ |
||
| 787 | 'solar_year' => $solarYear, |
||
| 788 | 'solar_month' => sprintf('%02d', $solarMonth), |
||
| 789 | 'solar_day' => sprintf('%02d', $solarDay), |
||
| 790 | ]; |
||
| 791 | } |
||
| 792 | |||
| 793 | /** |
||
| 794 | * 获取两个日期之间的距离. |
||
| 795 | * |
||
| 796 | * @param string|\DateTime $date1 |
||
| 797 | * @param string|\DateTime $date2 |
||
| 798 | * |
||
| 799 | * @return bool|\DateInterval |
||
| 800 | */ |
||
| 801 | public function dateDiff($date1, $date2) |
||
| 812 | } |
||
| 813 | |||
| 814 | /** |
||
| 815 | * 获取两个日期之间以年为单位的距离. |
||
| 816 | * |
||
| 817 | * @param array $lunar1 |
||
| 818 | * @param array $lunar2 |
||
| 819 | * @param bool $absolute |
||
| 820 | * |
||
| 821 | * @return int |
||
| 822 | */ |
||
| 823 | public function diffInYears($lunar1, $lunar2, $absolute = true) |
||
| 824 | { |
||
| 825 | $solar1 = |
||
| 826 | $this->lunar2solar($lunar1['lunar_year'], $lunar1['lunar_month'], $lunar1['lunar_day'], $lunar1['is_leap']); |
||
| 827 | $date1 = $this->makeDate("{$solar1['solar_year']}-{$solar1['solar_month']}-{$solar1['solar_day']}"); |
||
| 828 | |||
| 829 | $solar2 = |
||
| 830 | $this->lunar2solar($lunar2['lunar_year'], $lunar2['lunar_month'], $lunar2['lunar_day'], $lunar2['is_leap']); |
||
| 855 | } |
||
| 856 | |||
| 857 | /** |
||
| 858 | * 获取两个日期之间以月为单位的距离. |
||
| 859 | * @param array $lunar1 |
||
| 860 | * @param array $lunar2 |
||
| 861 | * @param bool $absolute |
||
| 862 | * @return float|int|mixed |
||
| 863 | */ |
||
| 864 | public function diffInMonths($lunar1, $lunar2, $absolute = true) |
||
| 865 | { |
||
| 866 | $solar1 = |
||
| 867 | $this->lunar2solar($lunar1['lunar_year'], $lunar1['lunar_month'], $lunar1['lunar_day'], $lunar1['is_leap']); |
||
| 868 | $date1 = $this->makeDate("{$solar1['solar_year']}-{$solar1['solar_month']}-{$solar1['solar_day']}"); |
||
| 869 | |||
| 870 | $solar2 = |
||
| 871 | $this->lunar2solar($lunar2['lunar_year'], $lunar2['lunar_month'], $lunar2['lunar_day'], $lunar2['is_leap']); |
||
| 872 | $date2 = $this->makeDate("{$solar2['solar_year']}-{$solar2['solar_month']}-{$solar2['solar_day']}"); |
||
| 873 | |||
| 874 | if ($date1 < $date2) { |
||
| 875 | $lessLunar = $lunar1; |
||
| 876 | $greaterLunar = $lunar2; |
||
| 877 | $changed = false; |
||
| 878 | } else { |
||
| 879 | $lessLunar = $lunar2; |
||
| 880 | $greaterLunar = $lunar1; |
||
| 881 | $changed = true; |
||
| 882 | } |
||
| 883 | |||
| 884 | $diff = 0; |
||
| 885 | |||
| 886 | if ($lessLunar['lunar_year'] == $greaterLunar['lunar_year']) { |
||
| 887 | $leapMonth = $this->leapMonth($lessLunar['lunar_year']); |
||
| 888 | $lessLunarAdjustFactor = |
||
| 889 | $lessLunar['is_leap'] || (0 < $leapMonth && $leapMonth < $lessLunar['lunar_month']) ? 1 : 0; |
||
| 890 | $greaterLunarAdjustFactor = |
||
| 891 | $greaterLunar['is_leap'] || (0 < $leapMonth && $leapMonth < $greaterLunar['lunar_month']) ? 1 : 0; |
||
| 892 | $diff = |
||
| 893 | $greaterLunar['lunar_month'] + $greaterLunarAdjustFactor - $lessLunar['lunar_month'] - $lessLunarAdjustFactor; |
||
| 894 | } else { |
||
| 895 | $lessLunarLeapMonth = $this->leapMonth($lessLunar['lunar_year']); |
||
| 896 | $greaterLunarLeapMonth = $this->leapMonth($greaterLunar['lunar_year']); |
||
| 897 | |||
| 898 | $lessLunarAdjustFactor = |
||
| 899 | (!$lessLunar['is_leap'] && $lessLunarLeapMonth == $lessLunar['lunar_month']) || $lessLunarLeapMonth > $lessLunar['lunar_month'] ? 1 : 0; |
||
| 900 | $diff += 12 + $lessLunarAdjustFactor - $lessLunar['lunar_month']; |
||
| 901 | for ($i = $lessLunar['lunar_year'] + 1; $i < $greaterLunar['lunar_year']; ++$i) { |
||
| 902 | $diff += $this->monthsOfYear($i); |
||
| 903 | } |
||
| 904 | $greaterLunarAdjustFactor = |
||
| 905 | $greaterLunar['is_leap'] || (0 < $greaterLunarLeapMonth && $greaterLunarLeapMonth < $greaterLunar['lunar_month']) ? 1 : 0; |
||
| 906 | $diff += $greaterLunarAdjustFactor + $greaterLunar['lunar_month']; |
||
| 907 | } |
||
| 908 | |||
| 909 | $diff -= $greaterLunar['lunar_day'] >= $lessLunar['lunar_day'] ? 0 : 1; |
||
| 910 | |||
| 911 | return $absolute ? $diff : ($changed ? -1 * $diff : $diff); |
||
| 912 | } |
||
| 913 | |||
| 914 | /** |
||
| 915 | * 获取两个日期之间以日为单位的距离. |
||
| 916 | * |
||
| 917 | * @param array $lunar1 |
||
| 918 | * @param array $lunar2 |
||
| 919 | * @param bool $absolute |
||
| 920 | * |
||
| 921 | * @return int |
||
| 922 | */ |
||
| 923 | public function diffInDays($lunar1, $lunar2, $absolute = true) |
||
| 924 | { |
||
| 925 | $solar1 = |
||
| 926 | $this->lunar2solar($lunar1['lunar_year'], $lunar1['lunar_month'], $lunar1['lunar_day'], $lunar1['is_leap']); |
||
| 927 | $date1 = $this->makeDate("{$solar1['solar_year']}-{$solar1['solar_month']}-{$solar1['solar_day']}"); |
||
| 928 | |||
| 929 | $solar2 = |
||
| 930 | $this->lunar2solar($lunar2['lunar_year'], $lunar2['lunar_month'], $lunar2['lunar_day'], $lunar2['is_leap']); |
||
| 931 | $date2 = $this->makeDate("{$solar2['solar_year']}-{$solar2['solar_month']}-{$solar2['solar_day']}"); |
||
| 932 | |||
| 933 | return $date1->diff($date2, $absolute)->format('%r%a'); |
||
| 934 | } |
||
| 935 | |||
| 936 | /** |
||
| 937 | * 增加年数. |
||
| 938 | * |
||
| 939 | * @param array $lunar |
||
| 940 | * @param int $value |
||
| 941 | * @param bool $overFlow |
||
| 942 | * |
||
| 943 | * @return array |
||
| 944 | */ |
||
| 945 | public function addYears($lunar, $value = 1, $overFlow = true) |
||
| 946 | { |
||
| 947 | $newYear = $lunar['lunar_year'] + $value; |
||
| 948 | $newMonth = $lunar['lunar_month']; |
||
| 949 | $newDay = $lunar['lunar_day']; |
||
| 950 | $isLeap = $lunar['is_leap']; |
||
| 951 | $needOverFlow = false; |
||
| 952 | |||
| 953 | $leapMonth = $this->leapMonth($newYear); |
||
| 954 | $isLeap = $isLeap && $newMonth == $leapMonth; |
||
| 955 | $maxDays = $isLeap ? $this->leapDays($newYear) : $this->lunarDays($newYear, $newMonth); |
||
| 956 | |||
| 957 | if ($newDay > $maxDays) { |
||
| 958 | if ($overFlow) { |
||
| 959 | $newDay = 1; |
||
| 960 | $needOverFlow = true; |
||
| 961 | } else { |
||
| 962 | $newDay = $maxDays; |
||
| 963 | } |
||
| 964 | } |
||
| 965 | $ret = $this->lunar($newYear, $newMonth, $newDay, $isLeap); |
||
| 966 | if ($needOverFlow) { |
||
| 967 | $ret = $this->addMonths($ret, 1, $overFlow); |
||
| 968 | } |
||
| 969 | |||
| 970 | return $ret; |
||
| 971 | } |
||
| 972 | |||
| 973 | /** |
||
| 974 | * 减少年数. |
||
| 975 | * |
||
| 976 | * @param array $lunar |
||
| 977 | * @param int $value |
||
| 978 | * @param bool $overFlow |
||
| 979 | * |
||
| 980 | * @return array |
||
| 981 | */ |
||
| 982 | public function subYears($lunar, $value = 1, $overFlow = true) |
||
| 983 | { |
||
| 984 | return $this->addYears($lunar, -1 * $value, $overFlow); |
||
| 985 | } |
||
| 986 | |||
| 987 | /** |
||
| 988 | * 增加月数. |
||
| 989 | * |
||
| 990 | * @param array $lunar |
||
| 991 | * @param int $value |
||
| 992 | * @param bool $overFlow |
||
| 993 | * |
||
| 994 | * @return array |
||
| 995 | */ |
||
| 996 | public function addMonths($lunar, $value = 1, $overFlow = true) |
||
| 997 | { |
||
| 998 | if (0 > $value) { |
||
| 999 | return $this->subMonths($lunar, -1 * $value, $overFlow); |
||
| 1000 | } else { |
||
| 1001 | $newYear = $lunar['lunar_year']; |
||
| 1002 | $newMonth = $lunar['lunar_month']; |
||
| 1003 | $newDay = $lunar['lunar_day']; |
||
| 1004 | $isLeap = $lunar['is_leap']; |
||
| 1005 | |||
| 1006 | while (0 < $value) { |
||
| 1007 | $leapMonth = $this->leapMonth($newYear); |
||
| 1008 | if (0 < $leapMonth) { |
||
| 1009 | $currentIsLeap = $isLeap; |
||
| 1010 | $isLeap = $newMonth + $value == $leapMonth + ($isLeap ? 0 : 1); |
||
| 1011 | |||
| 1012 | if ((!$currentIsLeap && $leapMonth == $newMonth) || ($newMonth < $leapMonth && $newMonth + $value > $leapMonth)) { |
||
| 1013 | --$value; |
||
| 1014 | } |
||
| 1015 | } else { |
||
| 1016 | $isLeap = false; |
||
| 1017 | } |
||
| 1018 | |||
| 1019 | if (13 > $newMonth + $value) { |
||
| 1020 | $newMonth += $value; |
||
| 1021 | $value = 0; |
||
| 1022 | } else { |
||
| 1023 | $value = $value + $newMonth - 13; |
||
| 1024 | ++$newYear; |
||
| 1025 | $newMonth = 1; |
||
| 1026 | } |
||
| 1027 | |||
| 1028 | if (0 == $value) { |
||
| 1029 | $maxDays = $isLeap ? $this->leapDays($newYear) : $this->lunarDays($newYear, $newMonth); |
||
| 1030 | if ($newDay > $maxDays) { |
||
| 1031 | if ($overFlow) { |
||
| 1032 | $newDay = 1; |
||
| 1033 | ++$value; |
||
| 1034 | } else { |
||
| 1035 | $newDay = $maxDays; |
||
| 1036 | } |
||
| 1037 | } |
||
| 1038 | } |
||
| 1039 | } |
||
| 1040 | |||
| 1041 | return $this->lunar($newYear, $newMonth, $newDay, $isLeap); |
||
| 1042 | } |
||
| 1043 | } |
||
| 1044 | |||
| 1045 | /** |
||
| 1046 | * 减少月数. |
||
| 1047 | * |
||
| 1048 | * @param array $lunar |
||
| 1049 | * @param int $value |
||
| 1050 | * @param bool $overFlow |
||
| 1051 | * |
||
| 1052 | * @return array |
||
| 1053 | */ |
||
| 1054 | public function subMonths($lunar, $value = 1, $overFlow = true) |
||
| 1102 | } |
||
| 1103 | } |
||
| 1104 | |||
| 1105 | /** |
||
| 1106 | * 增加天数. |
||
| 1107 | * |
||
| 1108 | * @param array $lunar |
||
| 1109 | * @param int $value |
||
| 1110 | * |
||
| 1111 | * @return array |
||
| 1112 | */ |
||
| 1113 | public function addDays($lunar, $value = 1) |
||
| 1114 | { |
||
| 1115 | $solar = |
||
| 1116 | $this->lunar2solar($lunar['lunar_year'], $lunar['lunar_month'], $lunar['lunar_day'], $lunar['is_leap']); |
||
| 1117 | $date = $this->makeDate("{$solar['solar_year']}-{$solar['solar_month']}-{$solar['solar_day']}"); |
||
| 1118 | $date->modify($value.' day'); |
||
| 1119 | |||
| 1120 | return $this->solar2lunar((int)$date->format('Y'), (int)$date->format('m'), (int)$date->format('d')); |
||
| 1121 | } |
||
| 1122 | |||
| 1123 | /** |
||
| 1124 | * 减少天数. |
||
| 1125 | * |
||
| 1126 | * @param array $lunar |
||
| 1127 | * @param int $value |
||
| 1128 | * |
||
| 1129 | * @return array |
||
| 1130 | */ |
||
| 1131 | public function subDays($lunar, $value = 1) |
||
| 1132 | { |
||
| 1133 | return $this->addDays($lunar, -1 * $value); |
||
| 1134 | } |
||
| 1135 | |||
| 1136 | /** |
||
| 1137 | * 创建日期对象 |
||
| 1138 | * |
||
| 1139 | * @param string $string |
||
| 1140 | * @param string $timezone |
||
| 1141 | * |
||
| 1142 | * @return \DateTime |
||
| 1143 | */ |
||
| 1144 | protected function makeDate($string = 'now', $timezone = 'PRC') |
||
| 1147 | } |
||
| 1148 | |||
| 1149 | /** |
||
| 1150 | * 获取时柱. |
||
| 1151 | * |
||
| 1152 | * @param int $hour 0~23 小时格式 |
||
| 1153 | * @param int $ganZhiDay 干支日期 |
||
| 1154 | * |
||
| 1155 | * @return array |
||
| 1156 | * |
||
| 1157 | * @see https://baike.baidu.com/item/%E6%97%B6%E6%9F%B1/6274024 |
||
| 1158 | */ |
||
| 1159 | protected function ganZhiHour($hour, $ganZhiDay) |
||
| 1175 |