Jump to content

[4.7.15] DATE form helper bug with timezones when checking min/max date


Recommended Posts

The \IPS\Helpers\Form\Date class does not account properly for a member's timezone when the min and/or max options are set.

 

This is the file's code on lines 303-314 in the validate() function:

		if ( $this->value and $this->options['min'] !== NULL and $this->options['min'] > $this->value )
		{
			$string = $this->options['min']->setTimeZone( $timezone )->localeDate( \IPS\Member::loggedIn() );
			if( $this->options['time'] )
			{
				$string .=' ' . $this->options['min']->setTimeZone( $timezone )->localeTime( \IPS\Member::loggedIn() );
			}
			throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_date_min', FALSE, array( 'sprintf' => array( $string ) ) ) );
		}
		
		/* Check maximum */
		if ( $this->value and $this->options['max'] !== NULL and $this->options['max'] < $this->value )
		{
			$string = $this->options['max']->setTimeZone( $timezone )->localeDate( \IPS\Member::loggedIn() );
			if( $this->options['time'] )
			{
				$string .=' ' . $this->options['max']->setTimeZone( $timezone )->localeTime( \IPS\Member::loggedIn() );
			}
			throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_date_max', FALSE, array( 'sprintf' => array( $string ) ) ) );
		}

 

The code doesn't account at all for the timezone when checking the min/max values against the entered value:

$this->options['min'] > $this->value

$this->options['max'] < $this->value

The timezone is added only inside the IF check to display the error, but not before it for the check:

$string = $this->options['min']->setTimeZone( $timezone )->localeDate( \IPS\Member::loggedIn() );

$string = $this->options['max']->setTimeZone( $timezone )->localeDate( \IPS\Member::loggedIn() );

 

This causes the check to fail for a user close to the UTC timezone, while it passes for a user with a more distant timezone.

 

Here is an example with 2 different timezones (Rome & New York):

DEBUG CODE:
==================================================
print_r( $this->value );
print_r( $this->options['min'] );
print_r( $this->options['min']->setTimeZone( $timezone ) );
var_dump( $this->options['min'] > $this->value );
exit;
==================================================

OUTPUT FOR ROME TIMEZONE:
==================================================
IPS\DateTime Object
(
    [date] => 2024-03-02 00:01:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Rome
)
IPS\DateTime Object
(
    [date] => 2024-03-02 00:58:41.440303
    [timezone_type] => 3
    [timezone] => UTC
)
IPS\DateTime Object
(
    [date] => 2024-03-02 01:58:41.440303
    [timezone_type] => 3
    [timezone] => Europe/Rome
)

bool(true)
==================================================

OUTPUT FOR NEW YORK TIMEZONE:
==================================================
IPS\DateTime Object
(
    [date] => 2024-03-02 02:41:00.000000
    [timezone_type] => 3
    [timezone] => America/New_York
)

IPS\DateTime Object
(
    [date] => 2024-03-02 00:52:00.648474
    [timezone_type] => 3
    [timezone] => UTC
)
IPS\DateTime Object
(
    [date] => 2024-03-01 19:52:00.648474
    [timezone_type] => 3
    [timezone] => America/New_York
)

bool(false)

 

As you can see from the debug output above, the member with a Rome timezone fails to pass the check (TRUE triggers the error), while the New York timezone passes the check (FALSE doesn't trigger the error).

 

The timezone must be added to the min/max checks before the check is done, and not after to display only the error.

Edited by teraßyte
Link to comment
Share on other sites

Thank you for bringing this issue to our attention! I can confirm this should be further reviewed and I have logged an internal bug report for our development team to investigate and address as necessary, in a future maintenance release.

 

Link to comment
Share on other sites

  • 3 months later...

I noticed one more issue with the code in the first post when the time option is enabled for the min/max checks:

			if( $this->options['time'] )
			{
				$string .=' ' . $this->options['min']->setTimeZone( $timezone )->localeTime( \IPS\Member::loggedIn() );
			}


=======================

			if( $this->options['time'] )
			{
				$string .=' ' . $this->options['max']->setTimeZone( $timezone )->localeTime( \IPS\Member::loggedIn() );
			}

 

The code passes the logged-in member's object as the first parameter to determine the language, but the function accepts that value as the third parameter instead:

	/**
	 * Format the time according to the user's locale (without the date)
	 *
	 * @param	bool				$seconds	If TRUE, will include seconds
	 * @param	bool				$minutes	If TRUE, will include minutes
	 * @param	\IPS\Lang|\IPS\Member|NULL	$memberOrLanguage		The language or member to use, or NULL for currently logged in member
	 * @return	string
	 */
	public function localeTime( $seconds=TRUE, $minutes=TRUE, $memberOrLanguage=NULL )

 

Both localTime() calls should be updated to:

->localeTime( TRUE, TRUE, \IPS\Member::loggedIn() );

 

It would be nice if there were also a new option to decide if we want to show the seconds/minutes in the error, but that's a request rather than a bug. 😋

Link to comment
Share on other sites

Posted (edited)

To add one more issue:

If you set a minimum time (for example: 25 June, 3:00 AM) and select a time lower than 3 AM (for example: 25 June, 2 AM) the field's error says:

The date must be after 06/25/24 03:00:00 AM.

However, if you enter that exact minimum date, the value is processed without error.

 

The error should be updated to clarify that the minimum value is accepted, too:

The date must start from 06/25/24 03:00:00 AM.
===
The date must be at least 06/25/24 03:00:00 AM.

Either option or something else would work.

Edited by teraßyte
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...