Querying by temporal extent
Retrieve datapoints that fall within a given time period, with support for precise time intervals, open-ended time ranges, and exact timestamp lookups.
Both Timeseries and Spatio-temporal datasets support efficient time-based queries.
Time interval queries
To query data for a specific time interval, use a tuple in the form (start, end) as the temporal_extent parameter. Both start and end must be TimeScalars, which can be datetime objects or strings in ISO 8601 format.
from tilebox.datasets import Client
client = Client()
sentinel2_msi = client.dataset("open_data.copernicus.sentinel2_msi")
data = sentinel2_msi.query(
collections=["S2A_S2MSI2A", "S2B_S2MSI2A", "S2C_S2MSI2A"],
temporal_extent=("2025-05-01", "2025-06-01"),
show_progress=True,
)
print(f"Queried {data.sizes['time']} data points.")startDate := time.Date(2025, time.May, 1, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, time.June, 1, 0, 0, 0, 0, time.UTC)
interval := query.NewTimeInterval(startDate, endDate)
ctx := context.Background()
client := datasets.NewClient()
dataset, err := client.Datasets.Get(ctx, "open_data.copernicus.sentinel2_msi")
if err != nil {
log.Fatalf("Failed to get dataset: %v", err)
}
collection, err := client.Collections.Get(ctx, dataset.ID, "S2A_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
collectionB, err := client.Collections.Get(ctx, dataset.ID, "S2B_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
collectionC, err := client.Collections.Get(ctx, dataset.ID, "S2C_S2MSI2A")
if err != nil {
log.Fatalf("Failed to get collection: %v", err)
}
var datapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID, collectionB.ID, collectionC.ID),
datasets.WithTemporalExtent(interval),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}
log.Printf("Queried %d datapoints", len(datapoints))Queried 481079 data points.Endpoint inclusivity
A time interval specified as a tuple is interpreted as a half-closed interval. This means the start time is inclusive, and the end time is exclusive.
For instance, using an end time of 2025-06-01 includes data points up to 2025-05-31 23:59:59.999, but excludes those from 2025-06-01 00:00:00.000.
This behavior mimics the Python range function and is useful for chaining time intervals.
import xarray as xr
data = []
for month in [4, 5, 6]:
interval = (f"2025-{month}-01", f"2025-{month+1}-01")
data.append(collection.query(temporal_extent=interval, show_progress=True))
# Concatenate the data into a single dataset, which is equivalent
# to the result of the single request in the code example above.
data = xr.concat(data, dim="time")var datapoints []*v1.Sentinel2Msi
for month := 4; month <= 6; month++ {
startDate := time.Date(2025, month, 1, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2025, month + 1, 1, 0, 0, 0, 0, time.UTC)
var partialDatapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&partialDatapoints,
datasets.WithCollectionIDs(collection.ID),
datasets.WithTemporalExtent(query.NewTimeInterval(startDate, endDate)),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}
// Concatenate the data into a single dataset, which is equivalent
// to the result of the single request in the code example above.
datapoints = append(datapoints, partialDatapoints...)
}Above example demonstrates how to split a large time interval into smaller chunks while loading data in separate requests. Typically, this is not necessary as the datasets client auto-paginates large intervals.
Manual endpoint inclusivity
For greater control over inclusivity of start and end times, you can explicitly specify a TimeInterval. This way you can specify both the start and end times, as well as their inclusivity. Here’s an example of creating equivalent TimeInterval objects in two different ways.
from datetime import datetime
from tilebox.datasets.query import TimeInterval
interval1 = TimeInterval(
datetime(2021, 1, 1), datetime(2023, 1, 1),
end_inclusive=False
)
interval2 = TimeInterval(
# python datetime granularity is in milliseconds
datetime(2021, 1, 1), datetime(2022, 12, 31, 23, 59, 59, 999999),
end_inclusive=True
)
print("Inclusivity is indicated by interval notation: ( and [")
print(interval1)
print(interval2)
print(f"They are equivalent: {interval1 == interval2}")
print(interval2.to_half_open())
# Query data for a time interval
data = collection.query(temporal_extent=interval1, show_progress=True)interval1 := query.TimeInterval{
Start: time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC),
End: time.Date(2023, time.January, 1, 0, 0, 0, 0, time.UTC),
EndInclusive: false,
}
interval2 := query.TimeInterval{
Start: time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC),
// the granularity of time.Time in Go is nanoseconds
End: time.Date(2022, time.December, 31, 23, 59, 59, 999999999, time.UTC),
EndInclusive: true,
}
log.Println("Inclusivity is indicated by interval notation: ( and [")
log.Println(interval1.String())
log.Println(interval2.String())
log.Println("They are equivalent:", interval1.Equal(&interval2))
log.Println(interval2.ToHalfOpen().String())
// Query data for a time interval
var datapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID),
datasets.WithTemporalExtent(interval1),
)Inclusivity is indicated by interval notation: ( and [
[2021-01-01T00:00:00.000 UTC, 2023-01-01T00:00:00.000 UTC)
[2021-01-01T00:00:00.000 UTC, 2022-12-31T23:59:59.999 UTC]
They are equivalent: True
[2021-01-01T00:00:00.000 UTC, 2023-01-01T00:00:00.000 UTC)Time scalar queries
You can query all datapoints linked to a specific timestamp by specifying a TimeScalar as the time query argument. A TimeScalar can be a datetime object or a string in ISO 8601 format.
Here’s how to query a data point at a specific millisecond from a dataset or collection.
data = sentinel2_msi.query(temporal_extent="2025-06-15T02:31:41.024")
print(f"Queried {data.sizes['time']} data points.")
first_timestamp = data.time[0].dt.strftime("%Y-%m-%dT%H:%M:%S.%f").item()
print("First datapoint time:", first_timestamp)temporalExtent := query.NewPointInTime(time.Date(2025, time.June, 15, 2, 31, 41, 024000000, time.UTC))
var datapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithTemporalExtent(temporalExtent),
)
log.Printf("Queried %d datapoints", len(datapoints))
log.Printf("First datapoint time: %s", datapoints[0].GetTime().AsTime())Queried 714 datapoints
First datapoint time: 2025-06-15T02:31:41.024 +0000 UTCTimezone handling
All TimeScalars specified as a string are treated as UTC if they do not include a timezone suffix. If you want to query data for a specific time or time range
in another timezone, it’s recommended to a type that includes timezone information. Tilebox will automatically convert such objects to UTC in order to send the right query requests.
All outputs will always contain UTC timestamps, which will need to be converted again to a different timezone if required.
from datetime import datetime
import pytz
# Tokyo has a UTC+9 hours offset, so this is the same as
# 2017-01-01 02:45:25.679 UTC
tokyo_time = pytz.timezone('Asia/Tokyo').localize(
datetime(2021, 1, 1, 11, 45, 25, 679000)
)
print(tokyo_time)
data = collection.query(temporal_extent=tokyo_time)
print(data)// Tokyo has a UTC+9 hours offset, so this is the same as
// 2017-01-01 02:45:25.679 UTC
location, _ := time.LoadLocation("Asia/Tokyo")
tokyoTime := query.NewPointInTime(time.Date(2021, 1, 1, 11, 45, 25, 679000000, location))
log.Println(tokyoTime)
var datapoints []*v1.Sentinel2Msi
err = client.Datapoints.QueryInto(ctx,
dataset.ID,
&datapoints,
datasets.WithCollectionIDs(collection.ID),
datasets.WithTemporalExtent(tokyoTime),
)
if err != nil {
log.Fatalf("Failed to query datapoints: %v", err)
}
log.Printf("Queried %d datapoints", len(datapoints))
// time is in UTC since API always returns UTC timestamps
log.Printf("First datapoint time: %s", datapoints[0].GetTime().AsTime())Output
2021-01-01 11:45:25.679000+09:00
<xarray.Dataset> Size: 725B
Dimensions: (time: 1, latlon: 2)
Coordinates:
ingestion_time (time) datetime64[ns] 8B 2024-06-21T11:03:33.852435
id (time) <U36 144B '015957ea-d82f-e454-34ab-a87603ee...
* time (time) datetime64[ns] 8B 2017-01-01T02:45:25.679000
* latlon (latlon) <U9 72B 'latitude' 'longitude'
Data variables:
...[2021-01-01 11:45:25.679 +0900 JST, 2021-01-01 11:45:25.679 +0900 JST]
Queried 1 datapoints
First datapoint time: 2021-01-01 02:45:25.679 +0000 UTC