From 8d0844bfae6301a089d9de70ed36953377b4a7e8 Mon Sep 17 00:00:00 2001
From: Utz-Uwe Haus <uhaus@cray.com>
Date: Wed, 22 Apr 2020 14:00:29 +0200
Subject: [PATCH] Add maestro_timestamp type

External YAML representation is rfc3339 timestamp string, protobuf
message Timestamp.
---
 attributes/Makefile.am           |   3 +-
 attributes/maestro-schema.c      |  20 ++++-
 attributes/maestro-schema.h      |   8 ++
 attributes/schema_type_parse.c   |  17 +---
 attributes/schema_type_parse.h   |   5 --
 attributes/schema_type_parse.peg |  22 +-----
 include/maestro/attributes.h     | 102 ++++++++++++++++++++----
 maestro/Makefile.am              |   5 +-
 maestro/attributes.c             |  84 ++++++++++++++++++++
 protocols/mstro_pool.pb-c.c      | 128 ++++++++++++++++++++++++++++++-
 protocols/mstro_pool.pb-c.h      |  48 ++++++++++--
 protocols/mstro_pool.proto       |  10 +++
 12 files changed, 386 insertions(+), 66 deletions(-)

diff --git a/attributes/Makefile.am b/attributes/Makefile.am
index 5685a7a3..92b792a2 100644
--- a/attributes/Makefile.am
+++ b/attributes/Makefile.am
@@ -43,10 +43,9 @@ libattributes_la_CPPFLAGS = \
 		-I$(top_srcdir)/deps/mamba \
                 -I$(top_srcdir)/include \
 		-I$(top_srcdir)/deps/libyaml/include \
-		-I$(top_srcdir)/deps/libcyaml/include
+		-I$(top_srcdir)/deps/libcyaml/include 
 
 libattributes_la_SOURCES = \
-	attribute_schema.h attribute_schema.c \
 	maestro-schema.h maestro-schema.c\
         schema_type_parse.c schema_type_parse.h \
 	maestro-core-yaml.h ecmwf-yaml.h \
diff --git a/attributes/maestro-schema.c b/attributes/maestro-schema.c
index 5b39d101..5952aece 100644
--- a/attributes/maestro-schema.c
+++ b/attributes/maestro-schema.c
@@ -15,6 +15,7 @@
 
 #include "maestro.h"
 #include "maestro/logging.h"
+#include "maestro/attributes.h"
 #include "maestro-schema.h"
 #include "schema_type_parse.h"
 #include "symtab.h"
@@ -747,6 +748,7 @@ enum mstro_schema_builtin_type {
   MSTRO_SCHEMA_BUILTIN_STRING,
   MSTRO_SCHEMA_BUILTIN_REGEX,
   MSTRO_SCHEMA_BUILTIN_BLOB,
+  MSTRO_SCHEMA_BUILTIN_TIMESTAMP,
   MSTRO_SCHEMA_BUILTIN_TYPE__MAX
 };
   
@@ -778,7 +780,10 @@ static struct {
                                     .stp_kind = MSTRO_STP_REGEX},
   [MSTRO_SCHEMA_BUILTIN_BLOB]   = { .type = MSTRO_SCHEMA_BUILTIN_BLOB,
                                     .basename = "blob",
-                                    .stp_kind = MSTRO_STP_BLOB}
+                                    .stp_kind = MSTRO_STP_BLOB},
+  [MSTRO_SCHEMA_BUILTIN_TIMESTAMP] = { .type = MSTRO_SCHEMA_BUILTIN_TIMESTAMP,
+                                    .basename = "timestamp",
+                                    .stp_kind = MSTRO_STP_TIMESTAMP}
 };
 
 /** lookup or create builtin type */
