1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "google_apis/drive/time_util.h"
6
7#include <string>
8#include <vector>
9
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/time/time.h"
14
15namespace google_apis {
16namespace util {
17
18namespace {
19
20const char kNullTimeString[] = "null";
21
22bool ParseTimezone(const base::StringPiece& timezone,
23                   bool ahead,
24                   int* out_offset_to_utc_in_minutes) {
25  DCHECK(out_offset_to_utc_in_minutes);
26
27  std::vector<base::StringPiece> parts;
28  int num_of_token = Tokenize(timezone, ":", &parts);
29
30  int hour = 0;
31  if (!base::StringToInt(parts[0], &hour))
32    return false;
33
34  int minute = 0;
35  if (num_of_token > 1 && !base::StringToInt(parts[1], &minute))
36    return false;
37
38  *out_offset_to_utc_in_minutes = (hour * 60 + minute) * (ahead ? +1 : -1);
39  return true;
40}
41
42}  // namespace
43
44bool GetTimeFromString(const base::StringPiece& raw_value,
45                       base::Time* parsed_time) {
46  base::StringPiece date;
47  base::StringPiece time_and_tz;
48  base::StringPiece time;
49  base::Time::Exploded exploded = {0};
50  bool has_timezone = false;
51  int offset_to_utc_in_minutes = 0;
52
53  // Splits the string into "date" part and "time" part.
54  {
55    std::vector<base::StringPiece> parts;
56    if (Tokenize(raw_value, "T", &parts) != 2)
57      return false;
58    date = parts[0];
59    time_and_tz = parts[1];
60  }
61
62  // Parses timezone suffix on the time part if available.
63  {
64    std::vector<base::StringPiece> parts;
65    if (time_and_tz[time_and_tz.size() - 1] == 'Z') {
66      // Timezone is 'Z' (UTC)
67      has_timezone = true;
68      offset_to_utc_in_minutes = 0;
69      time = time_and_tz;
70      time.remove_suffix(1);
71    } else if (Tokenize(time_and_tz, "+", &parts) == 2) {
72      // Timezone is "+hh:mm" format
73      if (!ParseTimezone(parts[1], true, &offset_to_utc_in_minutes))
74        return false;
75      has_timezone = true;
76      time = parts[0];
77    } else if (Tokenize(time_and_tz, "-", &parts) == 2) {
78      // Timezone is "-hh:mm" format
79      if (!ParseTimezone(parts[1], false, &offset_to_utc_in_minutes))
80        return false;
81      has_timezone = true;
82      time = parts[0];
83    } else {
84      // No timezone (uses local timezone)
85      time = time_and_tz;
86    }
87  }
88
89  // Parses the date part.
90  {
91    std::vector<base::StringPiece> parts;
92    if (Tokenize(date, "-", &parts) != 3)
93      return false;
94
95    if (!base::StringToInt(parts[0], &exploded.year) ||
96        !base::StringToInt(parts[1], &exploded.month) ||
97        !base::StringToInt(parts[2], &exploded.day_of_month)) {
98      return false;
99    }
100  }
101
102  // Parses the time part.
103  {
104    std::vector<base::StringPiece> parts;
105    int num_of_token = Tokenize(time, ":", &parts);
106    if (num_of_token != 3)
107      return false;
108
109    if (!base::StringToInt(parts[0], &exploded.hour) ||
110        !base::StringToInt(parts[1], &exploded.minute)) {
111      return false;
112    }
113
114    std::vector<base::StringPiece> seconds_parts;
115    int num_of_seconds_token = Tokenize(parts[2], ".", &seconds_parts);
116    if (num_of_seconds_token >= 3)
117      return false;
118
119    if (!base::StringToInt(seconds_parts[0], &exploded.second))
120        return false;
121
122    // Only accept milli-seconds (3-digits).
123    if (num_of_seconds_token > 1 &&
124        seconds_parts[1].length() == 3 &&
125        !base::StringToInt(seconds_parts[1], &exploded.millisecond)) {
126      return false;
127    }
128  }
129
130  exploded.day_of_week = 0;
131  if (!exploded.HasValidValues())
132    return false;
133
134  if (has_timezone) {
135    *parsed_time = base::Time::FromUTCExploded(exploded);
136    if (offset_to_utc_in_minutes != 0)
137      *parsed_time -= base::TimeDelta::FromMinutes(offset_to_utc_in_minutes);
138  } else {
139    *parsed_time = base::Time::FromLocalExploded(exploded);
140  }
141
142  return true;
143}
144
145std::string FormatTimeAsString(const base::Time& time) {
146  if (time.is_null())
147    return kNullTimeString;
148
149  base::Time::Exploded exploded;
150  time.UTCExplode(&exploded);
151  return base::StringPrintf(
152      "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
153      exploded.year, exploded.month, exploded.day_of_month,
154      exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
155}
156
157std::string FormatTimeAsStringLocaltime(const base::Time& time) {
158  if (time.is_null())
159    return kNullTimeString;
160
161  base::Time::Exploded exploded;
162  time.LocalExplode(&exploded);
163  return base::StringPrintf(
164      "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
165      exploded.year, exploded.month, exploded.day_of_month,
166      exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
167}
168
169}  // namespace util
170}  // namespace google_apis
171