How to Use Protobuf Well-Known Types: Any, Timestamp, and Duration
Use google.protobuf.Any to wrap arbitrary messages with type URLs, google.protobuf.Timestamp for absolute UTC timestamps, and google.protobuf.Duration for signed time spans.
Protobuf well-known types provide standardized, cross-language message definitions for common data patterns. Defined in the protocolbuffers/protobuf repository, these types ensure consistent binary wire format and JSON mapping across all supported languages.
Understanding Protobuf Well-Known Types
What Are Well-Known Types?
Well-known types are a set of protocol buffer message definitions maintained by Google in the src/google/protobuf/ directory. Unlike custom messages, these types ship with pre-generated code for every supported language and follow strict JSON mapping rules defined in the protobuf specification.
Core Types Overview
The three most frequently used well-known types are:
google.protobuf.Any– A container that holds an arbitrary serialized message as bytes along with a URL that identifies the message type. Defined insrc/google/protobuf/any.proto.google.protobuf.Timestamp– Represents a point in time independent of time zone or calendar, stored as seconds and nanoseconds since the Unix epoch. Defined insrc/google/protobuf/timestamp.proto.google.protobuf.Duration– Represents a signed span of time, also using seconds and nanoseconds components. Defined insrc/google/protobuf/duration.proto.
Working with google.protobuf.Any
The Any type enables dynamic typing by storing the raw bytes of any message alongside a type URL that identifies the message's fully qualified name (e.g., type.googleapis.com/my.package.User).
Packing and Unpacking Messages
When using Any, you must pack a message before serialization and unpack it after deserialization. The type URL format follows type.googleapis.com/{fully.qualified.message.name}.
Go Example:
import (
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/proto"
)
type User struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
}
func exampleAny() {
u := &User{Id: 42, Name: "Alice"}
// Pack the message into Any
anyMsg, _ := anypb.New(u)
// Unpacking requires checking type first
var unpacked User
if anypb.UnmarshalTo(anyMsg, &unpacked, proto.UnmarshalOptions{}) == nil {
// Successfully unpacked
}
}
Python Example:
from google.protobuf import any_pb2
from google.protobuf.message import Message
# Assuming User is a generated message class
user = User(id=99, name="Bob")
any_msg = any_pb2.Any()
any_msg.Pack(user)
# Type checking before unpacking
if any_msg.Is(User.DESCRIPTOR):
unpacked_user = User()
any_msg.Unpack(unpacked_user)
Java Example:
import com.google.protobuf.Any;
import com.google.protobuf.Message;
public class AnyExample {
public static void packAndUnpack(UserProto.User user) {
// Pack
Any any = Any.pack(user);
// Unpack with type check
if (any.is(UserProto.User.class)) {
UserProto.User unpacked = any.unpack(UserProto.User.class);
}
}
}
Handling Timestamps with google.protobuf.Timestamp
Timestamp represents time as two fields: seconds (int64) since the Unix epoch and nanos (int32) for the fractional second component. The valid range spans from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
Go Example:
import (
"time"
"google.golang.org/protobuf/types/known/timestamppb"
)
func timestampOps() {
// Current time
now := timestamppb.Now()
// From time.Time
t := time.Date(2024, 3, 2, 12, 0, 0, 0, time.UTC)
ts := timestamppb.New(t)
// Convert back to time.Time
goTime := ts.AsTime()
}
Python Example:
from google.protobuf import timestamp_pb2
from datetime import datetime, timezone
ts = timestamp_pb2.Timestamp()
ts.FromDatetime(datetime.now(timezone.utc))
# Convert back
dt = ts.ToDatetime()
Java Example:
import com.google.protobuf.Timestamp;
import com.google.protobuf.util.Timestamps;
import java.time.Instant;
public class TimestampExample {
public static void main() {
// From milliseconds
Timestamp now = Timestamps.fromMillis(System.currentTimeMillis());
// To Java Instant
Instant instant = Timestamps.toInstant(now);
// Add duration
Timestamp later = Timestamps.add(now, Durations.fromSeconds(3600));
}
}
Managing Durations with google.protobuf.Duration
Duration represents a signed span of time using the same seconds + nanos structure as Timestamp. Negative durations are valid. The JSON representation uses a string ending with the letter s (e.g., "300s" or "1.500s").
Go Example:
import (
"time"
"google.golang.org/protobuf/types/known/durationpb"
)
func durationOps() {
// Create from time.Duration
d := durationpb.New(5 * time.Second)
// Convert to time.Duration
goDuration := d.AsDuration()
// Arithmetic with timestamps
future := timestamppb.New(time.Now().Add(d.AsDuration()))
}
Python Example:
from google.protobuf import duration_pb2
from datetime import timedelta
dur = duration_pb2.Duration()
dur.FromTimedelta(timedelta(hours=2, minutes=30))
# Convert back
td = dur.ToTimedelta()
Java Example:
import com.google.protobuf.Duration;
import com.google.protobuf.util.Durations;
public class DurationExample {
public static void main() {
// From seconds
Duration hour = Durations.fromSeconds(3600);
// Parse from string
Duration parsed = Durations.parse("1.5s");
// Add to timestamp
Timestamp later = Timestamps.add(now, hour);
}
}
Key Source Files and Implementation Details
The well-known types are defined in the main protobuf repository under src/google/protobuf/. These proto files contain extensive comments explaining JSON mapping, valid ranges, and security considerations.
| File | Definition | Key Implementation Details |
|---|---|---|
src/google/protobuf/any.proto |
Any message with type_url and value fields |
Lines 78-101 detail type URL format (type.googleapis.com/{msg}) and security considerations for unpacking. Lines 59-68 explain JSON encoding with @type field. |
src/google/protobuf/timestamp.proto |
Timestamp with seconds and nanos |
Lines 8-15 specify valid range (0001-01-01 to 9999-12-31). Lines 59-99 provide language-agnostic construction examples. JSON maps to RFC 3339 strings. |
src/google/protobuf/duration.proto |
Duration with seconds and nanos |
Lines 50-84 demonstrate arithmetic examples. JSON representation requires string suffix s (e.g., "3.000001s"). Valid range is -315,576,000,000 to +315,576,000,000 seconds. |
All three types follow standard protobuf wire encoding: field 1 is always seconds or type_url, field 2 is nanos or value.
Summary
- Protobuf well-known types provide standardized, cross-language implementations for
Any,Timestamp, andDurationin theprotocolbuffers/protobufrepository. google.protobuf.Anyenables dynamic typing by storing arbitrary messages with a type URL; usePackandUnpackmethods with type safety checks.google.protobuf.Timestamprepresents absolute time as seconds and nanoseconds since Unix epoch; JSON serializes to RFC 3339 format.google.protobuf.Durationrepresents signed time spans; JSON requires thessuffix and supports negative values.- Source definitions in
src/google/protobuf/any.proto,timestamp.proto, andduration.protoguarantee binary compatibility and consistent JSON mapping across all languages.
Frequently Asked Questions
What is the difference between Timestamp and Duration in protobuf?
google.protobuf.Timestamp represents an absolute point in time relative to the Unix epoch (January 1, 1970), while google.protobuf.Duration represents a signed length of time independent of any starting point. In the source definitions (timestamp.proto and duration.proto), both use seconds and nanos fields, but semantically you add durations to timestamps to produce new timestamps.
How do I safely unpack a message from google.protobuf.Any?
Always verify the type before unpacking to avoid deserialization attacks. In Go, use anypb.UnmarshalTo with error checking; in Python, call any_msg.Is(User.DESCRIPTOR) before Unpack(); in Java, use any.is(User.class) before any.unpack(). The any.proto file (lines 78-101) warns that unpacking arbitrary types can be a security risk if the type URL is not validated.
Can I use well-known types with JSON serialization?
Yes, protobuf well-known types have canonical JSON representations defined in the protobuf specification. Timestamp maps to RFC 3339 strings (e.g., "2024-03-02T12:34:56.123Z"), Duration maps to strings ending with s (e.g., "300s"), and Any maps to a JSON object containing @type and value fields. These mappings are documented in the comments of timestamp.proto (lines 8-15), duration.proto, and any.proto (lines 59-68).
What is the valid range for protobuf Timestamp values?
According to src/google/protobuf/timestamp.proto (lines 8-15), valid timestamps range from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. This represents a range of approximately 10,000 years. The seconds field must be within this range, and nanos must be between 0 and 999,999,999 inclusive (or negative for negative timestamps in the case of Duration).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →