Both Timeseries and Spatio-temporal datasets support efficient time-based queries.
Time interval queries
Section titled “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.Sentinel2Msierr = 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
Section titled “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
Section titled “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 datetimefrom 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 intervaldata = 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 intervalvar datapoints []*v1.Sentinel2Msierr = 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
Section titled “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.Sentinel2Msierr = 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 datapointsFirst datapoint time: 2025-06-15T02:31:41.024 +0000 UTCTimezone handling
Section titled “Timezone 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 datetimeimport pytz
# Tokyo has a UTC+9 hours offset, so this is the same as# 2017-01-01 02:45:25.679 UTCtokyo_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 UTClocation, _ := time.LoadLocation("Asia/Tokyo")tokyoTime := query.NewPointInTime(time.Date(2021, 1, 1, 11, 45, 25, 679000000, location))log.Println(tokyoTime)
var datapoints []*v1.Sentinel2Msierr = 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 timestampslog.Printf("First datapoint time: %s", datapoints[0].GetTime().AsTime())Output
2021-01-01 11:45:25.679000+09:00<xarray.Dataset> Size: 725BDimensions: (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 datapointsFirst datapoint time: 2021-01-01 02:45:25.679 +0000 UTC