Timespan from/to String Conversion

Warning! Some information on this page is older than 5 years now. I keep it for reference, but it probably doesn't reflect my current knowledge and beliefs.

Tue
29
Jun 2010

Today I'd like to show a piece of code I've written today. These are functions for converting between a timespan, stored in a int64 as number of milliseconds and string in for of "HH:MM:SS.MMM" (hours, minutes, seconds, milliseconds). That's IMHO a convenient way of presenting timespan to the user. I'll include these functions in the next version of my CommonLib library, to work with raw millisecond number as well as TIMESPAN and/or GameTime types.

The code is in C++, uses STL string and my own StrToUint and UintToStr functions. All suggestions about how these algorithms could be optimized are welcome.

// Returns string in form of "HH:MM:SS" or "HH:MM:SS.MMM".
void MillisecondsToStr(string &out, int64 msec, bool showMilliseconds)
{
  out.clear();
  if (showMilliseconds)
    out.reserve(13);
  else
    out.reserve(9);

  if (msec < 0)
  {
    out += '-';
    msec = -msec;
  }

  int64 seconds = msec / 1000;
  msec -= seconds * 1000;
  int minutes = (int)(seconds / 60);
  seconds -= minutes * 60;
  int hours = minutes / 60;
  minutes -= hours * 60;

  string s;
  UintToStr(&s, hours);
  out.append(s);
  out += ':';
  UintToStr2(&s, minutes, 2); // At least 2 digits, padded with zeros.
  out.append(s);
  out += ':';
  UintToStr2(&s, seconds, 2); // At least 2 digits, padded with zeros.
  out.append(s);

  if (showMilliseconds)
  {
    out += '.';
    UintToStr2(&s, msec, 3); // At least 3 digits, padded with zeros.
    out.append(s);
  }
}

// Accepts string in form of "HH:MM:SS.MMM", as well as some variations like
// "HH:MM:SS", "SS.MMM" etc.
bool StrToMilliseconds(int64 &out_msec, const string &str)
{
  if (str.empty())
    return false;

  uint pos = 0;
  bool negative = str[0] == '-';
  if (negative)
    ++pos;

  // stage == 0 means beginning.
  //   out_msec == 0.
  // stage == 1 means parsed one number before ':' (hours or minutes).
  //   out_msec is in hours or minutes.
  // stage == 2 means parsed two numbers before ':' - hours and minutes.
  //   out_msec is in minutes.
  // stage == 3 means we have to parse mseconds now.
  //   out_msec is in mseconds.
  out_msec = 0;
  uint componentX, findPos, stage = 0;

  // Process subsequent numbers between beginning, separator chars ":." and end
  // of the input string.
  do
  {
    findPos = str.find_first_of(":.", pos);
    // separator char at first position where number expected.
    if (findPos == pos)
      return false;
    // StrToUint returns nonzero on error.
    if (StrToUint(&componentX, str.substr(pos, findPos - pos)) != 0)
      return false;

    // We have to parse mseconds now as only this remains.
    if (stage == 3)
    {
      // We must be at the end of string.
      if (findPos != string::npos)
        return false;
      out_msec += componentX;
    }
    else
    {
      // We for sure now parse seconds.
      if (findPos == string::npos || str[findPos] == '.')
      {
        // out_msec is in minutes, make it seconds.
        if (stage != 0 && stage != 1 && stage != 2)
          return false;
        out_msec *= 60;

        out_msec = (out_msec + componentX) * 1000;
        stage = 3;
      }
      // We parse hours or minutes - something before ':'.
      else
      {
        // First value
        if (stage == 0)
        {
          out_msec = componentX;
          stage = 1;
        }
        // We surely parse minutes now, out_msec is in hours.
        else if (stage == 1)
        {
          out_msec = out_msec * 60 + componentX;
          stage = 2;
        }
        else
          return false;
      }
    }

    pos = findPos + 1; // Wraps around in the last iteration, but don't care.
  }
  while (findPos != string::npos);

  if (negative)
    out_msec = -out_msec;
  return true;
}

Comments | #algorithms #c++ Share

Comments

[Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2019