protobuf icon indicating copy to clipboard operation
protobuf copied to clipboard

[Python] Enhancement request: Timestamp from datetime on one line

Open arthur-tacca opened this issue 7 years ago • 14 comments

The google.protobuf.Timestamp well-known type is very much appreciated, but I've found myself often needing to be able to convert a Python datetime into a Timestamp within an expression (especially in list comprehensions). Of course it is easy to knock up a function to do this:

def timestamp_from_datetime(dt):
    ts = Timestamp()
    ts.FromDatetime(dt)
    return ts

But it seems a bit annoying to have to do this. Would it be possible to add a facility to do this directly into Timestamp? A constructor or static method would be ideal, but even just return self in FromDatetime() would do (then we could write the above as Timestamp().FromDatetime(dt)). The same problem applies to Duration from timedelta, although I personally have not used that class.

By the way, I realised that the only time I come across this is as part of building a larger protobuf object. Getting datetime objects to work directly in their constructor would be even better, for my use case at least:

my_protobuf = MyProtobufMessage(
    int_field=3,  # fine
    list_of_strings=["foo", "bar"],  # fine
    timestamp=datetime.utcnow(),   # error
)

arthur-tacca avatar Dec 01 '17 15:12 arthur-tacca

Also it took me a while to find where to import Timestamp from. It was not in the docs. from google.protobuf.timestamp_pb2 import Timestamp

seperman avatar Dec 13 '17 18:12 seperman

Thank you @seperman. I really wish the documentation was stronger.

bchurchill avatar May 04 '18 20:05 bchurchill

I don't like the API consequences of return self from instance-scope FromDatetime; I've filed issue 5344 asking for a proper class-scope method so that the developer experience can be my_timestamp_message = timestamp_pb2.Timestamp.FromDatetime(my_datetime).

@nathanielmanistaatgoogle Thanks. Actually I agree with you; I said a static method, like you're suggesting "would be ideal" (class methods and static methods only differ in their implementation, not their use, in Python). The return self thing was just something that "would do" in case Googlers thought it would be easier.

arthur-tacca avatar Nov 13 '18 10:11 arthur-tacca

I've also found Timestamp a pain to work with. If you're using Python 3.7+ and willing to try another plugin then you can check out https://github.com/danielgtaylor/python-betterproto. It just uses Python's timezone-aware datetime.datetime object. For example, given this proto:

syntax = "proto3";

import "google/protobuf/timestamp.proto";

message Test {
  google.protobuf.Timestamp ts = 1;
}

You get this generated outout:

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: example.proto
# plugin: python-betterproto
from dataclasses import dataclass
from datetime import datetime

import betterproto


@dataclass
class Test(betterproto.Message):
    ts: datetime = betterproto.message_field(1)

Now you can just use it like any normal Python date/time object:

>>> t = Test()
>>> t.ts
datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
>>> t.ts.isoformat()
'1970-01-01T00:00:00+00:00'
>>> from datetime import datetime, timezone
>>> datetime.now(tz=timezone.utc) - t.ts
datetime.timedelta(days=18200, seconds=19110, microseconds=1012)

You can compare against betterproto.DATETIME_ZERO to see whether it has been modified from the default.

danielgtaylor avatar Oct 31 '19 05:10 danielgtaylor

Closing since this to be addressed now per Timestamp.FromDateTime. Feel free to reopen if this still needs further action.

zhangskz avatar Sep 01 '22 17:09 zhangskz

I don't think that solves it (it also looks like documentation for C#), the issue here is that there's no factory/alternate constructor for creating a Timestamp from a datetime in a single line, a la:

ts = Timestamp.MakeFromDatetime(dt)

Currently you have to do what OP wrote:

def timestamp_from_datetime(dt):
    ts = Timestamp()
    ts.FromDatetime(dt)
    return ts

It's probably the case that this should've been an alternate constructor from way back when, and now either there has to be an oddly different and worse name for it, or compatibility has to break. I sympathize with you all :sweat_smile:, seems tricky.

camgunz avatar Sep 21 '22 08:09 camgunz

@zhangskz camgunz is correct, and I can't reopen the issue (because if a repo owner closes an issue then only a repo owner can reopen it).

arthur-tacca avatar Sep 22 '22 14:09 arthur-tacca

or compatibility has to break

To be honest, I think it should just return self. I can't imagine anyone is asserting that FromDatetime returns a NoneType

rollo-b2c2 avatar Jul 27 '23 22:07 rollo-b2c2

@rollo-b2c2 The "compatibility ... break" you've quoted was referring to adding a feature to the constructor. Although, even for that, your point still holds – surely no one is asserting that passing a datetime to the protobuf Timestamp constructor should raise an exception!

arthur-tacca avatar Apr 12 '24 13:04 arthur-tacca

We triage inactive PRs and issues in order to make it easier to find active work. If this issue should remain active or becomes active again, please add a comment.

This issue is labeled inactive because the last activity was over 90 days ago.

github-actions[bot] avatar Jul 12 '24 10:07 github-actions[bot]

I'll keep it open; why not?

camgunz avatar Jul 15 '24 09:07 camgunz