@@ -1075,6 +1080,11 @@ mstro_attributes_parse_val(mstro_schema schema,
       minlen = tdecl->parsed_type->blob_minlen;
       maxlen = tdecl->parsed_type->blob_maxlen;
       break;
+    case MSTRO_STP_TIMESTAMP:
+      entry->valsize = sizeof(mstro_timestamp);
+      err = regcomp(&(regex[0]), RFC3339_PATTERN, REG_EXTENDED);
+      break;
+
     default:
       ERR("Unexpected parsed type %d\n", tdecl->parsed_type->kind);
       s=MSTRO_UNIMPL;
@@ -1160,6 +1170,14 @@ mstro_attributes_parse_val(mstro_schema schema,
         memcpy((char*)entry->val, val, val_len);
         break;
 
+      case MSTRO_STP_TIMESTAMP: {
+        s=mstro_timestamp_parse((const char*)val, val_len, ((mstro_timestamp*)entry->val));
+        if(s!=MSTRO_OK) {
+          ERR("Failed to parse timestamp value |%s| as rfc3339-timestamp\n");
+        }
+        break;
+      }
+
       default:
         ERR("Unexpected parsed type %d\n", tdecl->parsed_type->kind);
         s=MSTRO_UNIMPL;
diff --git a/attributes/maestro-schema.h b/attributes/maestro-schema.h
index 800e0e16..94deb69c 100644
--- a/attributes/maestro-schema.h
+++ b/attributes/maestro-schema.h
@@ -142,4 +142,12 @@ mstro_attributes_default(mstro_schema schema,
                          bool override,
                          mstro_attribute_dict *result);
 
+
+/** Pattern for RFC3339-timestamp string representation (see @ref
+ * mstro_timestamp) */
+#define RFC3339_PATTERN ("^([0-9]{4})-([0-9]{2})-([0-9]{2})"                     \
+                         "([Tt]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?"   \
+                         "(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?")
+
+
 #endif
diff --git a/attributes/schema_type_parse.c b/attributes/schema_type_parse.c
index 7b1d221d..3738e652 100644
--- a/attributes/schema_type_parse.c
+++ b/attributes/schema_type_parse.c
@@ -3721,6 +3721,10 @@ mstro_stp_val__describe(const struct mstro_stp_val *v)
     case MSTRO_STP_BLOB:
       DEBUG("v kind: BLOB (min=%" PRIu64 ", max=%" PRIu64 ")\n", v->blob_minlen, v->blob_maxlen);
       break;
+    case MSTRO_STP_TIMESTAMP:
+      DEBUG("v kind: TIMESTAMP\n");
+      break;
+
     case MSTRO_STP_BOOLVAL:
     case MSTRO_STP_MININT:
     case MSTRO_STP_MAXINT:
@@ -3729,7 +3733,6 @@ mstro_stp_val__describe(const struct mstro_stp_val *v)
     case MSTRO_STP_EXCLSTR:
     case MSTRO_STP_ICASE:
     case MSTRO_STP_NAME:
-    case MSTRO_STP_TIMESTAMP:
       DEBUG("v kind %d, describe unimplemented\n", v->kind);
       break;
 
@@ -4026,18 +4029,6 @@ mstro_stp_val__build_restricted_type(
       }
     case MSTRO_STP_TIMESTAMP: {
       leftovers = args; /* no args supported */
-      /* construct the regexp */
-      result->kind = MSTRO_STP_REGEX;
-      result->regex_ignorecase = false;
-      result->regex_name = strdup("RFC3339 timestamp");
-      result->regex_numpatterns = 1;
-      result->regex_patterns = malloc(sizeof(char*));
-      char *tmp = strdup(RFC3339_PATTERN);
-      if(result->regex_patterns==NULL || tmp==NULL || result->regex_name==NULL) {
-        ERR("Cannot allocate timestamp regex\n");
-        abort();
-      }
-      result->regex_patterns[0] = tmp;
       break;
     }
     default:
diff --git a/attributes/schema_type_parse.h b/attributes/schema_type_parse.h
index e9f5803a..2ab578d2 100644
--- a/attributes/schema_type_parse.h
+++ b/attributes/schema_type_parse.h
@@ -133,11 +133,6 @@ mstro_schema_type_parse(const char *string, struct mstro_stp_val **res);
 mstro_status
 mstro_stp_val_dispose(struct mstro_stp_val *v);
 
-/** User-facing: pattern for RFC3339-timestamp */
-#define RFC3339_PATTERN ("^([0-9]{4})-([0-9]{2})-([0-9]{2})"                     \
-                         "([Tt]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?"   \
-                         "(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?")
-
 
 
 #ifdef __cplusplus
diff --git a/attributes/schema_type_parse.peg b/attributes/schema_type_parse.peg
index d7e8a258..2c9e9f38 100644
--- a/attributes/schema_type_parse.peg
+++ b/attributes/schema_type_parse.peg
@@ -295,11 +295,6 @@ mstro_schema_type_parse(const char *string, struct mstro_stp_val **res);
 mstro_status
 mstro_stp_val_dispose(struct mstro_stp_val *v);
 
