changeset 13333:b04212d6b115

[gaim-migrate @ 15703] win32 TZ name to abbreviation conversion. This make the behavior consistent across platforms. committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Mon, 27 Feb 2006 08:49:14 +0000
parents 97545c71d208
children 99d081c638f6
files src/log.c src/util.c src/util.h src/win32/libc_interface.c src/win32/libc_interface.h
diffstat 5 files changed, 549 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/log.c	Mon Feb 27 08:35:13 2006 +0000
+++ b/src/log.c	Mon Feb 27 08:49:14 2006 +0000
@@ -643,11 +643,7 @@
 		gaim_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
 
 		tm = localtime(&log->time);
-#ifdef _WIN32
-		tz = "";
-#else
 		tz = gaim_escape_filename(gaim_utf8_strftime("%Z", tm));
-#endif
 		date = gaim_utf8_strftime("%Y-%m-%d.%H%M%S%z", tm);
 
 		filename = g_strdup_printf("%s%s%s", date, tz, ext ? ext : "");
--- a/src/util.c	Mon Feb 27 08:35:13 2006 +0000
+++ b/src/util.c	Mon Feb 27 08:49:14 2006 +0000
@@ -529,7 +529,10 @@
 
 	return buf;
 }
-
+#endif
+
+/* Windows doesn't HAVE_STRFTIME_Z_FORMAT, but this seems clearer. -- rlaager */
+#if !defined(HAVE_STRFTIME_Z_FORMAT) || defined(_WIN32)
 static size_t gaim_internal_strftime(char *s, size_t max, const char *format, const struct tm *tm)
 {
 	const char *start;
@@ -541,9 +544,10 @@
 	g_return_val_if_fail(format != NULL, 0);
 
 	/* This is fairly efficient, and it only gets
-	 * executed if the underlying system doesn't
-	 * support the %z format string for strftime(),
-	 * so I think it's good enough. -- rlaager */
+	 * executed on Windows or if the underlying
+	 * system doesn't support the %z format string,
+	 * for strftime() so I think it's good enough.
+	 * -- rlaager */
 	for (c = start = format; *c ; c++)
 	{
 		if (*c != '%')
@@ -551,6 +555,7 @@
 
 		c++;
 
+#ifndef HAVE_STRFTIME_Z_FORMAT
 		if (*c == 'z')
 		{
 			char *tmp = g_strdup_printf("%s%.*s%s",
@@ -562,6 +567,20 @@
 			fmt = tmp;
 			start = c + 1;
 		}
+#endif
+#ifdef _WIN32
+		if (*c == 'Z')
+		{
+			char *tmp = g_strdup_printf("%s%.*s%s",
+			                            fmt ? fmt : "",
+			                            c - start - 1,
+			                            start,
+			                            wgaim_get_timezone_abbreviation(tm));
+			g_free(fmt);
+			fmt = tmp;
+			start = c + 1;
+		}
+#endif
 	}
 
 	if (fmt != NULL)
@@ -583,7 +602,7 @@
 
 	return strftime(s, max, format, tm);
 }
-#else /* HAVE_STRFTIME_Z_FORMAT */
+#else /* HAVE_STRFTIME_Z_FORMAT && !_WIN32 */
 #define gaim_internal_strftime strftime
 #endif
 
--- a/src/util.h	Mon Feb 27 08:35:13 2006 +0000
+++ b/src/util.h	Mon Feb 27 08:49:14 2006 +0000
@@ -223,6 +223,11 @@
  * GMT.  Required to emit RFC822-conformant dates
  * (using "%a, %d %b %Y %H:%M:%S %z"). (GNU)'
  *
+ * On Windows, this function also converts the results for %Z from a timezone
+ * name (as returned by the system strftime() %Z format string) to a timezone
+ * abbreviation (as is the case on Unix).  As with %z, conversion specifiers
+ * should not be used.
+ *
  * @param format The format string, in UTF-8
  * @param tm     The time to format, or @c NULL to use the current local time
  *
--- a/src/win32/libc_interface.c	Mon Feb 27 08:35:13 2006 +0000
+++ b/src/win32/libc_interface.c	Mon Feb 27 08:49:14 2006 +0000
@@ -1,9 +1,5 @@
 /*
  * gaim
- *
- * File: libc_interface.c
- * Date: October 14, 2002
- * Description: libc interface for Windows api
  * 
  * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
  *
@@ -41,20 +37,9 @@
 #define g_rename rename
 #define g_stat stat
 #endif
-/*
- *  PROTOS
- */
-
-/*
- *  LOCALS
- */
 
 static char errbuf[1024];
 
-/*
- *  CODE
- */
-
 /* helpers */
 static int wgaim_is_socket( int fd ) {
 	int optval;
@@ -436,3 +421,520 @@
 	else
 		return NULL;
 }
+
+/*
+ * Used by gaim_utf8_strftime() by way of gaim_internal_strftime()
+ * in src/util.c
+ *
+ * Code derived from PostgreSQL src/timezone/pgtz.c:
+ * http://developer.postgresql.org/cvsweb.cgi/pgsql/src/timezone/pgtz.c
+ */
+
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+static const struct
+{
+	const char *wstd;		/* Windows name of standard timezone */
+	const char *wdst;		/* Windows name of daylight timezone */
+	const char *ustd;		/* Unix name of standard timezone */
+	const char *udst;		/* Unix name of daylight timezone */
+} win32_tzmap[] =
+{
+	/*
+	 * This list was built from the contents of the registry at
+	 * "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
+	 * on Windows XP Professional SP1
+	 */
+	{
+		"Afghanistan Standard Time", "Afghanistan Daylight Time",
+		"AFT", "AFT"
+	},
+	{
+		"Alaskan Standard Time", "Alaskan Daylight Time",
+		"AKST", "AKDT"
+	},
+	{
+		"Arab Standard Time", "Arab Daylight Time",
+		"AST", "AST"
+	},
+	{
+		"Arabian Standard Time", "Arabian Daylight Time",
+		"GST", "GST"
+	},
+	{
+		"Arabic Standard Time", "Arabic Daylight Time",
+		"AST", "ADT"
+	},
+	{
+		"Atlantic Standard Time", "Atlantic Daylight Time",
+		"AST", "ADT"
+	},
+	{
+		"AUS Central Standard Time", "AUS Central Daylight Time",
+		"CST", "CST"
+	},
+	{
+		"AUS Eastern Standard Time", "AUS Eastern Daylight Time",
+		"EST", "EST"
+	},
+	{
+		"Azores Standard Time", "Azores Daylight Time",
+		"AZOT", "AZOST"
+	},
+	{
+		"Canada Central Standard Time", "Canada Central Daylight Time",
+		"CST", "MDT"
+	},
+	{
+		"Cape Verde Standard Time", "Cape Verde Daylight Time",
+		"CVT", "CVST"
+	},
+	{
+		"Caucasus Standard Time", "Caucasus Daylight Time",
+		"AZT", "AZST"
+	},
+	{
+		"Cen. Australia Standard Time", "Cen. Australia Daylight Time",
+		"CST", "CST"
+	},
+	{
+		"Central America Standard Time", "Central America Daylight Time",
+		"CST", "CDT"
+	},
+	{
+		"Central Asia Standard Time", "Central Asia Daylight Time",
+		"BDT", "BDT"
+	},
+	{
+		"Central Europe Standard Time", "Central Europe Daylight Time",
+		"CET", "CEST"
+	},
+	{
+		"Central European Standard Time", "Central European Daylight Time",
+		"CET", "CEST"
+	},
+	{
+		"Central Pacific Standard Time", "Central Pacific Daylight Time",
+		"NCT", "NCST"
+	},
+	{
+		"Central Standard Time", "Central Daylight Time",
+		"CST", "CDT"
+	},
+	{
+		"China Standard Time", "China Daylight Time",
+		"HKT", "HKST"
+	},
+	{
+		"Dateline Standard Time", "Dateline Daylight Time",
+		"GMT+12", "GMT+12"
+	},
+	{
+		"E. Africa Standard Time", "E. Africa Daylight Time",
+		"EAT", "EAT"
+	},
+	{
+		"E. Australia Standard Time", "E. Australia Daylight Time",
+		"EST", "EST"
+	},
+	{
+		"E. Europe Standard Time", "E. Europe Daylight Time",
+		"EET", "EEST"
+	},
+	{
+		"E. South America Standard Time", "E. South America Daylight Time",
+		"BRT", "BRST"
+	},
+	{
+		"Eastern Standard Time", "Eastern Daylight Time",
+		"EST", "EDT"
+	},
+	{
+		"Egypt Standard Time", "Egypt Daylight Time",
+		"EET", "EEST"
+	},
+	{
+		"Ekaterinburg Standard Time", "Ekaterinburg Daylight Time",
+		"YEKT", "YEKST"
+	},
+	{
+		"Fiji Standard Time", "Fiji Daylight Time",
+		"FJT", "FJST"
+	},
+	{
+		"FLE Standard Time", "FLE Daylight Time",
+		"EET", "EEST"
+	},
+	{
+		"GMT Standard Time", "GMT Daylight Time",
+		"GMT", "IST"
+	},
+	{
+		"Greenland Standard Time", "Greenland Daylight Time",
+		"WGT", "WGST"
+	},
+	{
+		"Greenwich Standard Time", "Greenwich Daylight Time",
+		"WET", "WEST"
+	},
+	{
+		"GTB Standard Time", "GTB Daylight Time",
+		"EET", "EEST"
+	},
+	{
+		"Hawaiian Standard Time", "Hawaiian Daylight Time",
+		"HST", "HPT"
+	},
+	{
+		"India Standard Time", "India Daylight Time",
+		"IST", "IST"
+	},
+	{
+		"Iran Standard Time", "Iran Daylight Time",
+		"IRST", "IRDT"
+	},
+	{
+		"Jerusalem Standard Time", "Jerusalem Daylight Time",
+		"IST", "IDT"
+	},
+	{
+		"Korea Standard Time", "Korea Daylight Time",
+		"KST", "KDT"
+	},
+	{
+		"Mexico Standard Time", "Mexico Daylight Time",
+		"CST", "CDT"
+	},
+	{
+		"Mexico Standard Time", "Mexico Daylight Time",
+		"BOT", "BOST"
+	},
+	{
+		"Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
+		"GST", "GST"
+	},
+	{
+		"Mountain Standard Time", "Mountain Daylight Time",
+		"MST", "MDT"
+	},
+	{
+		"Myanmar Standard Time", "Myanmar Daylight Time",
+		"MMT", "MMT"
+	},
+	{
+		"N. Central Asia Standard Time", "N. Central Asia Daylight Time",
+		"ALMT", "ALMST"
+	},
+	{
+		"Nepal Standard Time", "Nepal Daylight Time",
+		"NPT", "NPT"
+	},
+	{
+		"New Zealand Standard Time", "New Zealand Daylight Time",
+		"NZST", "NZDT"
+	},
+	{
+		"Newfoundland Standard Time", "Newfoundland Daylight Time",
+		"NST", "NDT"
+	},
+	{
+		"North Asia East Standard Time", "North Asia East Daylight Time",
+		"IRKT", "IRKST"
+	},
+	{
+		"North Asia Standard Time", "North Asia Daylight Time",
+		"KRAT", "KRAST"
+	},
+	{
+		"Pacific SA Standard Time", "Pacific SA Daylight Time",
+		"CLT", "CLST"
+	},
+	{
+		"Pacific Standard Time", "Pacific Daylight Time",
+		"PST", "PDT"
+	},
+	{
+		"Romance Standard Time", "Romance Daylight Time",
+		"CET", "CEST"
+	},
+	{
+		"Russian Standard Time", "Russian Daylight Time",
+		"MSK", "MSD"
+	},
+	{
+		"SA Eastern Standard Time", "SA Eastern Daylight Time",
+		"ART", "ARST"
+	},
+	{
+		"SA Pacific Standard Time", "SA Pacific Daylight Time",
+		"COT", "COST"
+	},
+	{
+		"SA Western Standard Time", "SA Western Daylight Time",
+		"VET", "VET"
+	},
+	{
+		"Samoa Standard Time", "Samoa Daylight Time",
+		"SST", "NDT"
+	},
+	{
+		"SE Asia Standard Time", "SE Asia Daylight Time",
+		"ICT", "ICT"
+	},
+	{
+		"Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
+		"MYT", "MALST"
+	},
+	{
+		"South Africa Standard Time", "South Africa Daylight Time",
+		"CAT", "CAT"
+	},
+	{
+		"Sri Lanka Standard Time", "Sri Lanka Daylight Time",
+		"LKT", "IST"
+	},
+	{
+		"Taipei Standard Time", "Taipei Daylight Time",
+		"CST", "CDT"
+	},
+	{
+		"Tasmania Standard Time", "Tasmania Daylight Time",
+		"EST", "EST"
+	},
+	{
+		"Tokyo Standard Time", "Tokyo Daylight Time",
+		"JST", "JDT"
+	},
+	{
+		"Tonga Standard Time", "Tonga Daylight Time",
+		"TOT", "TOST"
+	},
+	{
+		"US Eastern Standard Time", "US Eastern Daylight Time",
+		"EST", "EDT"
+	},
+	{
+		"US Mountain Standard Time", "US Mountain Daylight Time",
+		"MST", "MDT"
+	},
+	{
+		"Vladivostok Standard Time", "Vladivostok Daylight Time",
+		"VLAT", "VLAST"
+	},
+	{
+		"W. Australia Standard Time", "W. Australia Daylight Time",
+		"WST", "WST"
+	},
+
+	/* Not mapped in PostgreSQL.
+	 *
+	 * I mapped this based on the following information... -- rlaager
+	 * $ cd /usr/share/zoneinfo/Africa
+	 * $ for i in * ; do echo `TZ=Africa/$i date +"%z %Z"` $i ; done | grep +0100
+	 * +0100 CET Algiers
+	 * +0100 WAT Bangui
+	 * +0100 WAT Brazzaville
+	 * +0100 CET Ceuta
+	 * +0100 WAT Douala
+	 * +0100 WAT Kinshasa
+	 * +0100 WAT Lagos
+	 * +0100 WAT Libreville
+	 * +0100 WAT Luanda
+	 * +0100 WAT Malabo
+	 * +0100 WAT Ndjamena
+	 * +0100 WAT Niamey
+	 * +0100 WAT Porto-Novo
+	 * +0100 CET Tunis
+	 **/
+	{
+		"W. Central Africa Standard Time", "W. Central Africa Daylight Time",
+		"WAT", "WAT"
+	},
+
+	{
+		"W. Europe Standard Time", "W. Europe Daylight Time",
+		"CET", "CEST"
+	},
+	{
+		"West Asia Standard Time", "West Asia Daylight Time",
+		"PKT", "PKST"
+	},
+	{
+		"West Pacific Standard Time", "West Pacific Daylight Time",
+		"ChST", "ChST"
+	},
+	{
+		"Yakutsk Standard Time", "Yakutsk Daylight Time",
+		"YAKT", "YAKST"
+	},
+	{
+		NULL, NULL,
+		NULL, NULL
+	}
+};
+
+const char *
+wgaim_get_timezone_abbreviation(const struct tm *tm)
+{
+	int			i;
+	char		tzname[128];
+	char		localtzname[256];
+	HKEY		rootKey;
+	int			idx;
+
+	if (!tm)
+	{
+		gaim_debug_warning("wgaim", "could not determine current date/time: localtime failed");
+		return NULL;
+	}
+
+	memset(tzname, 0, sizeof(tzname));
+	strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
+
+	for (i = 0; win32_tzmap[i].wstd != NULL; i++)
+	{
+		if (strcmp(tzname, win32_tzmap[i].wstd) == 0)
+		{
+			gaim_debug_info("wgaim", "TZ \"%s\" matches Windows timezone \"%s\"",
+			                win32_tzmap[i].ustd, tzname);
+			return win32_tzmap[i].ustd;
+		}
+		if (strcmp(tzname, win32_tzmap[i].wdst) == 0)
+		{
+			gaim_debug_info("wgaim", "TZ \"%s\" matches Windows timezone \"%s\"",
+			                win32_tzmap[i].udst, tzname);
+			return win32_tzmap[i].udst;
+		}
+	}
+
+	/*
+	 * Localized Windows versions return localized names for the timezone.
+	 * Scan the registry to find the English name, and then try matching
+	 * against our table again.
+	 */
+	memset(localtzname, 0, sizeof(localtzname));
+	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+			   "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
+					 0,
+					 KEY_READ,
+					 &rootKey) != ERROR_SUCCESS)
+	{
+		gaim_debug_warning("wgaim", "could not open registry key to identify Windows timezone: %i", (int) GetLastError());
+		return NULL;
+	}
+
+	for (idx = 0;; idx++)
+	{
+		char		keyname[256];
+		char		zonename[256];
+		DWORD		namesize;
+		FILETIME	lastwrite;
+		HKEY		key;
+		LONG		r;
+
+		memset(keyname, 0, sizeof(keyname));
+		namesize = sizeof(keyname);
+		if ((r = RegEnumKeyEx(rootKey,
+							  idx,
+							  keyname,
+							  &namesize,
+							  NULL,
+							  NULL,
+							  NULL,
+							  &lastwrite)) != ERROR_SUCCESS)
+		{
+			if (r == ERROR_NO_MORE_ITEMS)
+				break;
+			gaim_debug_warning("wgaim", "could not enumerate registry subkeys to identify Windows timezone: %i", (int) r);
+			break;
+		}
+
+		if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
+		{
+			gaim_debug_warning("wgaim", "could not open registry subkey to identify Windows timezone: %i", (int) r);
+			break;
+		}
+
+		memset(zonename, 0, sizeof(zonename));
+		namesize = sizeof(zonename);
+		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		{
+			gaim_debug_warning("wgaim", "could not query value for 'std' to identify Windows timezone: %i", (int) r);
+			RegCloseKey(key);
+			break;
+		}
+		if (strcmp(tzname, zonename) == 0)
+		{
+			/* Matched zone */
+			strcpy(localtzname, keyname);
+			RegCloseKey(key);
+			break;
+		}
+		memset(zonename, 0, sizeof(zonename));
+		namesize = sizeof(zonename);
+		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		{
+			gaim_debug_warning("wgaim", "could not query value for 'dlt' to identify Windows timezone: %i", (int) r);
+			RegCloseKey(key);
+			break;
+		}
+		if (strcmp(tzname, zonename) == 0)
+		{
+			/* Matched DST zone */
+			strcpy(localtzname, keyname);
+			RegCloseKey(key);
+			break;
+		}
+
+		RegCloseKey(key);
+	}
+
+	RegCloseKey(rootKey);
+
+	if (localtzname[0])
+	{
+		/* Found a localized name, so scan for that one too */
+		for (i = 0; win32_tzmap[i].wstd != NULL; i++)
+		{
+			if (strcmp(localtzname, win32_tzmap[i].wstd) == 0)
+			{
+				gaim_debug_info("wgaim", "TZ \"%s\" matches localized Windows timezone \"%s\" (\"%s\")",
+				                win32_tzmap[i].ustd, tzname, localtzname);
+				return win32_tzmap[i].ustd;
+			}
+			if (strcmp(localtzname, win32_tzmap[i].wdst) == 0)
+			{
+				gaim_debug_info("wgaim", "TZ \"%s\" matches localized Windows timezone \"%s\" (\"%s\")",
+				                win32_tzmap[i].udst, tzname, localtzname);
+				return win32_tzmap[i].udst;
+			}
+		}
+	}
+
+	gaim_debug_warning("wgaim", "could not find a match for Windows timezone \"%s\"", tzname);
+	return "";
+}
--- a/src/win32/libc_interface.h	Mon Feb 27 08:35:13 2006 +0000
+++ b/src/win32/libc_interface.h	Mon Feb 27 08:49:14 2006 +0000
@@ -159,4 +159,7 @@
 #define localtime_r( time, resultp ) \
 wgaim_localtime_r( time, resultp )
 
+/* helper for gaim_utf8_strftime() by way of gaim_internal_strftime() in src/util.c */
+const char *wgaim_get_timezone_abbreviation(const struct tm *tm);
+
 #endif /* _LIBC_INTERFACE_H_ */