text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "google_apis/drive/time_util.h"
  
  #include <string>
  #include <vector>
  
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_split.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "base/time/time.h"
  #include "base/time/time_to_iso8601.h"
  
  namespace google_apis {
  namespace util {
  
  namespace {
  
  const char kNullTimeString[] = "null";
  
  bool ParseTimezone(base::StringPiece timezone,
                     bool ahead,
                     int* out_offset_to_utc_in_minutes) {
    DCHECK(out_offset_to_utc_in_minutes);
  
    std::vector<base::StringPiece> parts = base::SplitStringPiece(
        timezone, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  
    int hour = 0;
    if (parts.empty() || !base::StringToInt(parts[0], &hour))
      return false;
  
    int minute = 0;
    if (parts.size() > 1 && !base::StringToInt(parts[1], &minute))
      return false;
  
    *out_offset_to_utc_in_minutes = (hour * 60 + minute) * (ahead ? +1 : -1);
    return true;
  }
  
  }  // namespace
  
  bool GetTimeFromString(base::StringPiece raw_value, base::Time* parsed_time) {
    base::StringPiece date;
    base::StringPiece time_and_tz;
    base::StringPiece time;
    base::Time::Exploded exploded = {0};
    bool has_timezone = false;
    int offset_to_utc_in_minutes = 0;
  
    // Splits the string into "date" part and "time" part.
    {
      std::vector<base::StringPiece> parts = base::SplitStringPiece(
          raw_value, "T", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
      if (parts.size() != 2)
        return false;
      date = parts[0];
      time_and_tz = parts[1];
    }
  
    // Parses timezone suffix on the time part if available.
    {
      std::vector<base::StringPiece> parts;
      if (time_and_tz.back() == 'Z') {
        // Timezone is 'Z' (UTC)
        has_timezone = true;
        offset_to_utc_in_minutes = 0;
        time = time_and_tz;
        time.remove_suffix(1);
      } else {
        parts = base::SplitStringPiece(
            time_and_tz, "+", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
        if (parts.size() == 2) {
          // Timezone is "+hh:mm" format
          if (!ParseTimezone(parts[1], true, &offset_to_utc_in_minutes))
            return false;
          has_timezone = true;
          time = parts[0];
        } else {
          parts = base::SplitStringPiece(
              time_and_tz, "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
          if (parts.size() == 2) {
            // Timezone is "-hh:mm" format
            if (!ParseTimezone(parts[1], false, &offset_to_utc_in_minutes))
              return false;
            has_timezone = true;
            time = parts[0];
          } else {
            // No timezone (uses local timezone)
            time = time_and_tz;
          }
        }
      }
    }
  
    // Parses the date part.
    {
      std::vector<base::StringPiece> parts = base::SplitStringPiece(
          date, "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
      if (parts.size() != 3)
        return false;
  
      if (!base::StringToInt(parts[0], &exploded.year) ||
          !base::StringToInt(parts[1], &exploded.month) ||
          !base::StringToInt(parts[2], &exploded.day_of_month)) {
        return false;
      }
    }
  
    // Parses the time part.
    {
      std::vector<base::StringPiece> parts = base::SplitStringPiece(
          time, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
      if (parts.size() != 3)
        return false;
  
      if (!base::StringToInt(parts[0], &exploded.hour) ||
          !base::StringToInt(parts[1], &exploded.minute)) {
        return false;
      }
  
      std::vector<base::StringPiece> seconds_parts = base::SplitStringPiece(
          parts[2], ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
      if (seconds_parts.size() >= 3)
        return false;
  
      if (!base::StringToInt(seconds_parts[0], &exploded.second))
        return false;
  
      // Only accept milli-seconds (3-digits).
      if (seconds_parts.size() > 1 &&
          seconds_parts[1].length() == 3 &&
          !base::StringToInt(seconds_parts[1], &exploded.millisecond)) {
        return false;
      }
    }
  
    exploded.day_of_week = 0;
    if (!exploded.HasValidValues())
      return false;
  
    if (has_timezone) {
      if (!base::Time::FromUTCExploded(exploded, parsed_time))
        return false;
      if (offset_to_utc_in_minutes != 0)
        *parsed_time -= base::TimeDelta::FromMinutes(offset_to_utc_in_minutes);
    } else {
      if (!base::Time::FromLocalExploded(exploded, parsed_time))
        return false;
    }
  
    return true;
  }
  
  std::string FormatTimeAsString(const base::Time& time) {
    if (time.is_null())
      return kNullTimeString;
  
    return base::TimeToISO8601(time);
  }
  
  std::string FormatTimeAsStringLocaltime(const base::Time& time) {
    if (time.is_null())
      return kNullTimeString;
  
    base::Time::Exploded exploded;
    time.LocalExplode(&exploded);
    return base::StringPrintf(
        "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
        exploded.year, exploded.month, exploded.day_of_month,
        exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
  }
  
  }  // namespace util
  }  // namespace google_apis
  