问题
I am trying to convert a firestore timestamp into milliseconds or a date format using python to perform a calculation using this date. Trying to parse it to date returns.
TypeError: strptime() argument 1 must be str, not DatetimeWithNanoseconds.
How can I convert a Firestore timestamp into milliseconds/date format in python? I need this to perform a calculation.
docs = db.collection(u'status').stream()
for doc in docs:
doc_data = doc.to_dict()
# TypeError: strptime() argument 1 must be str, not DatetimeWithNanoseconds
utc_time = datetime.strptime(doc_data['start_date'], "%Y-%m-%dT%H:%M:%S.%fZ")
回答1:
It turns out that DatetimeWithNanoseconds inherits from datetime.datetime already
回答2:
As implied by Luke's answer, you can use all of the usual methods of datetime with values of DatetimeWithNanoseconds, because it's a subclass. So for example you can do doc_data['start_date'].timestamp() to get the time in seconds since 1970-01-01 (including fractional seconds).
However, from experimenting and looking at the implementation of DatetimeWithNanoseconds, I've noticed some issues that developers should be aware of (and possibly that Google should fix).
DatetimeWithNanoseconds adds a read-only nanosecond property to datetime, but keeps its microsecond property. The intent appears to be that you can set one or the other, but not both, when creating the object, and then read either one. When using the DatetimeWithNanoseconds constructor, you can either set microsecond or nanosecond, but not both (if setting nanosecond, it must be specified as a keyword argument). If you try to set both, it throws an exception. If you only set nanosecond, it correctly sets microsecond to nanosecond / 1000, rounded down. If you only set microsecond, it sets microsecond, but nanosecond is set to zero.
>>> from google.api_core.datetime_helpers import DatetimeWithNanoseconds
>>> d1 = DatetimeWithNanoseconds(2020, 6, 22, 17, 1, 30, 12345)
>>> d1.microsecond
12345
>>> d1.nanosecond
0
>>> d2 = DatetimeWithNanoseconds(2020, 6, 22, 17, 1, 30, nanosecond=1234567)
>>> d2.microsecond
1234
>>> d2.nanosecond
1234567
If you construct a DatetimeWithNanoseconds in other ways, like for example DatetimeWithNanoseconds.now(), you always get a value with nanosecond set to zero.
>>> now = DatetimeWithNanoseconds.now()
>>> now.microsecond
51570
>>> now.nanosecond
0
The values that I get when fetching documents from Firestore always seem to have nanosecond set to zero! So if I were to fetch a document with a timestamp, then write it back to Firestore, it would lose precision. (Though admittedly I am using a pretty old version of firebase-admin, so it may have been fixed in a more recent version.)
I'm currently doing something like this to extract the seconds and nanoseconds from a timestamp (I use this in a context where I'm not sure if the timestamp is a datetime or a DatetimeWithNanoseconds):
def get_seconds_and_nanoseconds(val):
if isinstance(val, datetime_helpers.DatetimeWithNanoseconds) and val.nanosecond:
return {
'nanoseconds': val.nanosecond,
'seconds': int(val.timestamp()),
}
if isinstance(val, datetime):
return {
'nanoseconds': 1000 * int(val.microsecond),
'seconds': int(val.timestamp()),
}
raise TypeError(f'Not a datetime or DatetimeWithNanoseconds')
And to construct a correct DatetimeWithNanoseconds from seconds and nanoseconds, I do this:
def datetime_with_nanoseconds(seconds, nanoseconds):
dt = datetime.fromtimestamp(seconds, tz=timezone.utc)
dtnanos = datetime_helpers.DatetimeWithNanoseconds(
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
nanosecond=nanoseconds,
tzinfo=dt.tzinfo,
)
return dtnanos
It's ugly, but I haven't found a more concise way yet.
回答3:
Try this solution
try:
utc_time = strptime(''.join(str(doc_data['start_date']).rsplit(':', 1)), "%Y-%m-%d %H:%M:%S.%f%z")
except Exception as e:
if type(e) is ValueError:
utc_time = strptime(''.join(str(doc_data['start_date']).rsplit(':', 1)), "%Y-%m-%d %H:%M:%S%z")
else :
print(e)
回答4:
The DatetimeWithNanoseconds is an object so you have to use its attributes. To view its attributes use print(dir(doc_data['start_date'])).Finally you can format it to a string the below code shows how.
docs = db.collection(u'status').stream()
for doc in docs:
doc_data = doc_data.to_dict()
val1 = doc_data['start_date']
year,month,day,hour,minute,second,tzinfo = val1.year,val1.month,val1.day,val1.hour, val1.minute, val1.second, val1.tzinfo
utc_time = "%s-%s-%s %s:%s:%s.%s"%(year, month, day,hour,minute,second,tzinfo)
回答5:
import datetime
# here a is instance of 'google.api_core.datetime_helpers.DatetimeWithNanoseconds'
# we use datetime.combine method to convert DatetimeWithNanosecond into datetime object
answer = datetime.datetime.combine(date=a.date(), time=a.time(), tzinfo=a.tzinfo)
print(type(answer)
# it will be class datetime
visit https://docs.python.org/3/library/datetime.html#datetime.datetime.combine
来源:https://stackoverflow.com/questions/59516984/convert-datetimewithnanoseconds-to-date-format-in-python-firestore