Skip to content

Commit 4f3e81b

Browse files
committed
Served ads to users should check dates by their local time
1 parent 61fc036 commit 4f3e81b

6 files changed

Lines changed: 81 additions & 8 deletions

File tree

ad/manager.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class manager
1818
/** @var \phpbb\config\config */
1919
protected $config;
2020

21+
/** @var \phpbb\user */
22+
protected $user;
23+
2124
/** @var string */
2225
protected $ads_table;
2326

@@ -32,14 +35,16 @@ class manager
3235
*
3336
* @param \phpbb\db\driver\driver_interface $db DB driver interface
3437
* @param \phpbb\config\config $config Config object
38+
* @param \phpbb\user $user User object
3539
* @param string $ads_table Ads table
3640
* @param string $ad_locations_table Ad locations table
3741
* @param string $ad_group_table Ad group table
3842
*/
39-
public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, $ads_table, $ad_locations_table, $ad_group_table)
43+
public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\user $user, $ads_table, $ad_locations_table, $ad_group_table)
4044
{
4145
$this->db = $db;
4246
$this->config = $config;
47+
$this->user = $user;
4348
$this->ads_table = $ads_table;
4449
$this->ad_locations_table = $ad_locations_table;
4550
$this->ad_group_table = $ad_group_table;
@@ -78,15 +83,19 @@ public function get_ads($ad_locations, $user_groups, $non_content_page = false)
7883
$sql_where_non_content = $non_content_page ? 'AND a.ad_content_only = 0' : '';
7984
$sql_where_user_groups = !empty($user_groups) ? 'AND NOT EXISTS (SELECT ag.group_id FROM ' . $this->ad_group_table . ' ag WHERE ag.ad_id = a.ad_id AND ' . $this->db->sql_in_set('ag.group_id', $user_groups) . ')' : '';
8085

86+
// Get user's current time and convert to UTC equivalent for comparison
87+
$user_now = $this->user->create_datetime();
88+
$sql_time = $this->user->get_timestamp_from_format('Y-m-d H:i:s', $user_now->format('Y-m-d H:i:s'), new \DateTimeZone('UTC'));
89+
8190
$sql = 'SELECT al.location_id, a.ad_id, a.ad_code, a.ad_centering
8291
FROM ' . $this->ad_locations_table . ' al
8392
LEFT JOIN ' . $this->ads_table . ' a
8493
ON (al.ad_id = a.ad_id)
8594
WHERE a.ad_enabled = 1
8695
AND (a.ad_start_date = 0
87-
OR a.ad_start_date < ' . time() . ')
96+
OR a.ad_start_date <= ' . $sql_time . ')
8897
AND (a.ad_end_date = 0
89-
OR a.ad_end_date > ' . time() . ")
98+
OR a.ad_end_date > ' . $sql_time . ")
9099
$sql_where_views
91100
$sql_where_clicks
92101
$sql_where_non_content

config/services.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ services:
99
arguments:
1010
- '@dbal.conn'
1111
- '@config'
12+
- '@user'
1213
- '%phpbb.ads.tables.ads%'
1314
- '%phpbb.ads.tables.ad_locations%'
1415
- '%phpbb.ads.tables.ad_group%'

controller/admin_input.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,7 @@ protected function validate_date($date, $type)
342342
$timestamp = $datetime->getTimestamp();
343343

344344
// Compare against user's current day to avoid timezone confusion
345-
$user_today = new \DateTime('today', $this->user->timezone);
346-
if ($timestamp < $user_today->getTimestamp())
345+
if ($timestamp < $this->user->create_datetime('today')->getTimestamp())
347346
{
348347
$this->errors[] = 'AD_' . $type . '_DATE_INVALID';
349348
return 0;

tests/ad/ad_base.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class ad_base extends \phpbb_database_test_case
1818
/** @var \phpbb\config\config */
1919
protected $config;
2020

21+
/** @var \phpbb\user */
22+
protected $user;
23+
2124
/** @var string */
2225
protected $ads_table;
2326

@@ -52,6 +55,11 @@ protected function setUp(): void
5255

5356
$this->db = $this->new_dbal();
5457
$this->config = new \phpbb\config\config(array());
58+
$this->user = $this->createMock('\phpbb\user');
59+
$this->user->timezone = new \DateTimeZone('UTC');
60+
$current_time = new \DateTime('now', new \DateTimeZone('UTC'));
61+
$this->user->method('create_datetime')->willReturn($current_time);
62+
$this->user->method('get_timestamp_from_format')->willReturn($current_time->getTimestamp());
5563
$this->ads_table = 'phpbb_ads';
5664
$this->ad_locations_table = 'phpbb_ad_locations';
5765
$this->ad_group_table = 'phpbb_ad_group';
@@ -64,6 +72,6 @@ protected function setUp(): void
6472
*/
6573
public function get_manager()
6674
{
67-
return new \phpbb\ads\ad\manager($this->db, $this->config, $this->ads_table, $this->ad_locations_table, $this->ad_group_table);
75+
return new \phpbb\ads\ad\manager($this->db, $this->config, $this->user, $this->ads_table, $this->ad_locations_table, $this->ad_group_table);
6876
}
6977
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
/**
3+
*
4+
* Advertisement management. An extension for the phpBB Forum Software package.
5+
*
6+
* @copyright (c) 2025 phpBB Limited <https://www.phpbb.com>
7+
* @license GNU General Public License, version 2 (GPL-2.0)
8+
*
9+
*/
10+
11+
namespace phpbb\ads\tests\ad;
12+
13+
class get_ads_by_timezone_test extends ad_base
14+
{
15+
public function test_timezone_boundary_behavior()
16+
{
17+
$this->create_test_ad(strtotime('2020-01-01 00:00:00 UTC'));
18+
19+
// Users at Jan 1 midnight in their timezone should see Jan 1 ad
20+
$this->assert_user_sees_ad('Pacific/Honolulu', '2020-01-01 00:00:00', true);
21+
$this->assert_user_sees_ad('Asia/Tokyo', '2020-01-01 00:00:00', true);
22+
23+
// User before Jan 1 in their timezone should not see Jan 1 ad
24+
$this->assert_user_sees_ad('Pacific/Honolulu', '2019-12-31 23:59:59', false);
25+
$this->assert_user_sees_ad('Asia/Tokyo', '2019-12-31 23:59:59', false);
26+
}
27+
28+
private function create_test_ad($start_timestamp)
29+
{
30+
$ad_data = [
31+
'ad_id' => 99,
32+
'ad_name' => 'Test',
33+
'ad_note' => 'Test',
34+
'ad_code' => 'Test Ad',
35+
'ad_enabled' => 1,
36+
'ad_start_date' => $start_timestamp,
37+
'ad_end_date' => 0,
38+
'ad_priority' => 5,
39+
];
40+
$this->db->sql_query('INSERT INTO ' . $this->ads_table . ' ' . $this->db->sql_build_array('INSERT', $ad_data));
41+
$this->db->sql_query('INSERT INTO ' . $this->ad_locations_table . ' ' . $this->db->sql_build_array('INSERT', ['ad_id' => 99, 'location_id' => 'test_location']));
42+
}
43+
44+
private function assert_user_sees_ad($timezone, $datetime, $should_see)
45+
{
46+
$test_time = new \DateTime($datetime, new \DateTimeZone($timezone));
47+
$utc_timestamp = strtotime($test_time->format('Y-m-d H:i:s') . ' UTC');
48+
49+
$this->user = $this->createMock('\phpbb\user');
50+
$this->user->method('create_datetime')->willReturn($test_time);
51+
$this->user->method('get_timestamp_from_format')->willReturn($utc_timestamp);
52+
53+
$ads = $this->get_manager()->get_ads(['test_location'], []);
54+
self::assertCount($should_see ? 1 : 0, $ads);
55+
}
56+
}

tests/event/main_listener_base.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ protected function setUp(): void
138138
->getMock();
139139
$this->user = $user;
140140
$this->config = new \phpbb\config\config(array('phpbb_ads_adblocker_message' => '0'));
141-
$this->manager = new \phpbb\ads\ad\manager($this->new_dbal(), $this->config, $this->ads_table, $this->ad_locations_table, $this->ad_group_table);
141+
$this->manager = new \phpbb\ads\ad\manager($this->new_dbal(), $this->config, $this->user, $this->ads_table, $this->ad_locations_table, $this->ad_group_table);
142142
$this->location_manager = new \phpbb\ads\location\manager($location_types);
143-
$this->controller_helper = $this->controller_helper = $this->getMockBuilder('\phpbb\controller\helper')
143+
$this->controller_helper = $this->getMockBuilder('\phpbb\controller\helper')
144144
->disableOriginalConstructor()
145145
->getMock();
146146
$this->request = $request;

0 commit comments

Comments
 (0)