-/** User-facing: pattern for RFC3339-timestamp */
-#define RFC3339_PATTERN ("^([0-9]{4})-([0-9]{2})-([0-9]{2})"                     \
-                         "([Tt]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?"   \
-                         "(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?")
-
 
 }
 
@@ -676,6 +671,10 @@ mstro_stp_val__describe(const struct mstro_stp_val *v)
     case MSTRO_STP_BLOB:
       DEBUG("v kind: BLOB (min=%" PRIu64 ", max=%" PRIu64 ")\n", v->blob_minlen, v->blob_maxlen);
       break;
+    case MSTRO_STP_TIMESTAMP:
+      DEBUG("v kind: TIMESTAMP\n");
+      break;
+
     case MSTRO_STP_BOOLVAL:
     case MSTRO_STP_MININT:
     case MSTRO_STP_MAXINT:
@@ -684,7 +683,6 @@ mstro_stp_val__describe(const struct mstro_stp_val *v)
     case MSTRO_STP_EXCLSTR:
     case MSTRO_STP_ICASE:
     case MSTRO_STP_NAME:
-    case MSTRO_STP_TIMESTAMP:
       DEBUG("v kind %d, describe unimplemented\n", v->kind);
       break;
 
@@ -981,18 +979,6 @@ mstro_stp_val__build_restricted_type(
       }
     case MSTRO_STP_TIMESTAMP: {
       leftovers = args; /* no args supported */
-      /* construct the regexp */
-      result->kind = MSTRO_STP_REGEX;
-      result->regex_ignorecase = false;
-      result->regex_name = strdup("RFC3339 timestamp");
-      result->regex_numpatterns = 1;
-      result->regex_patterns = malloc(sizeof(char*));
-      char *tmp = strdup(RFC3339_PATTERN);
-      if(result->regex_patterns==NULL || tmp==NULL || result->regex_name==NULL) {
-        ERR("Cannot allocate timestamp regex\n");
-        abort();
-      }
-      result->regex_patterns[0] = tmp;
       break;
     }
     default:
diff --git a/include/maestro/attributes.h b/include/maestro/attributes.h
index 37f459f8..0f476cb9 100644
--- a/include/maestro/attributes.h
+++ b/include/maestro/attributes.h
@@ -36,10 +36,17 @@
 #ifndef MAESTRO_ATTRIBUTES_H_
 #define MAESTRO_ATTRIBUTES_H_ 1
 
-#include <stdlib.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
 
+#include <stdlib.h>
+#include <time.h>
+#include <stdbool.h>
+  
 #include "maestro/status.h"
 
+
 /**@addtogroup MSTRO_Core
  **@{
  **/
@@ -144,31 +151,90 @@ int MSTRO_ATTR_CORE_CDO_STR_TABLE_LEN(void);
 
 /**@} (end of group mstro_attr_builtin) */
 
