diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6fbdabd1d2baa5ab439f6dedeaa3c8d7a615ae53..0892cdbe3eb29f58de7fb91d18a32babd16c595d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.174 2008/05/12 20:01:59 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.175 2008/05/14 19:10:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -390,19 +390,7 @@ elapsed_time(instr_time *starttime)
 	instr_time	endtime;
 
 	INSTR_TIME_SET_CURRENT(endtime);
-
-#ifndef WIN32
-	endtime.tv_sec -= starttime->tv_sec;
-	endtime.tv_usec -= starttime->tv_usec;
-	while (endtime.tv_usec < 0)
-	{
-		endtime.tv_usec += 1000000;
-		endtime.tv_sec--;
-	}
-#else							/* WIN32 */
-	endtime.QuadPart -= starttime->QuadPart;
-#endif
-
+	INSTR_TIME_SUBTRACT(endtime, *starttime);
 	return INSTR_TIME_GET_DOUBLE(endtime);
 }
 
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index fa0a7f7b507de15242846418de116d6e168c62d0..b83d6f5d32c021a9ea199a11e556a0021dff6081 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -7,7 +7,7 @@
  * Copyright (c) 2001-2008, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.20 2008/01/01 19:45:49 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.21 2008/05/14 19:10:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,25 +55,7 @@ InstrStopNode(Instrumentation *instr, double nTuples)
 	}
 
 	INSTR_TIME_SET_CURRENT(endtime);
-
-#ifndef WIN32
-	instr->counter.tv_sec += endtime.tv_sec - instr->starttime.tv_sec;
-	instr->counter.tv_usec += endtime.tv_usec - instr->starttime.tv_usec;
-
-	/* Normalize after each add to avoid overflow/underflow of tv_usec */
-	while (instr->counter.tv_usec < 0)
-	{
-		instr->counter.tv_usec += 1000000;
-		instr->counter.tv_sec--;
-	}
-	while (instr->counter.tv_usec >= 1000000)
-	{
-		instr->counter.tv_usec -= 1000000;
-		instr->counter.tv_sec++;
-	}
-#else							/* WIN32 */
-	instr->counter.QuadPart += (endtime.QuadPart - instr->starttime.QuadPart);
-#endif
+	INSTR_TIME_ACCUM_DIFF(instr->counter, endtime, instr->starttime);
 
 	INSTR_TIME_SET_ZERO(instr->starttime);
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index f74ce95140970eaeab11cbcf934bc1ab86b57f75..b86d67e92814189ecc8f9b3ca71f7b9617936749 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.188 2008/05/08 17:04:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.189 2008/05/14 19:10:29 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
@@ -30,6 +30,8 @@
 #include <sys/stat.h>			/* for stat() */
 #endif
 
+#include "portability/instr_time.h"
+
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
 #include "dumputils.h"
