diff --git a/htdocs/core/lib/date.lib.php b/htdocs/core/lib/date.lib.php index 2e3faf20efb..acab2d3b847 100644 --- a/htdocs/core/lib/date.lib.php +++ b/htdocs/core/lib/date.lib.php @@ -115,7 +115,7 @@ function getServerTimeZoneInt($refgmtdate = 'now') /** * Add a delay to a date * - * @param int $time Date timestamp + * @param int $time Date timestamp (Must be a UTC timestamp) * @param float $duration_value Value of delay to add * @param string $duration_unit Unit of added delay (d, m, y, w, h, i) * @param int<0,1> $ruleforendofmonth Change the behavior of PHP over data-interval, 0 or 1 @@ -163,9 +163,11 @@ function dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforend } $date = new DateTime(); - if (getDolGlobalString('MAIN_DATE_IN_MEMORY_ARE_GMT')) { + if (!function_exists('getDolGlobalString') || !getDolGlobalString('MAIN_DATE_IN_MEMORY_ARE_NOT_GMT')) { // Add function_exists to allow usage of this function with minimal context $date->setTimezone(new DateTimeZone('UTC')); } + + $date->setTimestamp((int) $time); $interval = new DateInterval($deltastring); @@ -174,7 +176,8 @@ function dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforend } else { $date->add($interval); } - //Change the behavior of PHP over data-interval when the result of this function is Feb 29 (non-leap years), 30 or Feb 31 (so php returns March 1, 2 or 3 respectively) + + // Change the behavior of PHP over data-interval when the result of this function is Feb 29 (non-leap years), 30 or Feb 31 (so php returns March 1, 2 or 3 respectively) if ($ruleforendofmonth == 1 && $duration_unit == 'm') { $timeyear = (int) dol_print_date($time, '%Y'); $timemonth = (int) dol_print_date($time, '%m'); @@ -970,7 +973,7 @@ function num_public_holiday($timestampStart, $timestampEnd, $country_code = '', // Geneva fast in Switzerland } } - //print "ferie=".$ferie."\n"; + //print "ferie afterspe=".$ferie."\n"; // If we have to include Friday, Saturday and Sunday if (!$ferie) { @@ -990,7 +993,7 @@ function num_public_holiday($timestampStart, $timestampEnd, $country_code = '', } } } - //print "ferie=".$ferie."\n"; + //print "ferie afterincludexxxday=".$ferie."\n"; // We increase the counter of non working day if ($ferie) { @@ -998,8 +1001,9 @@ function num_public_holiday($timestampStart, $timestampEnd, $country_code = '', } // Increase number of days (on go up into loop) + //var_dump("before ".$jour.' '.$mois.' '.$annee.' '.$timestampStart); $timestampStart = dol_time_plus_duree($timestampStart, 1, 'd'); - //var_dump($jour.' '.$mois.' '.$annee.' '.$timestampStart); + //var_dump("after ".$jour.' '.$mois.' '.$annee.' '.$timestampStart); $i++; } @@ -1335,7 +1339,9 @@ function num_open_day($timestampStart, $timestampEnd, $inhour = 0, $lastday = 0, if ($timestampStart < $timestampEnd) { // --- 1. Calculate Gross Working Days --- // Gross working days = total days in range - non-working days (weekends & public holidays). - $nbOpenDay = num_between_day($timestampStart, $timestampEnd, $lastday) - num_public_holiday($timestampStart, $timestampEnd, $country_code, $lastday); + $a = num_between_day($timestampStart, $timestampEnd, $lastday); + $b = num_public_holiday($timestampStart, $timestampEnd, $country_code, $lastday); + $nbOpenDay = $a - $b; // --- 2. Apply Contextual Half-Day Deductions --- $halfday = (int) $halfday; // Ensure $halfday is an integer for reliable comparisons. diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index ac2d8bc9c74..bd87264bd4a 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -3887,22 +3887,27 @@ function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = } else { // Date is a timestamps if ($time < 100000000000) { // Protection against bad date values - $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring. - + $dtts = new DateTime(); if ($to_gmt) { $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC + $dtts->setTimezone($tzo); // important: must be before the setTimestamp + $dtts->setTimestamp((int) $time); } else { + $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring. + $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server + $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone + $dtts->setTimezone($tzo); } - $dtts = new DateTime(); - $dtts->setTimestamp($timetouse); - $dtts->setTimezone($tzo); + $newformat = str_replace( array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'), array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'), $format ); + $ret = $dtts->format($newformat); + //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret); $ret = str_replace( array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'), array('T', 'Z', '__a__', '__A__', '__b__', '__B__'), diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php index 19b13fdb7f6..386277ba8f1 100644 --- a/htdocs/holiday/card.php +++ b/htdocs/holiday/card.php @@ -1415,7 +1415,7 @@ if ((empty($id) && empty($ref)) || $action == 'create' || $action == 'add') { print $form->textwithpicto($langs->trans('NbUseDaysCP'), $htmlhelp); print ''; print ''; - print num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday); + print num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, (int) $object->halfday); print ''; print ''; diff --git a/test/phpunit/DateLibTest.php b/test/phpunit/DateLibTest.php index b3c70e0409d..51a8d11cfe7 100644 --- a/test/phpunit/DateLibTest.php +++ b/test/phpunit/DateLibTest.php @@ -102,12 +102,6 @@ class DateLibTest extends CommonClassTest print __METHOD__." result=".$result."\n"; $this->assertEquals(3, $result); - /* - $result = num_between_day(1514332800, 1538265600, 0); - print __METHOD__." result=".$result."\n"; - $this->assertEquals(277, $result); - */ - return $result; } @@ -192,6 +186,15 @@ class DateLibTest extends CommonClassTest $langs = $this->savlangs; $db = $this->savdb; + + // Check for sunday/sunday with time changing - Sunday 25 october 2025 - Sunday 1 november 2025 + $date1 = dol_mktime(0, 0, 0, 10, 26, 2025, 'gmt'); + $date2 = dol_mktime(0, 0, 0, 11, 1, 2025, 'gmt'); + $result = num_open_day($date1, $date2, 0, 1, 0, 'FR'); + print __METHOD__." result ".$result."\n"; + $this->assertEquals(5, $result, 'NumPublicHoliday for FR with date start before date change end inding after'); + + // With same hours - Tuesday/Wednesday jan 2013 $date1 = dol_mktime(0, 0, 0, 1, 1, 2013, 'gmt'); // tuesday $date2 = dol_mktime(0, 0, 0, 1, 2, 2013, 'gmt'); // wednesday @@ -422,10 +425,15 @@ class DateLibTest extends CommonClassTest $outputlangs->setDefaultLang('fr_FR'); $outputlangs->load("main"); - $result = dol_print_date(dol_time_plus_duree(dol_time_plus_duree(dol_time_plus_duree(0, 1, 'm'), 1, 'y'), 1, 'd'), 'dayhour', true, $outputlangs); + $result = dol_print_date(dol_time_plus_duree(dol_time_plus_duree(dol_time_plus_duree(0, 1, 'm'), 1, 'y'), 1, 'd'), 'dayhour', 'gmt', $outputlangs); print __METHOD__." result=".$result."\n"; $this->assertEquals('02/02/1971 00:00', $result); + // Add 4 days on a date just before daylight change + $result = dol_print_date(dol_time_plus_duree(dol_mktime(0, 0, 0, 10, 24, 2025, 'gmt'), 4, 'd'), 'dayhour', 'gmt', $outputlangs); + print __METHOD__." result=".$result."\n"; + $this->assertEquals('28/10/2025 00:00', $result); + return $result; }