+
+/** A datatype to hold an rfc3339 timestamp.
+ *
+ */
+typedef struct {
+  int64_t sec;    /* Number of seconds since the epoch of 1970-01-01T00:00:00Z */
+  int32_t nsec;   /* Nanoseconds [0, 999999999] */
+  int16_t offset; /* Offset from UTC in minutes [-1439, 1439] */
+} mstro_timestamp;
+
+/** Parse an RFC3339 timestamp.
+
+ * Return result through (caller-allocated) *tsp.
+ *
+ * If tsp==NULL, the parse will be performed, but no result
+ * returned. The status code indicates whether the string can be
+ * successfully parsed.
+ */
+mstro_status
+mstro_timestamp_parse(const char *str, size_t len, mstro_timestamp *tsp);
+
+/** Check whether TSP is a valid timestamp. */
+mstro_status
+mstro_timestamp_valid(const mstro_timestamp *tsp, bool *valid);
+
+/** Format timestamp as string. Returns length of formatted output, 0 on error. */
+size_t
+mstro_timestamp_format(char *dst, size_t len, const mstro_timestamp *tsp);
+
+/** Format timestamp as string with a given precision.
+ **
+ ** Precision can be between 0 and 9, and designates the fractional seconds precision.
+ **
+ ** Returns length of formatted output, 0 on error. */
+size_t
+mstro_timestamp_format_precision(char *dst, size_t len, const mstro_timestamp *tsp, int precision);
+
+/** Compare timestamps, returning <0, 0, >0 */
+int
+mstro_timestamp_compare(const mstro_timestamp *tsp1, const mstro_timestamp *tsp2);
+
+/** Convert timestamp to C style (caller-allocated) struct tm, UTC */
+mstro_status
+mstro_timestamp_to_tm_utc(const mstro_timestamp *tsp, struct tm *tmp);
+
+/** Convert timestamp to C style (caller-allocated) struct tm, local time zone */
+mstro_status
+mstro_timestamp_to_tm_local(const mstro_timestamp *tsp, struct tm *tmp);
+
+
+/** Built-in Maestro attribute data types */
 enum mstro_cdo_attr_value_types {
+  /** Invalid value */
   MSTRO_CDO_ATTR_VALUE_INVALID = 0,
-  MSTRO_CDO_ATTR_VALUE_NA, // Not applicable, corresponding key has not been found.
-  /* these are castable to the respective C type */
-  MSTRO_CDO_ATTR_VALUE_bool,
-  MSTRO_CDO_ATTR_VALUE_int32,
-  MSTRO_CDO_ATTR_VALUE_uint32,
-  MSTRO_CDO_ATTR_VALUE_int64,
-  MSTRO_CDO_ATTR_VALUE_uint64,
-  /* these are castable to const char* */
-  MSTRO_CDO_ATTR_VALUE_cstring,
-  MSTRO_CDO_ATTR_VALUE_json,
-  MSTRO_CDO_ATTR_VALUE_yaml,
 
-  /* this is castable to struct mstro_cdo_id */
-  MSTRO_CDO_ATTR_VALUE_cdoid,
+  /** Not applicable, since the key has not been found */
+  MSTRO_CDO_ATTR_VALUE_NA, 
 
+  /* types castable to the respective C types */
+  MSTRO_CDO_ATTR_VALUE_bool,     /**< castable to bool */
+  MSTRO_CDO_ATTR_VALUE_int32,    /**< castable to int32_t */
+  MSTRO_CDO_ATTR_VALUE_uint32,   /**< castable to uint32_t */
+  MSTRO_CDO_ATTR_VALUE_int64,    /**< castable to int64_t */ 
+  MSTRO_CDO_ATTR_VALUE_uint64,   /**< castable to uint64_t */
+
+  /* these are castable to const char* */
+  MSTRO_CDO_ATTR_VALUE_cstring,  /**< castable to const char* */
+  MSTRO_CDO_ATTR_VALUE_json,     /**< @deprecated JSON string, castable to const char * */
+  MSTRO_CDO_ATTR_VALUE_yaml,     /**< @deprecated YAML string, castable to const char * */
+  MSTRO_CDO_ATTR_VALUE_BLOB,     /**< Opaque blob type, user must cast
+                                  **  as appropriate.  The size must
+                                  **  be stored in some other
+                                  **  attribute by the user */
+
+  MSTRO_CDO_ATTR_VALUE_timestamp, /**< castable to mstro_timestamp */
+  MSTRO_CDO_ATTR_VALUE_cdoid,     /**< castable to mstro_cdo_id */
+
+  /* WIP: */
   /* Mamba layout spec */
   MSTRO_CDO_ATTR_VALUE_layout,
 
   /* Distribution spec */
   MSTRO_CDO_ATTR_VALUE_distribution,
 
-  /* Opaque blobtype */
-  MSTRO_CDO_ATTR_VALUE_BLOB,
 
   /* this is the number of different value types supported */
   MSTRO_CDO_ATTR_VALUE__MAX
@@ -241,4 +307,8 @@ mstro_cdo_attribute_set_default(mstro_cdo cdo);
   /**@} (end of group MSTRO_Attr) */
 /**@} (end of addtogroup MSTRO_Core) */
 
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
 #endif /* MAESTRO_ATTRIBUTES_H_ */
diff --git a/maestro/Makefile.am b/maestro/Makefile.am
index dddbde1a..5c7bc9d4 100644
--- a/maestro/Makefile.am
+++ b/maestro/Makefile.am
@@ -39,7 +39,8 @@ libmaestro_core_la_CPPFLAGS = \
 		-I$(top_srcdir)/deps/mamba/memory \
                 -I$(top_srcdir)/include \
 		-I$(top_srcdir)/deps/libyaml/include \
-		-I$(top_srcdir)/deps/libcyaml/include 
+		-I$(top_srcdir)/deps/libcyaml/include \
+		-I$(top_srcdir)/deps
 
 libmaestro_core_la_SOURCES = \
 	cdo.c \
@@ -62,7 +63,7 @@ libmaestro_core_la_SOURCES = \
 
 #	mempool.c 
 
-libmaestro_core_la_LIBADD=$(top_builddir)/deps/libcyaml/libcyaml.la $(top_builddir)/deps/mamba/libmmb.la
+libmaestro_core_la_LIBADD=$(top_builddir)/deps/libcyaml/libcyaml.la $(top_builddir)/deps/mamba/libmmb.la $(top_builddir)/deps/c-timestamp/libtimestamp.la
 
 # Conductor implementation varies depending on configuration
 if WITH_OFI_POOL_MANAGER
diff --git a/maestro/attributes.c b/maestro/attributes.c
index c84db790..08e78230 100644
--- a/maestro/attributes.c
+++ b/maestro/attributes.c
@@ -30,6 +30,8 @@
 #include "maestro/i_attributes.h"
 #include "maestro/logging.h"
 
+#include "c-timestamp/timestamp.h"
+
 #include <inttypes.h>
 
 /* FIXME: we should have a table of name/type/... and make the const
@@ -239,5 +241,87 @@ mstro_cdo_attribute_set_yaml(mstro_cdo cdo, const char* keyval_in_yaml)
   return MSTRO_OK;
 }
 
+mstro_status
+mstro_timestamp_parse(const char *str, size_t len, mstro_timestamp *tsp)
+{
+  if(str==NULL) {
+    ERR("NULL string, cannot parse as timestamp\n");
+    return MSTRO_INVARG;
+  }
+  timestamp_t aux;
+  timestamp_t *ts;
+  if(tsp==NULL) {
+    ts=&aux;
+  } else {
+    ts = (timestamp_t*)tsp;
+  }
+  
+  int err = timestamp_parse(str, len, ts);
+  if(err==0)
+    return MSTRO_OK;
+  else
+    return MSTRO_FAIL;
+}
+
+mstro_status
+mstro_timestamp_valid(const mstro_timestamp *tsp, bool *valid)
+{
+  *valid = timestamp_valid((timestamp_t*)tsp);
+  return MSTRO_OK;
+}
+
+size_t
+mstro_timestamp_format(char *dst, size_t len, const mstro_timestamp *tsp)
+{
+  return timestamp_format(dst, len, (const timestamp_t*)tsp);
+}
+
+size_t
+mstro_timestamp_format_precision(char *dst, size_t len, const mstro_timestamp *tsp, int precision)
+{
+  return timestamp_format_precision(dst, len, (const timestamp_t*)tsp, precision);
+}
+
+/** Compare timestamps, returning <0, 0, >0 */
+int
+mstro_timestamp_compare(const mstro_timestamp *tsp1, const mstro_timestamp *tsp2)
+{
+  return timestamp_compare((const timestamp_t*)tsp1,
+                           (const timestamp_t*)tsp2);
+}
+
+/** Convert timestamp to C style (caller-allocated) struct tm, UTC */
+mstro_status
+mstro_timestamp_to_tm_utc(const mstro_timestamp *tsp, struct tm *tmp)
+{
+  if(tsp==NULL) 
+    return MSTRO_INVARG;
+  if(tmp==NULL)
+    return MSTRO_INVOUT;
+
+  struct tm *res = timestamp_to_tm_utc((const timestamp_t*)tsp, tmp);
+  if(res==NULL)
+    return MSTRO_FAIL;
+  
+  return MSTRO_OK;
+}
+
+/** Convert timestamp to C style (caller-allocated) struct tm, local time zone */
+mstro_status
+mstro_timestamp_to_tm_local(const mstro_timestamp *tsp, struct tm *tmp)
+{
+  if(tsp==NULL) 
+    return MSTRO_INVARG;
+  if(tmp==NULL)
+    return MSTRO_INVOUT;
+
+  struct tm *res = timestamp_to_tm_local((const timestamp_t*)tsp, tmp);
+  if(res==NULL)
+    return MSTRO_FAIL;
+  
+  return MSTRO_OK;
+}
+
+
 
 
diff --git a/protocols/mstro_pool.pb-c.c b/protocols/mstro_pool.pb-c.c
index a9b0e92c..e4991229 100644
--- a/protocols/mstro_pool.pb-c.c
+++ b/protocols/mstro_pool.pb-c.c
@@ -502,6 +502,51 @@ void   mstro__pool__declare_ack__free_unpacked
   assert(message->base.descriptor == &mstro__pool__declare_ack__descriptor);
   protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
 }
