ISXDDataEssenceDescriptor_NamespaceURI UL fixed
[asdcplib.git] / src / KM_util.cpp
index 953159c2ca719ab936b9ee631226e22820175ca2..d263d6e47bcfd416b30ddf71a164a594a78890cf 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2009, John Hurst
+Copyright (c) 2005-2015, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <KM_memio.h>
 #include <KM_fileio.h>
 #include <KM_log.h>
-#include <KM_tai.h>
+#include <KM_mutex.h>
 #include <ctype.h>
 #include <list>
 #include <map>
@@ -53,14 +53,19 @@ Kumu::Version()
 
 struct map_entry_t
 {
-  int             rcode;
+  int rcode;
   Kumu::Result_t* result;
 };
 
-const ui32_t MapMax = 1024;
-const ui32_t MapSize = MapMax * (sizeof(struct map_entry_t));
-static bool s_MapInit = false;
-static struct map_entry_t s_ResultMap[MapSize];
+
+// WIN32 does not init this in time for use with Result_t(...) below, so it is
+// now a pointer that Result_t(...) fills in when it needs it.
+static Kumu::Mutex* s_MapLock = 0;
+
+static ui32_t s_MapSize = 0;
+static const ui32_t MapMax = 2048;
+static struct map_entry_t s_ResultMap[MapMax];
+
 
 //
 const Kumu::Result_t&
@@ -69,7 +74,10 @@ Kumu::Result_t::Find(int v)
   if ( v == 0 )
     return RESULT_OK;
 
-  for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
+  assert(s_MapLock);
+  AutoMutex L(*s_MapLock);
+
+  for ( ui32_t i = 0; i < s_MapSize; ++i )
     {
       if ( s_ResultMap[i].rcode == v )
        return *s_ResultMap[i].result;
@@ -82,22 +90,23 @@ Kumu::Result_t::Find(int v)
 Kumu::Result_t
 Kumu::Result_t::Delete(int v)
 {
-  if ( v >= RESULT_NOTAFILE.Value() )
+  if ( v < -99 || v > 99 )
     {
       DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
       return RESULT_FAIL;
     }
 
-  for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
+  assert(s_MapLock);
+  AutoMutex L(*s_MapLock);
+
+  for ( ui32_t i = 0; i < s_MapSize; ++i )
     {
       if ( s_ResultMap[i].rcode == v )
        {
-         s_ResultMap[i].rcode = 0;
-         s_ResultMap[i++].result = 0;
-
-         for ( ; s_ResultMap[i].result != 0 && i < MapMax; i++ )
+         for ( ++i; i < s_MapSize; ++i )
            s_ResultMap[i-1] = s_ResultMap[i];
 
+         --s_MapSize;
          return RESULT_OK;
        }
     }
@@ -105,46 +114,113 @@ Kumu::Result_t::Delete(int v)
   return RESULT_FALSE;
 }
 
+//
+unsigned int
+Kumu::Result_t::End()
+{
+  return s_MapSize;
+}
 
 //
-Kumu::Result_t::Result_t(int v, const char* l) : value(v), label(l)
+const Kumu::Result_t&
+Kumu::Result_t::Get(unsigned int i)
 {
-  assert(l);
-  assert(value < (int)MapMax);
+  return *s_ResultMap[i].result;
+}
+
+//
+Kumu::Result_t::Result_t(int v, const std::string& s, const std::string& l) : value(v), symbol(s), label(l)
+{
+  assert(!l.empty());
+  assert(!s.empty());
 
   if ( v == 0 )
     return;
 
-  if ( ! s_MapInit )
-    {
-      s_MapInit = true;
-      s_ResultMap[0].rcode = v;
-      s_ResultMap[0].result = this;
-      s_ResultMap[1].rcode = 0;
-      s_ResultMap[1].result = 0;
-      return;
-    }
+  // This may seem tricky, but it is certain that the static values declared in KM_error.h will
+  // be created (and thus this method will be called) before main(...) is called.  It is not
+  // until then that threads could be created, thus the mutex will exist before multi-threaded
+  // access could occur.
+  if ( s_MapLock == 0 )
+    s_MapLock = new Kumu::Mutex;
 
-  ui32_t i = 0;
-  while ( s_ResultMap[i].result != 0 && i < MapMax )
+  assert(s_MapLock);
+  AutoMutex L(*s_MapLock);
+
+  for ( ui32_t i = 0; i < s_MapSize; ++i )
     {
-      if ( s_ResultMap[i].rcode == v && s_ResultMap[i].result != 0 )
+      if ( s_ResultMap[i].rcode == v )
        return;
-
-      i++;
     }
 
-  assert(i+2 < MapMax);
+  assert(s_MapSize+1 < MapMax);
+
+  s_ResultMap[s_MapSize].rcode = v;
+  s_ResultMap[s_MapSize].result = this;
+  ++s_MapSize;
 
-  s_ResultMap[i].rcode = v;
-  s_ResultMap[i].result = this;
-  s_ResultMap[i+1].rcode = 0;
-  s_ResultMap[i+1].result = 0;
   return;
 }
 
+
+Kumu::Result_t::Result_t(const Result_t& rhs)
+{
+  value = rhs.value;
+  symbol = rhs.symbol;
+  label = rhs.label;
+  message = rhs.message;
+}
+
 Kumu::Result_t::~Result_t() {}
 
+//
+const Kumu::Result_t&
+Kumu::Result_t::operator=(const Result_t& rhs)
+{
+  value = rhs.value;
+  symbol = rhs.symbol;
+  label = rhs.label;
+  message = rhs.message;
+  return *this;
+}
+
+//
+const Kumu::Result_t
+Kumu::Result_t::operator()(const std::string& message) const
+{
+  Result_t result = *this;
+  result.message = message;
+  return result;
+}
+
+static int const MESSAGE_BUF_MAX = 2048;
+
+//
+const Kumu::Result_t
+Kumu::Result_t::operator()(const int& line, const char* filename) const
+{
+  assert(filename);
+  char buf[MESSAGE_BUF_MAX];
+  snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line);
+
+  Result_t result = *this;
+  result.message = buf;
+  return result;
+}
+
+//
+const Kumu::Result_t
+Kumu::Result_t::operator()(const std::string& message, const int& line, const char* filename) const
+{
+  assert(filename);
+  char buf[MESSAGE_BUF_MAX];
+  snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line);
+
+  Result_t result = *this;
+  result.message = message + buf;
+  return result;
+}
+
 
 //------------------------------------------------------------------------------------------
 // DTrace internals