@@ -282,24 +284,22 @@ exec_command(const char *cmd,
 	else if (pg_strcasecmp(cmd, "copy") == 0)
 	{
 		/* Default fetch-it-all-and-print mode */
-		TimevalStruct before,
+		instr_time	before,
 					after;
-		double		elapsed_msec = 0;
 
 		char	   *opt = psql_scan_slash_option(scan_state,
 												 OT_WHOLE_LINE, NULL, false);
 
 		if (pset.timing)
-			GETTIMEOFDAY(&before);
+			INSTR_TIME_SET_CURRENT(before);
 
 		success = do_copy(opt);
 
 		if (pset.timing && success)
 		{
-			GETTIMEOFDAY(&after);
-			elapsed_msec = DIFF_MSEC(&after, &before);
-			printf(_("Time: %.3f ms\n"), elapsed_msec);
-
+			INSTR_TIME_SET_CURRENT(after);
+			INSTR_TIME_SUBTRACT(after, before);
+			printf(_("Time: %.3f ms\n"), INSTR_TIME_GET_MILLISEC(after));
 		}
 
 		free(opt);
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 72aad262f5af02c974b83d9e1073e61c423e5697..ab1f03f310980dc614dcd1a66f27772e5796e6ff 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.138 2008/01/01 19:45:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.139 2008/05/14 19:10:29 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
@@ -17,6 +17,8 @@
 #include <win32.h>
 #endif
 
+#include "portability/instr_time.h"
+
 #include "pqsignal.h"
 
 #include "settings.h"
@@ -844,11 +846,11 @@ SendQuery(const char *query)
 	if (pset.fetch_count <= 0 || !is_select_command(query))
 	{
 		/* Default fetch-it-all-and-print mode */
-		TimevalStruct before,
+		instr_time	before,
 					after;
 
 		if (pset.timing)
-			GETTIMEOFDAY(&before);
+			INSTR_TIME_SET_CURRENT(before);
 
 		results = PQexec(pset.db, query);
 
@@ -858,8 +860,9 @@ SendQuery(const char *query)
 
 		if (pset.timing)
 		{
-			GETTIMEOFDAY(&after);
-			elapsed_msec = DIFF_MSEC(&after, &before);
+			INSTR_TIME_SET_CURRENT(after);
+			INSTR_TIME_SUBTRACT(after, before);
+			elapsed_msec = INSTR_TIME_GET_MILLISEC(after);
 		}
 
 		/* but printing results isn't: */
@@ -961,7 +964,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 	bool		did_pager = false;
 	int			ntuples;
 	char		fetch_cmd[64];
-	TimevalStruct before,
+	instr_time	before,
 				after;
 
 	*elapsed_msec = 0;
@@ -972,7 +975,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 	my_popt.topt.prior_records = 0;
 
 	if (pset.timing)
-		GETTIMEOFDAY(&before);
+		INSTR_TIME_SET_CURRENT(before);
 
 	/* if we're not in a transaction, start one */
 	if (PQtransactionStatus(pset.db) == PQTRANS_IDLE)
@@ -1001,8 +1004,9 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 
 	if (pset.timing)
 	{
-		GETTIMEOFDAY(&after);
-		*elapsed_msec += DIFF_MSEC(&after, &before);
+		INSTR_TIME_SET_CURRENT(after);
+		INSTR_TIME_SUBTRACT(after, before);
+		*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
 	}
 
 	snprintf(fetch_cmd, sizeof(fetch_cmd),
@@ -1028,15 +1032,16 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 	for (;;)
 	{
 		if (pset.timing)
-			GETTIMEOFDAY(&before);
+			INSTR_TIME_SET_CURRENT(before);
 
 		/* get FETCH_COUNT tuples at a time */
 		results = PQexec(pset.db, fetch_cmd);
 
 		if (pset.timing)
 		{
-			GETTIMEOFDAY(&after);
-			*elapsed_msec += DIFF_MSEC(&after, &before);
+			INSTR_TIME_SET_CURRENT(after);
+			INSTR_TIME_SUBTRACT(after, before);
+			*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
 		}
 
 		if (PQresultStatus(results) != PGRES_TUPLES_OK)
@@ -1112,7 +1117,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
 
 cleanup:
 	if (pset.timing)
-		GETTIMEOFDAY(&before);
+		INSTR_TIME_SET_CURRENT(before);
 
 	/*
 	 * We try to close the cursor on either success or failure, but on failure
@@ -1137,8 +1142,9 @@ cleanup:
 
 	if (pset.timing)
 	{
-		GETTIMEOFDAY(&after);
-		*elapsed_msec += DIFF_MSEC(&after, &before);
+		INSTR_TIME_SET_CURRENT(after);
+		INSTR_TIME_SUBTRACT(after, before);
+		*elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
 	}
 
 	return OK;
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index c8cefe1843f2a29418e315a9775042df39ba0705..7977eb5493c0c39f8ce68b0287d417a299fb2f42 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.56 2008/01/01 19:45:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.57 2008/05/14 19:10:29 tgl Exp $
  */
 #ifndef COMMON_H
 #define COMMON_H
@@ -63,36 +63,4 @@ extern const char *session_username(void);
 
 extern char *expand_tilde(char **filename);
 
-#ifndef WIN32
-
-#include <sys/time.h>
-
-typedef struct timeval TimevalStruct;
-
-#define GETTIMEOFDAY(T) gettimeofday(T, NULL)
-#define DIFF_MSEC(T, U) \
-	((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \
-	  ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)
-#else
-/*
- * To get good resolution (better than ~15ms) on Windows, use
- * the high resolution performance counters. They can't be used
- * to get absolute times, but are good for measuring differences.
- */
-static __inline__ double
-GetTimerFrequency(void)
-{
-	LARGE_INTEGER f;
-
-	QueryPerformanceFrequency(&f);
-	return (double) f.QuadPart;
-}
-
-typedef LARGE_INTEGER TimevalStruct;
-
-#define GETTIMEOFDAY(T) QueryPerformanceCounter((T))
-#define DIFF_MSEC(T, U) \
-	(((T)->QuadPart - (U)->QuadPart) * 1000.0 / GetTimerFrequency())
-#endif   /* WIN32 */
-
 #endif   /* COMMON_H */
diff --git a/src/include/Makefile b/src/include/Makefile
index 01a046f4fccc30d1a10b3e1aa123424ce9bbbd43..a37414b69914de71142aa142193674a2ff82cae5 100644
--- a/src/include/Makefile
+++ b/src/include/Makefile
@@ -4,7 +4,7 @@
 #
 # 'make install' installs whole contents of src/include.
 #
-# $PostgreSQL: pgsql/src/include/Makefile,v 1.24 2008/03/17 19:44:41 petere Exp $
+# $PostgreSQL: pgsql/src/include/Makefile,v 1.25 2008/05/14 19:10:29 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -21,7 +21,8 @@ SUBDIRS = access bootstrap catalog commands executor lib libpq mb \
 	nodes optimizer parser postmaster regex rewrite storage tcop \
 	snowball snowball/libstemmer tsearch tsearch/dicts utils \
 	port port/win32 port/win32_msvc port/win32_msvc/sys \
-	port/win32/arpa port/win32/netinet port/win32/sys
+	port/win32/arpa port/win32/netinet port/win32/sys \
+	portability
 
 # Install all headers
 install: all installdirs
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index 6889e9c0eb4b771cd370a68fbf3df72317af5d4b..080e264629bb126c55edaf81f44f55667778126a 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -6,51 +6,14 @@
  *
  * Copyright (c) 2001-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.18 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.19 2008/05/14 19:10:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef INSTRUMENT_H
 #define INSTRUMENT_H
 
-#include <sys/time.h>
-
-
-/*
- * gettimeofday() does not have sufficient resolution on Windows,
- * so we must use QueryPerformanceCounter() instead.  These macros
- * also give some breathing room to use other high-precision-timing APIs
- * on yet other platforms.	(The macro-ization is not complete, however;
- * see subtraction code in instrument.c and explain.c.)
- */
-#ifndef WIN32
-
-typedef struct timeval instr_time;
-
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_sec == 0 && (t).tv_usec == 0)
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_usec = 0)
-#define INSTR_TIME_SET_CURRENT(t)	gettimeofday(&(t), NULL)
-#define INSTR_TIME_GET_DOUBLE(t) \
-	(((double) (t).tv_sec) + ((double) (t).tv_usec) / 1000000.0)
-#else							/* WIN32 */
-
-typedef LARGE_INTEGER instr_time;
-
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
-#define INSTR_TIME_SET_ZERO(t)	((t).QuadPart = 0)
-#define INSTR_TIME_SET_CURRENT(t)	QueryPerformanceCounter(&(t))
-#define INSTR_TIME_GET_DOUBLE(t) \
-	(((double) (t).QuadPart) / GetTimerFrequency())
-
-static __inline__ double
-GetTimerFrequency(void)
-{
-	LARGE_INTEGER f;
-
-	QueryPerformanceFrequency(&f);
-	return (double) f.QuadPart;
-}
-#endif   /* WIN32 */
+#include "portability/instr_time.h"
 
 
 typedef struct Instrumentation
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
new file mode 100644
index 0000000000000000000000000000000000000000..666d495f2036cbb04a0337eb6416ae2f2cb419d7
--- /dev/null
+++ b/src/include/portability/instr_time.h
@@ -0,0 +1,139 @@
+/*-------------------------------------------------------------------------
+ *
+ * instr_time.h
+ *	  portable high-precision interval timing
+ *
+ * This file provides an abstraction layer to hide portability issues in
+ * interval timing.  On Unix we use gettimeofday(), but on Windows that
+ * gives a low-precision result so we must use QueryPerformanceCounter()
+ * instead.  These macros also give some breathing room to use other
+ * high-precision-timing APIs on yet other platforms.
+ *
+ * The basic data type is instr_time, which all callers should treat as an
+ * opaque typedef.  instr_time can store either an absolute time (of
+ * unspecified reference time) or an interval.  The operations provided
+ * for it are:
+ *
+ * INSTR_TIME_IS_ZERO(t)			is t equal to zero?
+ *
+ * INSTR_TIME_SET_ZERO(t)			set t to zero (memset is acceptable too)
+ *
+ * INSTR_TIME_SET_CURRENT(t)		set t to current time
+ *
+ * INSTR_TIME_SUBTRACT(x, y)		x -= y
+ *
+ * INSTR_TIME_ACCUM_DIFF(x, y, z)	x += (y - z)
+ *
+ * INSTR_TIME_GET_DOUBLE(t)			convert t to double (in seconds)
+ *
+ * INSTR_TIME_GET_MILLISEC(t)		convert t to double (in milliseconds)
+ *
+ * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
+ *
+ * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
+ * absolute times to intervals.  The INSTR_TIME_GET_xxx operations are
+ * only useful on intervals.
+ *
+ * When summing multiple measurements, it's recommended to leave the
+ * running sum in instr_time form (ie, use INSTR_TIME_ACCUM_DIFF) and
+ * convert to a result format only at the end.
+ *
+ * Beware of multiple evaluations of the macro arguments.
+ *
+ *
+ * Copyright (c) 2001-2008, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL: pgsql/src/include/portability/instr_time.h,v 1.1 2008/05/14 19:10:29 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INSTR_TIME_H
+#define INSTR_TIME_H
+
+#ifndef WIN32
+
+#include <sys/time.h>
+
+typedef struct timeval instr_time;
+
+#define INSTR_TIME_IS_ZERO(t)	((t).tv_usec == 0 && (t).tv_sec == 0)
+
+#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_usec = 0)
+
+#define INSTR_TIME_SET_CURRENT(t)	gettimeofday(&(t), NULL)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	do { \
+		(x).tv_sec -= (y).tv_sec; \
+		(x).tv_usec -= (y).tv_usec; \
+		/* Normalize */ \
+		while ((x).tv_usec < 0) \
+		{ \
+			(x).tv_usec += 1000000; \
+			(x).tv_sec--; \
+		} \
+	} while (0)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	do { \
+		(x).tv_sec += (y).tv_sec - (z).tv_sec; \
+		(x).tv_usec += (y).tv_usec - (z).tv_usec; \
+		/* Normalize after each add to avoid overflow/underflow of tv_usec */ \
+		while ((x).tv_usec < 0) \
+		{ \
+			(x).tv_usec += 1000000; \
+			(x).tv_sec--; \
+		} \
+		while ((x).tv_usec >= 1000000) \
+		{ \
+			(x).tv_usec -= 1000000; \
+			(x).tv_sec++; \
+		} \
+	} while (0)
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	(((double) (t).tv_sec) + ((double) (t).tv_usec) / 1000000.0)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	(((double) (t).tv_sec * 1000.0) + ((double) (t).tv_usec) / 1000.0)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) (t).tv_usec)
+
+#else	/* WIN32 */
+
+typedef LARGE_INTEGER instr_time;
+
+#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+
+#define INSTR_TIME_SET_ZERO(t)	((t).QuadPart = 0)
+
+#define INSTR_TIME_SET_CURRENT(t)	QueryPerformanceCounter(&(t))
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).QuadPart -= (y).QuadPart)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).QuadPart += (y).QuadPart - (z).QuadPart)
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	(((double) (t).QuadPart) / GetTimerFrequency())
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	(((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+
+static __inline__ double
+GetTimerFrequency(void)
+{
+	LARGE_INTEGER f;
+
+	QueryPerformanceFrequency(&f);
+	return (double) f.QuadPart;
+}
+
+#endif   /* WIN32 */
+
+#endif   /* INSTR_TIME_H */