Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace OpenSpout\Reader\XLSX\Helper;
6
 
7
use DateInterval;
8
 
9
final class DateIntervalFormatHelper
10
{
11
    /**
12
     * @see https://www.php.net/manual/en/dateinterval.format.php.
13
     */
14
    private const dateIntervalFormats = [
15
        'hh' => '%H',
16
        'h' => '%h',
17
        'mm' => '%I',
18
        'm' => '%i',
19
        'ss' => '%S',
20
        's' => '%s',
21
    ];
22
 
23
    /**
24
     * Excel stores durations as fractions of days (24h = 1).
25
     *
26
     * Only fills hours/minutes/seconds because those are the only values that we can format back out again.
27
     * Excel can also only handle those units as duration.
28
     * PHP's DateInterval is also quite limited - it will not automatically convert unit overflow
29
     *  (60 seconds are not converted to 1 minute).
30
     */
31
    public static function createDateIntervalFromHours(float $dayFractions): DateInterval
32
    {
33
        $time = abs($dayFractions) * 24; // convert to hours
34
        $hours = floor($time);
35
        $time = ($time - $hours) * 60;
36
        $minutes = (int) floor($time); // must cast to int for type strict compare below
37
        $time = ($time - $minutes) * 60;
38
        $seconds = (int) round($time); // must cast to int for type strict compare below
39
 
40
        // Bubble up rounding gain if we ended up with 60 seconds - disadvantage of using fraction of days for small durations:
41
        if (60 === $seconds) {
42
            $seconds = 0;
43
            ++$minutes;
44
        }
45
        if (60 === $minutes) {
46
            $minutes = 0;
47
            ++$hours;
48
        }
49
 
50
        $interval = new DateInterval("P0DT{$hours}H{$minutes}M{$seconds}S");
51
        if ($dayFractions < 0) {
52
            $interval->invert = 1;
53
        }
54
 
55
        return $interval;
56
    }
57
 
58
    public static function isDurationFormat(string $excelFormat): bool
59
    {
60
        // Only consider formats with leading brackets as valid duration formats (e.g. "[hh]:mm", "[mm]:ss", etc.):
61
        return 1 === preg_match('/^(\[hh?](:mm(:ss)?)?|\[mm?](:ss)?|\[ss?])$/', $excelFormat);
62
    }
63
 
64
    public static function toPHPDateIntervalFormat(string $excelDateFormat, ?string &$startUnit = null): string
65
    {
66
        $startUnit = null;
67
        $phpFormatParts = [];
68
        $formatParts = explode(':', str_replace(['[', ']'], '', $excelDateFormat));
69
        foreach ($formatParts as $formatPart) {
70
            $startUnit ??= $formatPart;
71
            $phpFormatParts[] = self::dateIntervalFormats[$formatPart];
72
        }
73
 
74
        // Add the minus sign for potential negative durations:
75
        return '%r'.implode(':', $phpFormatParts);
76
    }
77
 
78
    public static function formatDateInterval(DateInterval $dateInterval, string $excelDateFormat): string
79
    {
80
        $phpFormat = self::toPHPDateIntervalFormat($excelDateFormat, $startUnit);
81
 
82
        // We have to move the hours to minutes or hours+minutes to seconds if the format in Excel did the same:
83
        $startUnit = $startUnit[0]; // only take the first char
84
        $dateIntervalClone = clone $dateInterval;
85
        if ('m' === $startUnit) {
86
            $dateIntervalClone->i = $dateIntervalClone->i + $dateIntervalClone->h * 60;
87
            $dateIntervalClone->h = 0;
88
        } elseif ('s' === $startUnit) {
89
            $dateIntervalClone->s = $dateIntervalClone->s + $dateIntervalClone->i * 60 + $dateIntervalClone->h * 3600;
90
            $dateIntervalClone->i = 0;
91
            $dateIntervalClone->h = 0;
92
        }
93
 
94
        return $dateIntervalClone->format($phpFormat);
95
    }
96
}