Skip to content

Commit 37c2f15

Browse files
authored
Merge pull request #158 from millyhung/feat/interval-time-range-support
feat: Add Interval class with RFC 3339 validation and GoogleSearch time filtering
2 parents 2bbd86a + e4bcaca commit 37c2f15

2 files changed

Lines changed: 138 additions & 6 deletions

File tree

src/Data/GoogleSearch.php

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,56 @@
44

55
namespace Gemini\Data;
66

7+
use InvalidArgumentException;
78
use stdClass;
89

910
/**
10-
* This type has no fields.
1111
* GoogleSearch tool type. Tool to support Google Search in Model. Powered by Google.
1212
*
1313
* https://ai.google.dev/api/caching#GoogleSearch
1414
*/
1515
final class GoogleSearch
1616
{
17+
/**
18+
* @param Interval|null $timeRangeFilter Optional. Filter search results to a specific time range. If customers set a start time, they must set an end time (and vice versa).
19+
*
20+
* @throws InvalidArgumentException When timeRangeFilter has only startTime or only endTime specified
21+
*/
1722
public function __construct(
18-
) {}
23+
public readonly ?Interval $timeRangeFilter = null,
24+
) {
25+
if ($this->timeRangeFilter !== null) {
26+
$hasStartTime = $this->timeRangeFilter->startTime !== null;
27+
$hasEndTime = $this->timeRangeFilter->endTime !== null;
1928

20-
public static function from(): self
29+
// GoogleSearch requires both start and end time to be set together
30+
if ($hasStartTime !== $hasEndTime) {
31+
throw new InvalidArgumentException('In GoogleSearch timeRangeFilter, if you set a start time, you must set an end time (and vice versa)');
32+
}
33+
}
34+
}
35+
36+
/**
37+
* @param array{timeRangeFilter?: array{startTime?: ?string, endTime?: ?string}} $attributes
38+
*/
39+
public static function from(array $attributes = []): self
2140
{
22-
return new self;
41+
return new self(
42+
timeRangeFilter: isset($attributes['timeRangeFilter']) ? Interval::from($attributes['timeRangeFilter']) : null,
43+
);
2344
}
2445

25-
public function toArray(): stdClass
46+
/**
47+
* @return stdClass|array{timeRangeFilter: array{startTime?: ?string, endTime?: ?string}|stdClass}
48+
*/
49+
public function toArray(): stdClass|array
2650
{
27-
return new stdClass;
51+
if ($this->timeRangeFilter === null) {
52+
return new stdClass;
53+
}
54+
55+
return [
56+
'timeRangeFilter' => $this->timeRangeFilter->toArray(),
57+
];
2858
}
2959
}

src/Data/Interval.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Gemini\Data;
6+
7+
use DateTime;
8+
use InvalidArgumentException;
9+
use stdClass;
10+
11+
/**
12+
* Represents a time interval, encoded as a Timestamp start (inclusive) and a Timestamp end (exclusive).
13+
*
14+
* The start must be less than or equal to the end. When the start equals the end, the interval is empty (matches no time). When both start and end are unspecified, the interval matches any time.
15+
*
16+
* https://ai.google.dev/api/caching#Interval
17+
*/
18+
final class Interval
19+
{
20+
/**
21+
* @param ?string $startTime Optional. Inclusive start of the interval. If specified, a Timestamp matching this interval will have to be the same or after the start. Uses RFC 3339 format. Examples: "2014-10-02T15:01:23Z", "2014-10-02T15:01:23.045123456Z" or "2014-10-02T15:01:23+05:30".
22+
* @param ?string $endTime Optional. Exclusive end of the interval. If specified, a Timestamp matching this interval will have to be before the end. Uses RFC 3339 format. Examples: "2014-10-02T15:01:23Z", "2014-10-02T15:01:23.045123456Z" or "2014-10-02T15:01:23+05:30".
23+
*
24+
* @throws InvalidArgumentException When timestamp format is invalid or start time is after end time
25+
*/
26+
public function __construct(
27+
public readonly ?string $startTime,
28+
public readonly ?string $endTime,
29+
) {
30+
if ($this->startTime !== null && ! $this->isValidTimestamp($this->startTime)) {
31+
throw new InvalidArgumentException('startTime must be in RFC 3339 timestamp format');
32+
}
33+
34+
if ($this->endTime !== null && ! $this->isValidTimestamp($this->endTime)) {
35+
throw new InvalidArgumentException('endTime must be in RFC 3339 timestamp format');
36+
}
37+
38+
if ($this->startTime !== null && $this->endTime !== null) {
39+
$startTimestamp = strtotime($this->startTime);
40+
$endTimestamp = strtotime($this->endTime);
41+
42+
if ($startTimestamp === false || $endTimestamp === false) {
43+
throw new InvalidArgumentException('Invalid timestamp format');
44+
}
45+
46+
if ($startTimestamp > $endTimestamp) {
47+
throw new InvalidArgumentException('startTime must be less than or equal to endTime');
48+
}
49+
}
50+
}
51+
52+
/**
53+
* @param array{startTime?: ?string, endTime?: ?string} $attributes
54+
*/
55+
public static function from(array $attributes): self
56+
{
57+
return new self($attributes['startTime'] ?? null, $attributes['endTime'] ?? null);
58+
}
59+
60+
/**
61+
* @return stdClass|array{startTime?: ?string, endTime?: ?string}
62+
*/
63+
public function toArray(): stdClass|array
64+
{
65+
$data = [];
66+
67+
if ($this->startTime !== null) {
68+
$data['startTime'] = $this->startTime;
69+
}
70+
71+
if ($this->endTime !== null) {
72+
$data['endTime'] = $this->endTime;
73+
}
74+
75+
return empty($data) ? new stdClass : $data;
76+
}
77+
78+
/**
79+
* Validates if a string is in RFC 3339 timestamp format
80+
*/
81+
private function isValidTimestamp(string $timestamp): bool
82+
{
83+
// RFC 3339 regex pattern
84+
$pattern = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/';
85+
86+
if (! preg_match($pattern, $timestamp)) {
87+
return false;
88+
}
89+
90+
// Handle Z timezone suffix by converting to +00:00
91+
$normalizedTimestamp = str_replace('Z', '+00:00', $timestamp);
92+
93+
// Try with extended format first (includes microseconds)
94+
$dateTime = DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $normalizedTimestamp);
95+
if ($dateTime === false) {
96+
// Try with standard RFC3339 format
97+
$dateTime = DateTime::createFromFormat(DateTime::RFC3339, $normalizedTimestamp);
98+
}
99+
100+
return $dateTime !== false;
101+
}
102+
}

0 commit comments

Comments
 (0)