Enhancing neomodel DateProperty and DateTimeProperty in neomodel for better string handling and ISO format storage

Hello Neo4j community,

I recently encountered some issues with the DateProperty and DateTimeProperty classes in neomodel when trying to handle string inputs and store datetime values in a more readable format.
I recently encountered an issue with the DateProperty class in neomodel when trying to save date values as strings. Specifically, I was getting the following error:

neomodel.exceptions.DeflateError: Attempting to deflate property 'due_date' on None of class 'Goal': datetime.date object expected, got '2025-12-31'

This error occurred because the deflate method in DateProperty was strictly expecting a date object, but I was trying to save a string representation of a date.

To resolve this, I modified the DateProperty class. Here's the change I made to the deflate method:

1. DateProperty Changes:

The original deflate method in DateProperty was strictly expecting a date object, which caused issues when trying to save string date inputs. I modified the deflate method to handle string inputs as well:

@validator
def deflate(self, value):
    if isinstance(value, str): # Added this line
        try:
            value = datetime.strptime(value, "%Y-%m-%d").date()
        except ValueError:
            raise ValueError(f"Invalid date format. Expected YYYY-MM-DD, got {value}")
    if not isinstance(value, date):
        msg = f"datetime.date object expected, got {repr(value)}"
        raise ValueError(msg)
    return value.isoformat()

With this change, I can now use string representations of dates (like '2025-12-31') when setting DateProperty fields, and neomodel correctly converts and stores them as date objects.

2. DateTimeProperty Changes:

The original DateTimeProperty was storing datetime values as Unix timestamps, which made them hard to read directly in Neo4j. I modified this class to store datetime values as ISO format strings and to handle string inputs (same as above). Storing as ISO formats might not be as universally desired, but the string conversion to date, as above, should prove useful:

@validator
def inflate(self, value):
    if isinstance(value, str):
        try:
            return datetime.fromisoformat(value).replace(tzinfo=pytz.utc)
        except ValueError:
            raise ValueError(f"Invalid ISO format datetime string: {value}")
    elif isinstance(value, datetime):
        return value.replace(tzinfo=pytz.utc)
    else:
        raise TypeError(f"String or datetime object expected, got {type(value)}")

@validator
def deflate(self, value):
    if isinstance(value, str):
        try:
            value = datetime.fromisoformat(value)
        except ValueError:
            raise ValueError(f"Invalid ISO format datetime string: {value}")
    if not isinstance(value, datetime):
        raise ValueError(f"datetime object expected, got {type(value)}.")
    
    if value.tzinfo is None:
        if config.FORCE_TIMEZONE:
            raise ValueError(f"Error deflating {value}: No timezone provided.")
        else:
            value = value.replace(tzinfo=pytz.utc)
    else:
        value = value.astimezone(pytz.utc)
    return value.isoformat()

These changes allow the DateTimeProperty to:

  1. Accept both string inputs in ISO format and datetime objects
  2. Store datetime values as readable ISO format strings in Neo4j
  3. Ensure all datetime values are in UTC for consistency

Has anyone else encountered similar issues or found alternative solutions? I'd be interested in hearing about other approaches or potential improvements to this solution.

Hello and thank you for your post.

Two comments here, as one of the neomodel maintainers :

  1. (DateProperty) thank you for this, can you open a PR on neomodel's GitHub for this ? And ideally add a test in the test_properties file, there are inflate/deflate tests for dates there
  2. (DateTimeProperty) This is actually already catered for by the DateTimeFormatProperty, I think. I didn't develop this, but I think the idea is that DateTimeProperty uses low-level representation (timestamps), DateTimeFormatProperty lets you define your format, and the new DateTimeNeo4jFormatProperty uses the Neo4j native format
1 Like

Hi Marius,

Thanks for your comment and apologies for the delay.

I have added the change, a test, and created a PR and fork, it is called fix-datetime. Thank you!