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 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 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 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 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