| // 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" |
| |
| namespace google_apis { |
| namespace util { |
| |
| namespace { |
| |
| const char kNullTimeString[] = "null"; |
| |
| bool ParseTimezone(const 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(const 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; |
| |
| base::Time::Exploded exploded; |
| time.UTCExplode(&exploded); |
| return base::StringPrintf( |
| "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", |
| exploded.year, exploded.month, exploded.day_of_month, |
| exploded.hour, exploded.minute, exploded.second, exploded.millisecond); |
| } |
| |
| 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 |