From 813cca6d783cda46538778c8d038c59d3e5122c1 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 27 Nov 2023 16:25:08 +0100 Subject: [PATCH 1/2] Shapefile: recogize ' 0' as a null date Fixes https://lists.osgeo.org/pipermail/gdal-dev/2023-November/058010.html --- ogr/ogrsf_frmts/shape/dbfopen.c | 7 ++++++- ogr/ogrsf_frmts/shape/shape2ogr.cpp | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ogr/ogrsf_frmts/shape/dbfopen.c b/ogr/ogrsf_frmts/shape/dbfopen.c index 44befaa25cdd..7b3b7a5d5c5d 100644 --- a/ogr/ogrsf_frmts/shape/dbfopen.c +++ b/ogr/ogrsf_frmts/shape/dbfopen.c @@ -1165,7 +1165,12 @@ static bool DBFIsValueNULL(char chType, const char *pszValue) case 'D': /* NULL date fields have value "00000000" */ - return strncmp(pszValue, "00000000", 8) == 0; + /* Some DBF files have fields filled with spaces */ + /* (trimmed by DBFReadStringAttribute) to indicate null */ + /* values for dates (#4265). */ + /* And others have ' 0': https://lists.osgeo.org/pipermail/gdal-dev/2023-November/058010.html */ + return strncmp(pszValue, "00000000", 8) == 0 || + strcmp(pszValue, " ") == 0 || strcmp(pszValue, "0") == 0; case 'L': /* NULL boolean fields have value "?" */ diff --git a/ogr/ogrsf_frmts/shape/shape2ogr.cpp b/ogr/ogrsf_frmts/shape/shape2ogr.cpp index cb1d009d7cf6..6a01f514afc9 100644 --- a/ogr/ogrsf_frmts/shape/shape2ogr.cpp +++ b/ogr/ogrsf_frmts/shape/shape2ogr.cpp @@ -1447,12 +1447,6 @@ OGRFeature *SHPReadOGRFeature(SHPHandle hSHP, DBFHandle hDBF, const char *const pszDateValue = DBFReadStringAttribute(hDBF, iShape, iField); - // Some DBF files have fields filled with spaces - // (trimmed by DBFReadStringAttribute) to indicate null - // values for dates (#4265). - if (pszDateValue[0] == '\0') - continue; - OGRField sFld; memset(&sFld, 0, sizeof(sFld)); From 72e2c6d417520c17d73342ac9e36341e0d895f1b Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 27 Nov 2023 16:36:14 +0100 Subject: [PATCH 2/2] Shapefile: fix writing an invalid "0000/00/00" date --- autotest/ogr/ogr_shape.py | 25 +++++++++++++++++++++++++ ogr/ogrsf_frmts/shape/shape2ogr.cpp | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/autotest/ogr/ogr_shape.py b/autotest/ogr/ogr_shape.py index 0fca09602ecd..6e85f49fd8eb 100755 --- a/autotest/ogr/ogr_shape.py +++ b/autotest/ogr/ogr_shape.py @@ -5819,3 +5819,28 @@ def test_ogr_shape_write_arrow_IF_FID_NOT_PRESERVED_ERROR(tmp_vsimem): lyr.WriteArrowBatch( schema, array, ["FID=OGC_FID", "IF_FID_NOT_PRESERVED=ERROR"] ) + + +############################################################################### +# Test writing an invalid "0000/00/00" date + + +@gdaltest.enable_exceptions() +def test_ogr_shape_write_date_0000_00_00(tmp_vsimem): + + filename = tmp_vsimem / "test_ogr_shape_write_date_0000_00_00.shp" + ds = gdal.GetDriverByName("ESRI Shapefile").Create( + filename, 0, 0, 0, gdal.GDT_Unknown + ) + lyr = ds.CreateLayer("test") + lyr.CreateField(ogr.FieldDefn("date", ogr.OFTDate)) + f = ogr.Feature(lyr.GetLayerDefn()) + f["date"] = "0000/00/00" + lyr.CreateFeature(f) + f = None + ds.Close() + + ds = ogr.Open(filename) + lyr = ds.GetLayer(0) + f = lyr.GetNextFeature() + assert f.IsFieldNull("date") diff --git a/ogr/ogrsf_frmts/shape/shape2ogr.cpp b/ogr/ogrsf_frmts/shape/shape2ogr.cpp index 6a01f514afc9..7c44f5aa6e75 100644 --- a/ogr/ogrsf_frmts/shape/shape2ogr.cpp +++ b/ogr/ogrsf_frmts/shape/shape2ogr.cpp @@ -1757,6 +1757,12 @@ OGRErr SHPWriteOGRFeature(SHPHandle hSHP, DBFHandle hDBF, CE_Warning, CPLE_NotSupported, "Year < 0 or > 9999 is not a valid date for shapefile"); } + else if (psField->Date.Year == 0 && psField->Date.Month == 0 && + psField->Date.Day == 0) + { + DBFWriteNULLAttribute( + hDBF, static_cast(poFeature->GetFID()), iField); + } else { DBFWriteIntegerAttribute(