| 94 | QString format_utc_offset(void) |
| 95 | { |
| 96 | char offset[32]; |
| 97 | int offset_total_seconds, offset_hours, offset_minutes; |
| 98 | QString offset_string("UNDEF"); |
| 99 | |
| 100 | offset_total_seconds = calc_utc_offset(); |
| 101 | |
| 102 | offset_hours = offset_total_seconds / 3600; |
| 103 | offset_minutes = abs((offset_total_seconds % 3600) / 60); |
| 104 | |
| 105 | if (sprintf(offset, "%.2i:%.2i", offset_hours, offset_minutes) > 0) |
| 106 | offset_string = offset; |
| 107 | |
| 108 | return offset_string; |
| 109 | } |
| 110 | |
| 111 | #ifndef USING_MINGW |
| 112 | /* Helper function for getSystemTimeZoneID() that compares the |
| 113 | zoneinfo_file_path (regular) file with files in the zoneinfo_dir_path until |
| 114 | it finds a match. The matching file's name is used to determine the time |
| 115 | zone ID. */ |
| 116 | static QString findZoneinfoFile(QString zoneinfo_file_path, |
| 117 | QString zoneinfo_dir_path) |
| 118 | { |
| 119 | QString zone_id("UNDEF"); |
| 120 | QDir zoneinfo_dir(zoneinfo_dir_path); |
| 121 | QFileInfoList dirlist = zoneinfo_dir.entryInfoList(); |
| 122 | QFileInfo info; |
| 123 | QString basename; |
| 124 | QFile zoneinfo_file(zoneinfo_file_path); |
| 125 | qint64 zoneinfo_file_size = zoneinfo_file.size(); |
| 126 | |
| 127 | for (QFileInfoList::const_iterator it = dirlist.begin(); |
| 128 | it != dirlist.end(); it++) |
| 129 | { |
| 130 | info = *it; |
| 131 | // Skip '.' and '..' and other files starting with "." and |
| 132 | // skip localtime (which is often a link to zoneinfo_file_path) |
| 133 | basename = info.baseName(); |
| 134 | if (basename.isEmpty() || (basename == "localtime")) { |
| 135 | continue; |
| 136 | } |
| 137 | if (info.isDir()) |
| 138 | { |
| 139 | zone_id = findZoneinfoFile(zoneinfo_file_path, |
| 140 | info.absoluteFilePath()); |
| 141 | if (zone_id != "UNDEF") |
| 142 | return zone_id; |
| 143 | } |
| 144 | else if (info.isFile() && (info.size() == zoneinfo_file_size) && |
| 145 | info.isReadable()) |
| 146 | { |
| 147 | // sanity check - zoneinfo files should typically be less than |
| 148 | // about 4kB, but leave room for growth |
| 149 | if (zoneinfo_file_size > 200 * 1024) |
| 150 | continue; |
| 151 | QFile this_file(info.absoluteFilePath()); |
| 152 | QByteArray zoneinfo_file_data; |
| 153 | zoneinfo_file_data.resize(zoneinfo_file_size); |
| 154 | QByteArray this_file_data; |
| 155 | this_file_data.resize(zoneinfo_file_size); |
| 156 | if (zoneinfo_file.open(QIODevice::ReadOnly)) |
| 157 | { |
| 158 | QDataStream in(&zoneinfo_file); |
| 159 | if (in.readRawData(zoneinfo_file_data.data(), |
| 160 | zoneinfo_file_size) != zoneinfo_file_size) |
| 161 | { |
| 162 | zoneinfo_file.close(); |
| 163 | return zone_id; |
| 164 | } |
| 165 | zoneinfo_file.close(); |
| 166 | } |
| 167 | if (this_file.open(QIODevice::ReadOnly)) |
| 168 | { |
| 169 | QDataStream in(&this_file); |
| 170 | if (in.readRawData(this_file_data.data(), |
| 171 | zoneinfo_file_size) != zoneinfo_file_size) |
| 172 | { |
| 173 | this_file.close(); |
| 174 | return zone_id; |
| 175 | } |
| 176 | this_file.close(); |
| 177 | } |
| 178 | if (zoneinfo_file_data == this_file_data) |
| 179 | { |
| 180 | zone_id = info.absoluteFilePath(); |
| 181 | break; |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | return zone_id; |
| 186 | } |
| 187 | #endif |
| 188 | |
| 189 | /* Helper function for getTimeZoneID() that provides an unprocessed time zone |
| 190 | id obtained using system-dependent means of identifying the system's time |
| 191 | zone. */ |
| 192 | static QString getSystemTimeZoneID(void) |
| 193 | { |
| 194 | QString zone_id("UNDEF"); |
| 195 | #ifndef USING_MINGW |
| 196 | // Try to determine the time zone information by inspecting the system |
| 197 | // configuration |
| 198 | bool found = false; |
| 199 | QString time_zone_file_path("/etc/timezone"); |
| 200 | QString clock_file_path("/etc/sysconfig/clock"); |
| 201 | QString zoneinfo_file_path("/etc/localtime"); |
| 202 | QString zoneinfo_dir_path("/usr/share/zoneinfo"); |
| 203 | |
| 204 | // First, check time_zone_file_path (used by Debian-based systems) |
| 205 | QFile time_zone_file(time_zone_file_path); |
| 206 | QFileInfo info(time_zone_file); |
| 207 | if (info.exists() && info.isFile() && info.isReadable()) |
| 208 | { |
| 209 | if (time_zone_file.open(QIODevice::ReadOnly | QIODevice::Text)) |
| 210 | { |
| 211 | QString line; |
| 212 | QTextStream in(&time_zone_file); |
| 213 | while (!in.atEnd()) |
| 214 | { |
| 215 | line = in.readLine(); |
| 216 | line = line.trimmed(); |
| 217 | if (!line.startsWith("#")) |
| 218 | { |
| 219 | zone_id = line; |
| 220 | found = true; |
| 221 | break; |
| 222 | } |
| 223 | } |
| 224 | time_zone_file.close(); |
| 225 | if (found) |
| 226 | return zone_id; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | // Next, look for the ZONE entry in clock_file_path (used by Red Hat-based |
| 231 | // systems) |
| 232 | QFile clock_file(clock_file_path); |
| 233 | info.setFile(clock_file); |
| 234 | if (info.exists() && info.isFile() && info.isReadable()) |
| 235 | { |
| 236 | if (clock_file.open(QIODevice::ReadOnly | QIODevice::Text)) |
| 237 | { |
| 238 | QString line; |
| 239 | QTextStream in(&clock_file); |
| 240 | // Handle whitespace and quotes |
| 241 | QRegExp re("^ZONE\\s*=\\s*(?:'|\")?(.+)$"); |
| 242 | re.setPatternSyntax(QRegExp::RegExp2); |
| 243 | while (!in.atEnd()) |
| 244 | { |
| 245 | line = in.readLine(); |
| 246 | if (re.indexIn(line) != -1) |
| 247 | { |
| 248 | zone_id = re.cap(1); |
| 249 | found = true; |
| 250 | break; |
| 251 | } |
| 252 | } |
| 253 | clock_file.close(); |
| 254 | if (found) |
| 255 | return zone_id; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | // Next check zoneinfo_file_path |
| 260 | QFile zoneinfo_file(zoneinfo_file_path); |
| 261 | info.setFile(zoneinfo_file); |
| 262 | |
| 263 | if (info.exists() && info.isFile()) |
| 264 | { |
| 265 | QString tz; |
| 266 | if (info.isSymLink()) |
| 267 | { |
| 268 | // The symlink refers to a file whose name contains the zone ID |
| 269 | tz = info.symLinkTarget(); |
| 270 | } |
| 271 | else |
| 272 | { |
| 273 | // The zoneinfo_file is a copy of the file in the |
| 274 | // zoneinfo_dir_path, so search for the same file in |
| 275 | // zoneinfo_dir_path |
| 276 | tz = findZoneinfoFile(zoneinfo_file_path, zoneinfo_dir_path); |
| 277 | } |
| 278 | if (tz != "UNDEF") |
| 279 | { |
| 280 | int pos = 0; |
| 281 | // Get the zone ID from the filename |
| 282 | // Look for the basename of zoneinfo_dir_path in case it's a |
| 283 | // relative link |
| 284 | QString zoneinfo_dirname = zoneinfo_dir_path.section('/', -1); |
| 285 | if ((pos = tz.indexOf(zoneinfo_dirname)) != -1) |
| 286 | { |
| 287 | zone_id = tz.right(tz.size() - (pos + 1) - |
| 288 | zoneinfo_dirname.size()); |
| 289 | } |
| 290 | } |
| 291 | else |
| 292 | { |
| 293 | // If we still haven't found a time zone, try localtime_r() to at |
| 294 | // least get the zone name/abbreviation (as opposed to the |
| 295 | // identifier for the set of rules governing the zone) |
| 296 | char name[64]; |
| 297 | time_t t; |
| 298 | struct tm *result = (struct tm *)malloc(sizeof(*result)); |
| 299 | |
| 300 | t = time(NULL); |
| 301 | localtime_r(&t, result); |
| 302 | |
| 303 | if (result != NULL) |
| 304 | { |
| 305 | if (strftime(name, sizeof(name), "%Z", result) > 0) |
| 306 | zone_id = name; |
| 307 | free(result); |
| 308 | } |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | #endif |
| 313 | return zone_id; |
| 314 | } |
| 315 | |
| 316 | /** \fn getTimeZoneID() |
| 317 | * \brief Returns the zoneinfo time zone ID or as much time zone information |
| 318 | * as possible |
| 319 | */ |
| 320 | QString getTimeZoneID(void) |
| 321 | { |
| 322 | QString zone_id("UNDEF"); |
| 323 | #ifndef USING_MINGW |
| 324 | // First, try the TZ environment variable to check for environment-specific |
| 325 | // overrides |
| 326 | QString tz = getenv("TZ"); |
| 327 | if (tz.isEmpty()) |
| 328 | { |
| 329 | // No TZ, so attempt to determine the system-configured time zone ID |
| 330 | tz = getSystemTimeZoneID(); |
| 331 | } |
| 332 | |
| 333 | if (!tz.isEmpty()) |
| 334 | { |
| 335 | zone_id = tz; |
| 336 | if (zone_id.startsWith("\"") || zone_id.startsWith("'")) |
| 337 | zone_id.remove(0, 1); |
| 338 | if (zone_id.endsWith("\"") || zone_id.endsWith("'")) |
| 339 | zone_id.chop(1); |
| 340 | if (zone_id.startsWith(":")) |
| 341 | zone_id.remove(0, 1); |
| 342 | // the "posix/" subdirectory typically contains the same files as the |
| 343 | // "zoneinfo/" parent directory, but are not typically what are in use |
| 344 | if (zone_id.startsWith("posix/")) |
| 345 | zone_id.remove(0, 6); |
| 346 | } |
| 347 | |
| 348 | #endif |
| 349 | return zone_id; |
| 350 | } |
| 351 | |