+void   mstro__pool__timestamp__init
+                     (Mstro__Pool__Timestamp         *message)
+{
+  static const Mstro__Pool__Timestamp init_value = MSTRO__POOL__TIMESTAMP__INIT;
+  *message = init_value;
+}
+size_t mstro__pool__timestamp__get_packed_size
+                     (const Mstro__Pool__Timestamp *message)
+{
+  assert(message->base.descriptor == &mstro__pool__timestamp__descriptor);
+  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
+}
+size_t mstro__pool__timestamp__pack
+                     (const Mstro__Pool__Timestamp *message,
+                      uint8_t       *out)
+{
+  assert(message->base.descriptor == &mstro__pool__timestamp__descriptor);
+  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
+}
+size_t mstro__pool__timestamp__pack_to_buffer
+                     (const Mstro__Pool__Timestamp *message,
+                      ProtobufCBuffer *buffer)
+{
+  assert(message->base.descriptor == &mstro__pool__timestamp__descriptor);
+  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
+}
+Mstro__Pool__Timestamp *
+       mstro__pool__timestamp__unpack
+                     (ProtobufCAllocator  *allocator,
+                      size_t               len,
+                      const uint8_t       *data)
+{
+  return (Mstro__Pool__Timestamp *)
+     protobuf_c_message_unpack (&mstro__pool__timestamp__descriptor,
+                                allocator, len, data);
+}
+void   mstro__pool__timestamp__free_unpacked
+                     (Mstro__Pool__Timestamp *message,
+                      ProtobufCAllocator *allocator)
+{
+  if(!message)
+    return;
+  assert(message->base.descriptor == &mstro__pool__timestamp__descriptor);
+  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
+}
 void   mstro__pool__aval__init
                      (Mstro__Pool__AVal         *message)
 {
@@ -2738,7 +2783,71 @@ const ProtobufCMessageDescriptor mstro__pool__declare_ack__descriptor =
   (ProtobufCMessageInit) mstro__pool__declare_ack__init,
   NULL,NULL,NULL    /* reserved[123] */
 };