@@ -537,9 +613,6 @@ Kumu::GenRandomValue(SymmetricKey& Key)
 
 
 //------------------------------------------------------------------------------------------
-// read a ber value from the buffer and compare with test value.
-// Advances buffer to first character after BER value.
-
 // read a ber value from the buffer and compare with test value.
 // Advances buffer to first character after BER value.
 //
@@ -605,6 +678,20 @@ static const ui64_t ber_masks[9] =
     0
   };
 
+//
+ui32_t
+Kumu::get_BER_length_for_value(ui64_t val)
+{
+  for ( ui32_t i = 0; i < 9; i++ )
+    {
+      if ( ( val & ber_masks[i] ) == 0 )
+       return i + 1;
+    }
+
+  ui64Printer tmp_i(val);
+  DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
+  return 0;
+}
 
 //
 bool
@@ -626,14 +713,14 @@ Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
     { // sanity check BER length
       if ( ber_len > 9 )
         {
-          DefaultLogSink().Error("BER size %u exceeds maximum size of 9\n", ber_len);
+          DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
           return false;
         }
       
-      if ( val & ber_masks[ber_len - 1] )
+      if ( ( val & ber_masks[ber_len - 1] ) != 0 )
         {
          ui64Printer tmp_i(val);
-          DefaultLogSink().Error("BER size %u too small for value %s\n", tmp_i.c_str());
+          DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
           return false;
         }
     }
@@ -651,302 +738,96 @@ Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
 
 
 //------------------------------------------------------------------------------------------
