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
15 namespace google_apis {
16 namespace util {
17
18 namespace {
19
20 const char kNullTimeString[] = "null";
21
ParseTimezone(const base::StringPiece & timezone,bool ahead,int * out_offset_to_utc_in_minutes)22 bool 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
GetTimeFromString(const base::StringPiece & raw_value,base::Time * parsed_time)44 bool 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
FormatTimeAsString(const base::Time & time)145 std::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
FormatTimeAsStringLocaltime(const base::Time & time)157 std::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