Skip to content

Commit ddc2a5e

Browse files
authored
fix: permissively deserialize start and end datetime (#838)
## Closes - #837 ## Checklist Delete any checklist items that do not apply (e.g. if your change is minor, it may not require documentation updates). - [x] Unit tests - [x] Git history is linear - [x] Commit messages are descriptive - [x] (optional) Git commit messages follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) - [x] Code is formatted (`cargo fmt`) - [x] `cargo test` - [x] Changes are added to the CHANGELOG <!-- markdownlint-disable-file MD041 -->
1 parent f30e64a commit ddc2a5e

File tree

3 files changed

+176
-2
lines changed

3 files changed

+176
-2
lines changed

crates/core/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1414

1515
- Switch to zstd(15) for default geoparquet compression ([#834](https://github.com/stac-utils/rustac/pull/834))
1616

17+
### Fixed
18+
19+
- Permissively deserialize start and end datetimes ([#838](https://github.com/stac-utils/rustac/pull/838))
20+
1721
## [0.13.2] - 2025-10-22
1822

1923
### Fixed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
{
2+
"id": "0fbe7681-e8d4-4480-bd45-eb008c59f72d",
3+
"bbox": [-116.897638, 50.195065, -114.657376, 51.737401],
4+
"type": "Feature",
5+
"links": [
6+
{
7+
"rel": "collection",
8+
"type": "application/json",
9+
"href": "https://www.eodms-sgdot.nrcan-rncan.gc.ca/stac/collections/rcm-ard"
10+
},
11+
{
12+
"rel": "parent",
13+
"type": "application/json",
14+
"href": "https://www.eodms-sgdot.nrcan-rncan.gc.ca/stac/collections/rcm-ard"
15+
},
16+
{
17+
"rel": "root",
18+
"type": "application/json",
19+
"href": "https://www.eodms-sgdot.nrcan-rncan.gc.ca/stac/"
20+
},
21+
{
22+
"rel": "self",
23+
"type": "application/geo+json",
24+
"href": "https://www.eodms-sgdot.nrcan-rncan.gc.ca/stac/collections/rcm-ard/items/0fbe7681-e8d4-4480-bd45-eb008c59f72d"
25+
}
26+
],
27+
"assets": {
28+
"rl": {
29+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RL.tif",
30+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
31+
"roles": ["data", "gamma()"],
32+
"title": "Backscatter Measurements RL Polarization"
33+
},
34+
"rr": {
35+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RR.tif",
36+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
37+
"roles": ["data", "gamma()"],
38+
"title": "Backscatter Measurements RR Polarization"
39+
},
40+
"rrrl": {
41+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RRRL.tif",
42+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
43+
"roles": ["data", "covmat"],
44+
"title": "Normalized Polarimetric Radar Covariance Matrix (CovMat)"
45+
},
46+
"metadata": {
47+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_product.xml",
48+
"type": "text/xml",
49+
"roles": ["metadata"],
50+
"title": "ARD Product Metadata"
51+
},
52+
"data_mask": {
53+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_bitmask.tif",
54+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
55+
"roles": ["data", "data-mask"],
56+
"title": "Data Mask Image"
57+
},
58+
"thumbnail": {
59+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RR_quickLook.png",
60+
"type": "image/png",
61+
"roles": ["thumbnail"],
62+
"title": "Thumbnail"
63+
},
64+
"eula_license": {
65+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM_EULA_PUBLIC_v1-0_20191213_UNCLASSIFIED.pdf",
66+
"type": "application/pdf",
67+
"roles": ["license"],
68+
"title": "EULA"
69+
},
70+
"rl_thumbnail": {
71+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RL_quickLook.tif",
72+
"type": "image/tiff; application=geotiff",
73+
"title": "Backscatter RL Polarization Quicklook"
74+
},
75+
"rr_thumbnail": {
76+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_RR_quickLook.tif",
77+
"type": "image/tiff; application=geotiff",
78+
"title": "Backscatter RR Polarization Quicklook"
79+
},
80+
"local_inc_angle": {
81+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_localIncAngle.tif",
82+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
83+
"roles": ["data", "local-incidence-angle"],
84+
"title": "Local Incident Angle Image"
85+
},
86+
"gamma_to_sigma_ratio": {
87+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_gammaToSigmaRatio.tif",
88+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
89+
"roles": ["data"],
90+
"title": "Gamma-to-Sigma Ratio Image"
91+
},
92+
"local_contributing_area": {
93+
"href": "https://rcm-ceos-ard.s3.ca-central-1.amazonaws.com/MLC/2025/11/13/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC/RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_localContributingArea.tif",
94+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
95+
"roles": ["data"],
96+
"title": "Scattering Area Image"
97+
}
98+
},
99+
"geometry": {
100+
"type": "Polygon",
101+
"coordinates": [
102+
[
103+
[-116.897638, 51.493118],
104+
[-115.082447, 51.737401],
105+
[-114.657376, 50.435388],
106+
[-116.418298, 50.195065],
107+
[-116.897638, 51.493118]
108+
]
109+
]
110+
},
111+
"collection": "rcm-ard",
112+
"properties": {
113+
"ard_proc_version": "1.2.0",
114+
"ceosard:specification": "NRB - POL",
115+
"ceosard:specification_version": "1.0",
116+
"ceosard:type": "radar",
117+
"created": "2025-11-13T12:17:01.022440+00:00",
118+
"datetime": "2025-11-13T01:18:44.291467",
119+
"end_datetime": "2025-11-13T01:19:05.574225",
120+
"order_key": "RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC",
121+
"processing:facility": "SHUB",
122+
"processing:level": "l2",
123+
"processing:software": { "RCM PGS": "7.06" },
124+
"product_column_spacing": 20.0,
125+
"product_row_spacing": 20.0,
126+
"raster:bands": [
127+
{ "nodata": 0, "sampling": "RasterPixelIsArea", "data_type": "Float" }
128+
],
129+
"rcm_uuid": "825c5e7c-292e-53bb-89e2-8ec711339010",
130+
"sar:center_frequency": 5.405,
131+
"sar:frequency_band": "C",
132+
"sar:instrument_mode": "Medium Resolution 30m",
133+
"sar:looks_azimuth": 2,
134+
"sar:looks_range": 2,
135+
"sar:observation_direction": "Right",
136+
"sar:pixel_spacing_azimuth": 22.7,
137+
"sar:pixel_spacing_range": 7.9,
138+
"sar:polarizations": ["CH", "CV", "XC"],
139+
"sar:product_type": "MLC",
140+
"sat:absolute_orbit": 34986.0,
141+
"sat:orbit_state": "Ascending",
142+
"sat:relative_orbit": 121,
143+
"sequence_id": 34170555,
144+
"start_datetime": "2025-11-13T01:18:44.291467",
145+
"title": "RCM2_OK3685761_PK3902965_1_SC30MCPA_20251113_011844_CH_CV_MLC",
146+
"version": "1.2"
147+
},
148+
"stac_version": "1.0.0",
149+
"stac_extensions": [
150+
"https://stac-extensions.github.io/sar/v1.0.0/schema.json",
151+
"https://stac-extensions.github.io/processing/v1.1.0/schema.json",
152+
"https://stac-extensions.github.io/sat/v1.0.0/schema.json",
153+
"https://stac-extensions.github.io/raster/v1.1.0/schema.json",
154+
"https://stac-extensions.github.io/ceos-ard/v0.2.0/schema.json",
155+
"https://stac-extensions.github.io/version/v1.2.0/schema.json"
156+
]
157+
}

crates/core/src/item.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ pub struct Properties {
188188
/// This is a [common
189189
/// metadata](https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md)
190190
/// field.
191-
#[serde(skip_serializing_if = "Option::is_none")]
191+
#[serde(
192+
skip_serializing_if = "Option::is_none",
193+
default,
194+
deserialize_with = "deserialize_datetime_permissively"
195+
)]
192196
pub start_datetime: Option<DateTime<Utc>>,
193197

194198
/// The last or end date and time for the Item, in UTC.
@@ -198,7 +202,11 @@ pub struct Properties {
198202
/// This is a [common
199203
/// metadata](https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md)
200204
/// field.
201-
#[serde(skip_serializing_if = "Option::is_none")]
205+
#[serde(
206+
skip_serializing_if = "Option::is_none",
207+
default,
208+
deserialize_with = "deserialize_datetime_permissively"
209+
)]
202210
pub end_datetime: Option<DateTime<Utc>>,
203211

204212
/// A human readable title describing the Item.
@@ -931,4 +939,9 @@ mod tests {
931939
fn read_invalid_item_datetime() {
932940
let _: Item = crate::read("data/invalid-item-datetime.json").unwrap();
933941
}
942+
943+
#[test]
944+
fn read_invalid_item_datetimes() {
945+
let _: Item = crate::read("data/invalid-datetimes.json").unwrap();
946+
}
934947
}

0 commit comments

Comments
 (0)