-static const ProtobufCFieldDescriptor mstro__pool__aval__field_descriptors[9] =
+static const ProtobufCFieldDescriptor mstro__pool__timestamp__field_descriptors[3] =
+{
+  {
+    "sec",
+    1,
+    PROTOBUF_C_LABEL_NONE,
+    PROTOBUF_C_TYPE_SFIXED64,
+    0,   /* quantifier_offset */
+    offsetof(Mstro__Pool__Timestamp, sec),
+    NULL,
+    NULL,
+    0,             /* flags */
+    0,NULL,NULL    /* reserved1,reserved2, etc */
+  },
+  {
+    "nsec",
+    2,
+    PROTOBUF_C_LABEL_NONE,
+    PROTOBUF_C_TYPE_SFIXED32,
+    0,   /* quantifier_offset */
+    offsetof(Mstro__Pool__Timestamp, nsec),
+    NULL,
+    NULL,
+    0,             /* flags */
+    0,NULL,NULL    /* reserved1,reserved2, etc */
+  },
+  {
+    "offset",
+    3,
+    PROTOBUF_C_LABEL_NONE,
+    PROTOBUF_C_TYPE_SINT32,
+    0,   /* quantifier_offset */
+    offsetof(Mstro__Pool__Timestamp, offset),
+    NULL,
+    NULL,
+    0,             /* flags */
+    0,NULL,NULL    /* reserved1,reserved2, etc */
+  },
+};
+static const unsigned mstro__pool__timestamp__field_indices_by_name[] = {
+  1,   /* field[1] = nsec */
+  2,   /* field[2] = offset */
+  0,   /* field[0] = sec */
+};
+static const ProtobufCIntRange mstro__pool__timestamp__number_ranges[1 + 1] =
+{
+  { 1, 0 },
+  { 0, 3 }
+};
+const ProtobufCMessageDescriptor mstro__pool__timestamp__descriptor =
+{
+  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
+  "mstro.pool.Timestamp",
+  "Timestamp",
+  "Mstro__Pool__Timestamp",
+  "mstro.pool",
+  sizeof(Mstro__Pool__Timestamp),
+  3,
+  mstro__pool__timestamp__field_descriptors,
+  mstro__pool__timestamp__field_indices_by_name,
+  1,  mstro__pool__timestamp__number_ranges,
+  (ProtobufCMessageInit) mstro__pool__timestamp__init,
+  NULL,NULL,NULL    /* reserved[123] */
+};
+static const ProtobufCFieldDescriptor mstro__pool__aval__field_descriptors[10] =
 {
   {
     "bool",
@@ -2848,6 +2957,18 @@ static const ProtobufCFieldDescriptor mstro__pool__aval__field_descriptors[9] =
     0 | PROTOBUF_C_FIELD_FLAG_ONEOF,             /* flags */
     0,NULL,NULL    /* reserved1,reserved2, etc */
   },
+  {
+    "timestamp",
+    10,
+    PROTOBUF_C_LABEL_NONE,
+    PROTOBUF_C_TYPE_MESSAGE,
+    offsetof(Mstro__Pool__AVal, val_case),
+    offsetof(Mstro__Pool__AVal, timestamp),
+    &mstro__pool__timestamp__descriptor,
+    NULL,
+    0 | PROTOBUF_C_FIELD_FLAG_ONEOF,             /* flags */
+    0,NULL,NULL    /* reserved1,reserved2, etc */
+  },
 };
 static const unsigned mstro__pool__aval__field_indices_by_name[] = {
   0,   /* field[0] = bool */
@@ -2857,13 +2978,14 @@ static const unsigned mstro__pool__aval__field_indices_by_name[] = {
   1,   /* field[1] = int32 */
   2,   /* field[2] = int64 */
   7,   /* field[7] = string */
+  9,   /* field[9] = timestamp */
   3,   /* field[3] = uint32 */
   4,   /* field[4] = uint64 */
 };
 static const ProtobufCIntRange mstro__pool__aval__number_ranges[1 + 1] =
 {
   { 1, 0 },
-  { 0, 9 }
+  { 0, 10 }
 };
 const ProtobufCMessageDescriptor mstro__pool__aval__descriptor =
 {
@@ -2873,7 +2995,7 @@ const ProtobufCMessageDescriptor mstro__pool__aval__descriptor =
   "Mstro__Pool__AVal",
   "mstro.pool",
   sizeof(Mstro__Pool__AVal),
-  9,
+  10,
   mstro__pool__aval__field_descriptors,
   mstro__pool__aval__field_indices_by_name,
   1,  mstro__pool__aval__number_ranges,
diff --git a/protocols/mstro_pool.pb-c.h b/protocols/mstro_pool.pb-c.h
index 84d3e026..8816b13f 100644
--- a/protocols/mstro_pool.pb-c.h
+++ b/protocols/mstro_pool.pb-c.h
@@ -26,6 +26,7 @@ typedef struct _Mstro__Pool__EmergencyDetach Mstro__Pool__EmergencyDetach;
 typedef struct _Mstro__Pool__Bye Mstro__Pool__Bye;
 typedef struct _Mstro__Pool__Declare Mstro__Pool__Declare;
 typedef struct _Mstro__Pool__DeclareAck Mstro__Pool__DeclareAck;
+typedef struct _Mstro__Pool__Timestamp Mstro__Pool__Timestamp;
 typedef struct _Mstro__Pool__AVal Mstro__Pool__AVal;
 typedef struct _Mstro__Pool__Attributes Mstro__Pool__Attributes;
 typedef struct _Mstro__Pool__Attributes__Map Mstro__Pool__Attributes__Map;
@@ -360,6 +361,21 @@ struct  _Mstro__Pool__DeclareAck
     , 0, NULL, 0 }
 
 
+/*
+ ** Built-in type corresponding to @ref mstro_timestamp 
+ */
+struct  _Mstro__Pool__Timestamp
+{
+  ProtobufCMessage base;
+  int64_t sec;
+  int32_t nsec;
+  int32_t offset;
+};
+#define MSTRO__POOL__TIMESTAMP__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&mstro__pool__timestamp__descriptor) \
+    , 0, 0, 0 }
+
+
 typedef enum {
   MSTRO__POOL__AVAL__VAL__NOT_SET = 0,
   MSTRO__POOL__AVAL__VAL_BOOL = 1,
@@ -370,15 +386,11 @@ typedef enum {
   MSTRO__POOL__AVAL__VAL_FLOAT = 6,
   MSTRO__POOL__AVAL__VAL_DOUBLE = 7,
   MSTRO__POOL__AVAL__VAL_STRING = 8,
-  MSTRO__POOL__AVAL__VAL_BYTES = 9
+  MSTRO__POOL__AVAL__VAL_BYTES = 9,
+  MSTRO__POOL__AVAL__VAL_TIMESTAMP = 10
     PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(MSTRO__POOL__AVAL__VAL)
 } Mstro__Pool__AVal__ValCase;
 
-/*
- ** Builtin attribute value types.
- *  Refer to
- *  https://developers.google.com/protocol-buffers/docs/proto3#scalar
- */
 struct  _Mstro__Pool__AVal
 {
   ProtobufCMessage base;
@@ -396,6 +408,7 @@ struct  _Mstro__Pool__AVal
      * protobuf limits this to 2^32 
      */
     ProtobufCBinaryData bytes;
+    Mstro__Pool__Timestamp *timestamp;
   };
 };
 #define MSTRO__POOL__AVAL__INIT \
