| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | declare(strict_types=1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | namespace PhpCfdi\SatCatalogos\CFDI; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | use DateTimeImmutable; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | use PhpCfdi\SatCatalogos\Exceptions\SatCatalogosLogicException; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | class HusoHorario | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  |     /** @var string */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  |     private $text; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |     /** @var HusoHorarioEstacion */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  |     private $verano; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |     /** @var HusoHorarioEstacion */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     private $invierno; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 | 20 |  |     public function __construct(string $text, HusoHorarioEstacion $verano, HusoHorarioEstacion $invierno) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 | 20 |  |         $this->text = $text; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 | 20 |  |         $this->verano = $verano; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 | 20 |  |         $this->invierno = $invierno; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 | 20 |  |         if ($verano->tieneCambioHorario() !== $invierno->tieneCambioHorario()) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 | 1 |  |             throw new SatCatalogosLogicException( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 | 1 |  |                 'No se puede crear un huso horario con estaciones donde solo una tiene cambio de horario' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |             ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 | 19 |  |         if (! $verano->tieneCambioHorario() and $verano->diferencia() !== $invierno->diferencia()) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 | 1 |  |             throw new SatCatalogosLogicException( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 | 1 |  |                 'El huso horario no tiene cambio de horario de verano pero tiene no tiene la misma diferencia horaria' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |             ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 | 18 |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 | 2 |  |     public function nombre(): string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 | 2 |  |         return $this->text; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 | 2 |  |     public function verano(): HusoHorarioEstacion | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 | 2 |  |         return $this->verano; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 | 2 |  |     public function invierno(): HusoHorarioEstacion | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 | 2 |  |         return $this->invierno; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 54 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 55 | 10 |  |     public function convertToDateTime(string $partialDate): DateTimeImmutable | 
            
                                                                        
                            
            
                                    
            
            
                | 56 |  |  |     { | 
            
                                                                        
                            
            
                                    
            
            
                | 57 | 10 |  |         $date = $this->dateTimeFromPartial($partialDate, $this->invierno->diferencia()); | 
            
                                                                        
                            
            
                                    
            
            
                | 58 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 59 |  |  |         // time zone does not have DST | 
            
                                                                        
                            
            
                                    
            
            
                | 60 | 10 |  |         if (! $this->verano->tieneCambioHorario()) { | 
            
                                                                        
                            
            
                                    
            
            
                | 61 |  |  |             return $date; | 
            
                                                                        
                            
            
                                    
            
            
                | 62 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 64 | 10 |  |         $year = (int) $date->format('Y'); | 
            
                                                                        
                            
            
                                    
            
            
                | 65 | 10 |  |         $dstSince = $this->dstLimit($year, $this->verano, $this->invierno->diferencia()); | 
            
                                                                        
                            
            
                                    
            
            
                | 66 | 10 |  |         $dstUntil = $this->dstLimit($year, $this->invierno, $this->invierno->diferencia()); | 
            
                                                                        
                            
            
                                    
            
            
                | 67 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 68 |  |  |         // is outside DST, nothing to change | 
            
                                                                        
                            
            
                                    
            
            
                | 69 | 10 |  |         if ($date < $dstSince || $date >= $dstUntil) { | 
            
                                                                        
                            
            
                                    
            
            
                | 70 | 5 |  |             return $date; | 
            
                                                                        
                            
            
                                    
            
            
                | 71 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 72 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 73 |  |  |         // is DST | 
            
                                                                        
                            
            
                                    
            
            
                | 74 | 5 |  |         $dstHoursDiff = $this->verano->diferencia() - $this->invierno->diferencia(); | 
            
                                                                        
                            
            
                                    
            
            
                | 75 | 5 |  |         $dstSince = $dstSince->modify(sprintf('%d hours', $dstHoursDiff)); | 
            
                                                                        
                            
            
                                    
            
            
                | 76 | 5 |  |         $altered = $this->dateTimeFromPartial($partialDate, $this->verano->diferencia()); | 
            
                                                                        
                            
            
                                    
            
            
                | 77 | 5 |  |         if ($date < $dstSince) { | 
            
                                                                        
                            
            
                                    
            
            
                | 78 | 2 |  |             $altered = $altered->modify(sprintf('%d hours', $dstHoursDiff)); | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 80 | 5 |  |         return $altered; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 | 10 |  |     private function dateTimeFromPartial(string $partialDate, int $hoursDiff): DateTimeImmutable | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 | 10 |  |         $sign = $hoursDiff < 0 ? '-' : '+'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 | 10 |  |         $iso8601 = sprintf('%s%s%02d00', $partialDate, $sign, abs($hoursDiff)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |         /** @noinspection PhpUnhandledExceptionInspection */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 | 10 |  |         return new DateTimeImmutable($iso8601); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 | 10 |  |     private function dstLimit(int $year, HusoHorarioEstacion $limit, int $hoursDiff): DateTimeImmutable | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |         /** @noinspection PhpUnhandledExceptionInspection */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 | 10 |  |         $date = new DateTimeImmutable(sprintf('%04d-%02d-01 00:00:00 UTC', $year, $limit->mesNumerico())); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 | 10 |  |         $date = $date->modify($limit->diaExpresion()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 | 10 |  |         $date = $date->modify(sprintf('%d hours', $limit->horaNumero() - $hoursDiff)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 | 10 |  |         return $date; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 99 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 100 |  |  |  |