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