@@ -1422,6 +1435,25 @@ Mstro__Pool__DeclareAck *
 void   mstro__pool__declare_ack__free_unpacked
                      (Mstro__Pool__DeclareAck *message,
                       ProtobufCAllocator *allocator);
+/* Mstro__Pool__Timestamp methods */
+void   mstro__pool__timestamp__init
+                     (Mstro__Pool__Timestamp         *message);
+size_t mstro__pool__timestamp__get_packed_size
+                     (const Mstro__Pool__Timestamp   *message);
+size_t mstro__pool__timestamp__pack
+                     (const Mstro__Pool__Timestamp   *message,
+                      uint8_t             *out);
+size_t mstro__pool__timestamp__pack_to_buffer
+                     (const Mstro__Pool__Timestamp   *message,
+                      ProtobufCBuffer     *buffer);
+Mstro__Pool__Timestamp *
+       mstro__pool__timestamp__unpack
+                     (ProtobufCAllocator  *allocator,
+                      size_t               len,
+                      const uint8_t       *data);
+void   mstro__pool__timestamp__free_unpacked
+                     (Mstro__Pool__Timestamp *message,
+                      ProtobufCAllocator *allocator);
 /* Mstro__Pool__AVal methods */
 void   mstro__pool__aval__init
                      (Mstro__Pool__AVal         *message);
