There are two ways of approaching the issue of unknown "common" types; the first is to use a shim property, for example a property that represents the value as something similar (a string or long for example):
[ProtoMember(8)]
public string Foo {
    get { ... read from the other member ... }
    set { ... assign the other member ... }
}
The other approach is a surrogate, which is a second protobuf contract that is automatically substituted. The requirements to use a surrogate are:
- there must be a defined conversion operator (implicit or explict) between the two types (for example, DateTimeOffsetandDateTimeOffsetSurrogate)
- you then use SetSurrogate(surrogateType)to educate protobuf-net, for exampleRuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
the shim property is simpler, but requires repeat per-member. The surrogate is applied automatically to all instances of the type within the model. The surrogate then follows standard protobuf-net rules, so you would indicate which members to serialize, etc.
EDIT: Adding code example
using System;
using ProtoBuf;
[ProtoContract]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public string DateTimeString { get; set; }
    public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
    {
        return new DateTimeOffsetSurrogate {DateTimeString = value.ToString("u")};
    }
    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
    {
        return DateTimeOffset.Parse(value.DateTimeString);
    }
}
Then register it like this
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));