-#ifdef KM_WIN32
-
-#define TIMESTAMP_TO_SYSTIME(ts, t) \
-  (t)->wYear    = (ts).Year;   /* year */ \
-  (t)->wMonth   = (ts).Month;  /* month of year (1 - 12) */ \
-  (t)->wDay     = (ts).Day;    /* day of month (1 - 31) */ \
-  (t)->wHour    = (ts).Hour;   /* hours (0 - 23) */ \
-  (t)->wMinute  = (ts).Minute; /* minutes (0 - 59) */ \
-  (t)->wSecond  = (ts).Second; /* seconds (0 - 60) */ \
-  (t)->wDayOfWeek = 0; \
-  (t)->wMilliseconds = 0
-
-#define SYSTIME_TO_TIMESTAMP(t, ts) \
-  (ts).Year   = (t)->wYear;    /* year */ \
-  (ts).Month  = (t)->wMonth;   /* month of year (1 - 12) */ \
-  (ts).Day    = (t)->wDay;     /* day of month (1 - 31) */ \
-  (ts).Hour   = (t)->wHour;    /* hours (0 - 23) */ \
-  (ts).Minute = (t)->wMinute;  /* minutes (0 - 59) */ \
-  (ts).Second = (t)->wSecond;  /* seconds (0 - 60) */
 
-//
-Kumu::Timestamp::Timestamp() :
-  Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
-{
-  SYSTEMTIME sys_time;
-  GetSystemTime(&sys_time);
-  SYSTIME_TO_TIMESTAMP(&sys_time, *this);
-}
+#ifndef KM_WIN32
+#include <time.h>
+#endif
 
 //
