'how to replace datetime fields in Python DatetimeWithNanoseconds object

In Python, I need to set a timestamp field in a google-cloud firestore document.

I know that the firestore timestamp field shall be set using google.api_core.datetime_helpers.DatetimeWithNanoseconds, which is a subclass of Datetime

I need to set the now() timestamp with a specified minute field and dial it back by certain hours as given below

                'cursor_specs' = {
                    # specs to construct the next cursor
                    'minute_tick': 0,
                    'hours_dial_back': 1,
                },

Because DatetimeWithNanoseconds is a subclass of Datetime and supposedly inherits all its function, so I tried to use now() and replace() directly with DatetimeWithNanoseconds

from google.api_core.datetime_helpers import DatetimeWithNanoseconds

time_cursor = DatetimeWithNanoseconds.now().replace(minute=cursor_specs['minute_tick'],
                                    second=0, microsecond=0
                                ) - timedelta(hours=cursor_specs['hours_dial_back']

fs_model = fs.document(model["name"]).set({'time_cursor': time_cursor})

It failed with an error

  File "/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/_helpers.py", line 219, in <dictcomp>
    return {key: encode_value(value) for key, value in values_dict.items()}
  File "/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/_helpers.py", line 173, in encode_value
    return document.Value(timestamp_value=value.timestamp_pb())
  File "/usr/local/lib/python3.8/site-packages/google/api_core/datetime_helpers.py", line 271, in timestamp_pb
    nanos = self._nanosecond or self.microsecond * 1000
AttributeError: _nanosecond


Solution 1:[1]

I posted this question because I struggled with it so I hope my finding could help others.

The best solution I found is to use now() and replace() to set a regular Datetime object first, and then convert it to a DatetimeWithNanoseconds object.

from google.api_core.datetime_helpers import DatetimeWithNanoseconds
from google.api_core.datetime_helpers import to_rfc3339

time_cursor_in_datetime = datetime.now().replace(
                                minute=model['cursor']['next']['minute_tick'],
                                second=0, microsecond=0
                            ) - timedelta(hours=model['cursor']['next']['hours_dial_back'])

time_cursor =  DatetimeWithNanoseconds.from_rfc3339(
                                to_rfc3339(time_cursor_in_datetime)
                            )


fs_model = fs.document(model["name"]).set({'time_cursor': time_cursor})

Solution 2:[2]

What worked for me is using the default=str option in the json.dumps() function:

import json

# Firestore API code to get data 
...

firestoreResult = docRef.get().to_dict()  # DatetimeWithNanoseconds format 

outputWithTimestamp = json.dumps(, indent=4, default=str)

All DatetimeWithNanoseconds will now show up in the format "my_timestamp": "2022-02-26 09:07:47.223000+00:00".

Google Firestore example of to_dict(): https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Hui Zheng
Solution 2 engineer-x