@@ -2188,6 +2220,9 @@ typedef void (*Mstro__Pool__Declare_Closure)
 typedef void (*Mstro__Pool__DeclareAck_Closure)
                  (const Mstro__Pool__DeclareAck *message,
                   void *closure_data);
+typedef void (*Mstro__Pool__Timestamp_Closure)
+                 (const Mstro__Pool__Timestamp *message,
+                  void *closure_data);
 typedef void (*Mstro__Pool__AVal_Closure)
                  (const Mstro__Pool__AVal *message,
                   void *closure_data);
@@ -2331,6 +2366,7 @@ extern const ProtobufCMessageDescriptor mstro__pool__emergency_detach__descripto
 extern const ProtobufCMessageDescriptor mstro__pool__bye__descriptor;
 extern const ProtobufCMessageDescriptor mstro__pool__declare__descriptor;
 extern const ProtobufCMessageDescriptor mstro__pool__declare_ack__descriptor;
+extern const ProtobufCMessageDescriptor mstro__pool__timestamp__descriptor;
 extern const ProtobufCMessageDescriptor mstro__pool__aval__descriptor;
 extern const ProtobufCMessageDescriptor mstro__pool__attributes__descriptor;
 extern const ProtobufCMessageDescriptor mstro__pool__attributes__map__descriptor;
diff --git a/protocols/mstro_pool.proto b/protocols/mstro_pool.proto
index 63fa3239..5320db4d 100644
--- a/protocols/mstro_pool.proto
+++ b/protocols/mstro_pool.proto
@@ -129,10 +129,19 @@ message DeclareAck {
 
 /** Attributes. */
 
+/** Built-in type corresponding to @ref mstro_timestamp */
+message Timestamp {
+  sfixed64    sec = 1;
+  sfixed32   nsec = 2;
+  sint32   offset = 3;
+};
+
 /** Builtin attribute value types.
  *  Refer to
  *  https://developers.google.com/protocol-buffers/docs/proto3#scalar
  **/
+
+
 message AVal {
   oneof val {
     bool       bool   = 1;
@@ -147,6 +156,7 @@ message AVal {
 
     string     string = 8;
     bytes      bytes  = 9; /* protobuf limits this to 2^32 */
+    Timestamp  timestamp = 10;
   };
 };
 
-- 
GitLab