-bool
-Kumu::Timestamp::operator<(const Timestamp& rhs) const
-{
-  SYSTEMTIME lhst, rhst;
-  FILETIME lft, rft;
-
-  TIMESTAMP_TO_SYSTIME(*this, &lhst);
-  TIMESTAMP_TO_SYSTIME(rhs, &rhst);
-  SystemTimeToFileTime(&lhst, &lft);
-  SystemTimeToFileTime(&rhst, &rft);
-  return ( CompareFileTime(&lft, &rft) == -1 );
+Kumu::Timestamp::Timestamp() : m_TZOffsetMinutes(0) {
+  m_Timestamp.now();
 }
 
-//
-bool
-Kumu::Timestamp::operator>(const Timestamp& rhs) const
-{
-  SYSTEMTIME lhst, rhst;
-  FILETIME lft, rft;
-
-  TIMESTAMP_TO_SYSTIME(*this, &lhst);
-  TIMESTAMP_TO_SYSTIME(rhs, &rhst);
-  SystemTimeToFileTime(&lhst, &lft);
-  SystemTimeToFileTime(&rhst, &rft);
-  return ( CompareFileTime(&lft, &rft) == 1 );
+Kumu::Timestamp::Timestamp(const Timestamp& rhs) {
+  m_Timestamp = rhs.m_Timestamp;
+  m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
 }
 
-inline ui64_t
-seconds_to_ns100(ui32_t seconds)
-{
-  return ((ui64_t)seconds * 10000000);
+Kumu::Timestamp::Timestamp(const char* datestr) : m_TZOffsetMinutes(0) {
+  DecodeString(datestr);
 }
 
-//
-void
-Kumu::Timestamp::AddDays(i32_t days)
-{
-  SYSTEMTIME current_st;
-  FILETIME current_ft;
-  ULARGE_INTEGER current_ul;
-
-  if ( days != 0 )
-    {
-      TIMESTAMP_TO_SYSTIME(*this, &current_st);
-      SystemTimeToFileTime(&current_st, &current_ft);
-      memcpy(&current_ul, &current_ft, sizeof(current_ul));
-      current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
-      memcpy(&current_ft, &current_ul, sizeof(current_ft));
-      FileTimeToSystemTime(&current_ft, &current_st);
-      SYSTIME_TO_TIMESTAMP(&current_st, *this);
-    }
+Kumu::Timestamp::~Timestamp() {
 }
 
 //
-void
-Kumu::Timestamp::AddHours(i32_t hours)
+const Kumu::Timestamp&
+Kumu::Timestamp::operator=(const Timestamp& rhs)
 {
-  SYSTEMTIME current_st;
-  FILETIME current_ft;
-  ULARGE_INTEGER current_ul;
-
-  if ( hours != 0 )
-    {
-      TIMESTAMP_TO_SYSTIME(*this, &current_st);
-      SystemTimeToFileTime(&current_st, &current_ft);
-      memcpy(&current_ul, &current_ft, sizeof(current_ul));
-      current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
-      memcpy(&current_ft, &current_ul, sizeof(current_ft));
-      FileTimeToSystemTime(&current_ft, &current_st);
-      SYSTIME_TO_TIMESTAMP(&current_st, *this);
-    }
+  m_Timestamp = rhs.m_Timestamp;
+  m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
+  return *this;
 }
 
-//
-void
-Kumu::Timestamp::AddMinutes(i32_t minutes)
-{
-  SYSTEMTIME current_st;
-  FILETIME current_ft;
-  ULARGE_INTEGER current_ul;
-
-  if ( minutes != 0 )
-    {
-      TIMESTAMP_TO_SYSTIME(*this, &current_st);
-      SystemTimeToFileTime(&current_st, &current_ft);
-      memcpy(&current_ul, &current_ft, sizeof(current_ul));
-      current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
-      memcpy(&current_ft, &current_ul, sizeof(current_ft));
-      FileTimeToSystemTime(&current_ft, &current_st);
-      SYSTIME_TO_TIMESTAMP(&current_st, *this);
-    }
+bool Kumu::Timestamp::operator<(const Timestamp& rhs) const {
+  return m_Timestamp.x < rhs.m_Timestamp.x;
 }
 
-#else // KM_WIN32
-
-#include <time.h>
-
-#define TIMESTAMP_TO_CALTIME(ts, ct)                                  \
-  (ct)->date.year  = (ts).Year;    /* year */                         \
-  (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */               \
-  (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */        \
-  (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                       \
-  (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */             \
-  (ct)->second     = (ts).Second;  /* seconds (0 - 60) */             \
-  (ct)->offset     = 0;
-
-#define CALTIME_TO_TIMESTAMP(ct, ts)                                  \
-  assert((ct)->offset == 0);                                          \
-  (ts).Year   = (ct)->date.year;    /* year */                        \
-  (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
-  (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */               \
-  (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */              \
-  (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */            \
-  (ts).Second = (ct)->second;       /* seconds (0 - 60) */
-
-
-//
-Kumu::Timestamp::Timestamp() :
-  Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
-{
-  Kumu::TAI::tai now;
-  Kumu::TAI::caltime ct;
-  now.now();
-  ct = now;
-  CALTIME_TO_TIMESTAMP(&ct, *this)
+bool Kumu::Timestamp::operator>(const Timestamp& rhs) const {
+  return m_Timestamp.x > rhs.m_Timestamp.x;
 }
 
-//
-bool
-Kumu::Timestamp::operator<(const Timestamp& rhs) const
-{
-  Kumu::TAI::caltime lh_ct, rh_ct;
-  TIMESTAMP_TO_CALTIME(*this, &lh_ct)
-  TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
-
-  Kumu::TAI::tai lh_tai, rh_tai;
-  lh_tai = lh_ct;
-  rh_tai = rh_ct;
-
-  return ( lh_tai.x < rh_tai.x );
+bool Kumu::Timestamp::operator==(const Timestamp& rhs) const {
+  return m_Timestamp.x == rhs.m_Timestamp.x;
 }
 
-//
-bool
-Kumu::Timestamp::operator>(const Timestamp& rhs) const
-{
-  Kumu::TAI::caltime lh_ct, rh_ct;
-  TIMESTAMP_TO_CALTIME(*this, &lh_ct)
-  TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
-
-  Kumu::TAI::tai lh_tai, rh_tai;
-  lh_tai = lh_ct;
-  rh_tai = rh_ct;
-
-  return ( lh_tai.x > rh_tai.x );
+bool Kumu::Timestamp::operator!=(const Timestamp& rhs) const {
+  return m_Timestamp.x != rhs.m_Timestamp.x;
 }
 
 //
 void
-Kumu::Timestamp::AddDays(i32_t days)
+Kumu::Timestamp::GetComponents(ui16_t& Year, ui8_t&  Month, ui8_t&  Day,
+                              ui8_t&  Hour, ui8_t&  Minute, ui8_t&  Second) const
 {
-  Kumu::TAI::caltime ct;
-  Kumu::TAI::tai t;
-
-  if ( days != 0 )
-    {
-      TIMESTAMP_TO_CALTIME(*this, &ct)
-      t = ct;
-      t.add_days(days);
-      ct = t;
-      CALTIME_TO_TIMESTAMP(&ct, *this)
-    }
+  TAI::caltime ct;
+  ct = m_Timestamp;
+  Year = ct.date.year;
+  Month = ct.date.month;
+  Day = ct.date.day;
+  Hour = ct.hour;
+  Minute = ct.minute;
+  Second = ct.second;
 }
 
 //
 void
-Kumu::Timestamp::AddHours(i32_t hours)
-{
-  Kumu::TAI::caltime ct;
-  Kumu::TAI::tai t;
-
-  if ( hours != 0 )
-    {
-      TIMESTAMP_TO_CALTIME(*this, &ct)
-      t = ct;
-      t.add_hours(hours);
-      ct = t;
-      CALTIME_TO_TIMESTAMP(&ct, *this)
-    }
-}
-
-//
-void
-Kumu::Timestamp::AddMinutes(i32_t minutes)
-{
-  Kumu::TAI::caltime ct;
-  Kumu::TAI::tai t;
-
-  if ( minutes != 0 )
-    {
-      TIMESTAMP_TO_CALTIME(*this, &ct)
-      t = ct;
-      t.add_minutes(minutes);
-      ct = t;
-      CALTIME_TO_TIMESTAMP(&ct, *this)
-    }
-}
-
-#endif // KM_WIN32
-
-
-Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
-{
-  Year   = rhs.Year;
-  Month  = rhs.Month;
-  Day    = rhs.Day;
-  Hour   = rhs.Hour;
-  Minute = rhs.Minute;
-  Second = rhs.Second;
-}
-
-Kumu::Timestamp::~Timestamp()
+Kumu::Timestamp::SetComponents(const ui16_t& Year, const ui8_t&  Month, const ui8_t&  Day,
+                              const ui8_t&  Hour, const ui8_t&  Minute, const ui8_t&  Second)
 {
+  TAI::caltime ct;
+  ct.date.year = Year;
+  ct.date.month = Month;
+  ct.date.day = Day;
+  ct.hour = Hour;
+  ct.minute = Minute;
+  ct.second = Second;
+  ct.offset = 0;
+  m_Timestamp = ct;
+  m_TZOffsetMinutes = 0;
 }
 
-//
-const Kumu::Timestamp&
-Kumu::Timestamp::operator=(const Timestamp& rhs)
-{
-  Year   = rhs.Year;
-  Month  = rhs.Month;
-  Day    = rhs.Day;
-  Hour   = rhs.Hour;
-  Minute = rhs.Minute;
-  Second = rhs.Second;
-  return *this;
-}
-
-//
+// returns false if the requested adjustment is out of range
 bool
-Kumu::Timestamp::operator==(const Timestamp& rhs) const
+Kumu::Timestamp::SetTZOffsetMinutes(const i32_t& minutes)
 {
-  if ( Year == rhs.Year
-       && Month  == rhs.Month
-       && Day    == rhs.Day
-       && Hour   == rhs.Hour
-       && Minute == rhs.Minute
-       && Second == rhs.Second )
-    return true;
-
-  return false;
-}
+  static const i32_t tz_limit = 14 * 60 * 60;
 
-//
-bool
-Kumu::Timestamp::operator!=(const Timestamp& rhs) const
-{
-  if ( Year != rhs.Year
-       || Month  != rhs.Month
-       || Day    != rhs.Day
-       || Hour   != rhs.Hour
-       || Minute != rhs.Minute
-       || Second != rhs.Second )
-    return true;
-
-  return false;
+  if ( minutes < ( - tz_limit) || minutes > tz_limit )
+    return false;
+
+  m_TZOffsetMinutes = minutes;
+  return true;
 }
 
 // 
@@ -956,20 +837,42 @@ Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
   if ( buf_len < ( DateTimeLen + 1 ) )
     return 0;
 
-  // 2004-05-01T13:20:00-00:00
-  snprintf(str_buf, buf_len,
-          "%04hu-%02hu-%02huT%02hu:%02hu:%02hu+00:00",
-          Year, Month, Day, Hour, Minute, Second);
+  ui16_t year;
+  ui8_t month, day, hour, minute, second;
+  ui32_t ofst_hours = 0, ofst_minutes = 0;
+  char direction = '+';
+
+  if ( m_TZOffsetMinutes == 0 )
+    {
+      GetComponents(year, month, day, hour, minute, second);
+    }
+  else
+    {
+      // calculate local time
+      Kumu::Timestamp tmp_t(*this);
+      tmp_t.AddMinutes(m_TZOffsetMinutes);
+      tmp_t.GetComponents(year, month, day, hour, minute, second);
+
+      ofst_hours = Kumu::xabs(m_TZOffsetMinutes) / 60;
+      ofst_minutes = Kumu::xabs(m_TZOffsetMinutes) % 60;
+
+      if ( m_TZOffsetMinutes < 0 )
+       direction = '-';
+    }
   
+  // 2004-05-01T13:20:00+00:00
+  snprintf(str_buf, buf_len,
+          "%04hu-%02hhu-%02hhuT%02hhu:%02hhu:%02hhu%c%02u:%02u",
+          year, month, day, hour, minute, second,
+          direction, ofst_hours, ofst_minutes);
+
   return str_buf;
 }
 
-//
+// ^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:([+-]\d{2}):(\d{2}))?)?$
 bool
 Kumu::Timestamp::DecodeString(const char* datestr)
 {
-  Timestamp TmpStamp;
-
   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
        || datestr[4] != '-'
        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
@@ -978,10 +881,14 @@ Kumu::Timestamp::DecodeString(const char* datestr)
     return false;
 
   ui32_t char_count = 10;
-  TmpStamp.Year = atoi(datestr);
-  TmpStamp.Month = atoi(datestr + 5);
-  TmpStamp.Day = atoi(datestr + 8);
-  TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
+  TAI::caltime YMDhms;
+  YMDhms.hour = 0;
+  YMDhms.minute = 0;
+  YMDhms.second = 0;
+  YMDhms.offset = 0;
+  YMDhms.date.year = strtol(datestr, 0, 10);
+  YMDhms.date.month = strtol(datestr + 5, 0, 10);
+  YMDhms.date.day = strtol(datestr + 8, 0, 10);
  
   if ( datestr[10] == 'T' )
     {
@@ -991,8 +898,8 @@ Kumu::Timestamp::DecodeString(const char* datestr)
        return false;
 
       char_count += 6;
-      TmpStamp.Hour = atoi(datestr + 11);
-      TmpStamp.Minute = atoi(datestr + 14);
+      YMDhms.hour = strtol(datestr + 11, 0, 10);
+      YMDhms.minute = strtol(datestr + 14, 0, 10);
 
       if ( datestr[16] == ':' )
        {
@@ -1000,16 +907,23 @@ Kumu::Timestamp::DecodeString(const char* datestr)
            return false;
 
          char_count += 3;
-         TmpStamp.Second = atoi(datestr + 17);
+         YMDhms.second = strtol(datestr + 17, 0, 10);
        }
 
       if ( datestr[19] == '.' )
        {
-         if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
-           return false;
-         
+         if ( ! isdigit(datestr[20]) )
+           {
+             return false;
+           }
+
          // we don't carry the ms value
-         datestr += 4;
+         while ( isdigit(datestr[20]) )
+           {
+             ++datestr;
+           }
+
+         ++datestr;
        }
 
       if ( datestr[19] == '-' || datestr[19] == '+' )
@@ -1021,8 +935,8 @@ Kumu::Timestamp::DecodeString(const char* datestr)
 
          char_count += 6;
 
-         ui32_t TZ_hh = atoi(datestr + 20);
-         ui32_t TZ_mm = atoi(datestr + 23);
+         ui32_t TZ_hh = strtol(datestr + 20, 0, 10);
+         ui32_t TZ_mm = strtol(datestr + 23, 0, 10);
          if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
            return false;
 
@@ -1033,7 +947,7 @@ Kumu::Timestamp::DecodeString(const char* datestr)
 
          /* a negative offset is behind UTC and so needs to increment to
           * convert, while a positive offset must do the reverse */
-         TmpStamp.AddMinutes(-TZ_offset);
+         YMDhms.offset = TZ_offset;
        }
       else if (datestr[19] == 'Z')
        {
@@ -1048,23 +962,9 @@ Kumu::Timestamp::DecodeString(const char* datestr)
                                   datestr, char_count);
       return false;
     }
-
-#ifdef KM_WIN32
-  SYSTEMTIME st;
-  FILETIME ft;
-  TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
-  if ( SystemTimeToFileTime(&st, &ft) == 0 )
-    return false;
-  SYSTIME_TO_TIMESTAMP(&st, *this);
-#else
-  Kumu::TAI::tai t;
-  Kumu::TAI::caltime ct;
-  TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
-  t = ct; // back and forth to tai to normalize offset
-  ct = t;
-  CALTIME_TO_TIMESTAMP(&ct, *this)
-#endif
-
+  
+  m_Timestamp = YMDhms;
+  m_TZOffsetMinutes = YMDhms.offset;
   return true;
 }
 
@@ -1072,19 +972,25 @@ Kumu::Timestamp::DecodeString(const char* datestr)
 bool
 Kumu::Timestamp::HasValue() const
 {
-  if ( Year || Month || Day || Hour || Minute || Second )
-    return true;
-
-  return false;
+  return true;
 }
 
 //
 bool
 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
 {
+  ui16_t year;
+  ui8_t month, day, hour, minute, second, tick;
+
   assert(Reader);
-  if ( ! Reader->ReadUi16BE(&Year) ) return false;     
-  if ( ! Reader->ReadRaw(&Month, 6) ) return false;
+  if ( ! Reader->ReadUi16BE(&year) ) return false;
+  if ( ! Reader->ReadUi8(&month) ) return false;
+  if ( ! Reader->ReadUi8(&day) ) return false;
+  if ( ! Reader->ReadUi8(&hour) ) return false;
+  if ( ! Reader->ReadUi8(&minute) ) return false;
+  if ( ! Reader->ReadUi8(&second) ) return false;
+  if ( ! Reader->ReadUi8(&tick) ) return false;
+  SetComponents(year, month, day, hour, minute, second);
   return true;
 }
 
@@ -1093,11 +999,38 @@ bool
 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
 {
   assert(Writer);
-  if ( ! Writer->WriteUi16BE(Year) ) return false;     
-  if ( ! Writer->WriteRaw(&Month, 6) ) return false;
+
+  ui16_t year;
+  ui8_t month, day, hour, minute, second, tick = 0;
+  GetComponents(year, month, day, hour, minute, second);
+
+  if ( ! Writer->WriteUi16BE(year) ) return false;     
+  if ( ! Writer->WriteUi8(month) ) return false;
+  if ( ! Writer->WriteUi8(day) ) return false;
+  if ( ! Writer->WriteUi8(hour) ) return false;
+  if ( ! Writer->WriteUi8(minute) ) return false;
+  if ( ! Writer->WriteUi8(second) ) return false;
+  if ( ! Writer->WriteUi8(tick) ) return false;
   return true;
 }
 
+//
+ui64_t
+Kumu::Timestamp::GetCTime() const
+{
+  return m_Timestamp.x - ui64_C(4611686018427387914);
+}
+
+//
+void
+Kumu::Timestamp::SetCTime(const ui64_t& ctime)
+{
+  m_Timestamp.x = ctime + ui64_C(4611686018427387914);
+}
+
+
+
+
 //------------------------------------------------------------------------------------------
 
 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
@@ -1260,6 +1193,58 @@ Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
   return result;
 }
 
+//------------------------------------------------------------------------------------------
+
+//
+const char*
+Kumu::km_strnstr(const char *s, const char *find, size_t slen)
+{
+  char c, sc;
+  size_t len;
+
+  if ( ( c = *find++ ) != '\0' )
+    {
+      len = strlen(find);
+      do
+       {
+         do
+           {
+             if ( slen-- < 1 || ( sc = *s++ ) == '\0' )
+               return 0;
+           }
+         while ( sc != c );
+
+         if ( len > slen )
+           return 0;
+       }
+      while ( strncmp(s, find, len) != 0 );
+      --s;
+    }
+
+  return s;
+}
+
+//
+std::list<std::string>
+Kumu::km_token_split(const std::string& str, const std::string& separator)
+{
+  std::list<std::string> components;
+  const char* pstr = str.c_str();
+  const char* r = strstr(pstr, separator.c_str());
+
+  while ( r != 0 )
+    {
+      assert(r >= pstr);
+      std::string tmp_str;
+      tmp_str.assign(pstr, r - pstr);
+      components.push_back(tmp_str);
+      pstr = r + separator.size();
+      r = strstr(pstr, separator.c_str());
+    }
+      
+  components.push_back(std::string(pstr));
+  return components;
+}
 
 //
 // end KM_util.cpp