strftime で %:z と %::z という指定を可能にするのはどうでしょうか。

  % ./ruby -e 'p Time.now.strftime("%z %:z %::z")'
  "+0900 +09:00 +09:00:00"

時差をフォーマットするのに現在は %z がありますが、これは +hhmm という形です。
これは RFC 822 形式の時刻を表現するにはちょうどいいのですが、
XML Schema や RFC 3339 や W3CDTF で使う、コロンが入った +hh:mm という
形式とは合いません。

そして、最近知ったのですが、GNU coreutils の date には %:z という指定で
+hh:mm という形式を生成する機能があります。
また、コロンをふたつにして %::z とすると +hh:mm:ss と秒まで生成します。

というわけで、Ruby でも %:z と %::z をサポートするのはどうでしょう。

なお、コロンが入った形式はその文字列自体を見て時刻の類であることがわかるので
間違いが起きにくく、この型式はそれ自体良いものだと思います。

% svn diff --diff-cmd diff -x '-u -p'
Index: time.c
===================================================================
--- time.c	(revision 28563)
+++ time.c	(working copy)
@@ -4346,7 +4346,9 @@ strftimev(const char *fmt, VALUE time)
  *    %X - Preferred representation for the time alone, no date
  *    %y - Year without a century (00..99)
  *    %Y - Year with century
- *    %z - Time zone as  hour offset from UTC (e.g. +0900)
+ *    %z - Time zone as hour and minute offset from UTC (e.g. +0900)
+ *            %:z - hour and minute offset from UTC with a colon (e.g. +09:00)
+ *            %::z - hour, minute and second offset from UTC (e.g. +09:00:00)
  *    %Z - Time zone name
  *    %% - Literal ``%'' character
  *
Index: strftime.c
===================================================================
--- strftime.c	(revision 28563)
+++ strftime.c	(working copy)
@@ -215,7 +215,7 @@ rb_strftime_with_timespec(char *s, size_
 #endif
 #endif /* HAVE_TM_NAME */
 #endif /* HAVE_TM_ZONE */
-	int precision, flags;
+	int precision, flags, colons;
 	char padding;
 	enum {LEFT, CHCASE, LOWER, UPPER, LOCALE_O, LOCALE_E};
 #define BIT_OF(n) (1U<<(n))
@@ -348,6 +348,7 @@ rb_strftime_with_timespec(char *s, size_
 		precision = -1;
 		flags = 0;
 		padding = 0;
+                colons = 0;
 	again:
 		switch (*++format) {
 		case '\0':
@@ -530,13 +531,31 @@ rb_strftime_with_timespec(char *s, size_
 		 * us that muck around with various message processors.
 		 */
 		case 'z':	/* time zone offset east of GMT e.g. -0600 */
-			if (precision < 4) precision = 4;
-			NEEDS(precision + 1);
+                        switch (colons) {
+                          case 0: /* %z -> +hhmm */
+                            precision = precision <= 5 ? 2 : precision-3;
+                            NEEDS(precision + 3);
+                            break;
+
+                          case 1: /* %:z -> +hh:mm */
+                            precision = precision <= 5 ? 2 : precision-3;
+                            NEEDS(precision + 4);
+                            break;
+
+                          case 2: /* %::z -> +hh:mm:ss */
+                            precision = precision <= 5 ? 2 : precision-3;
+                            NEEDS(precision + 7);
+                            break;
+
+                          default:
+                            format--;
+                            goto unknown;
+                        }
 			if (gmt) {
 				off = 0;
 			}
 			else {
-				off = NUM2LONG(rb_funcall(quo(vtm->utc_offset, INT2FIX(60)),
rb_intern("round"), 0));
+				off = NUM2LONG(rb_funcall(vtm->utc_offset, rb_intern("round"), 0));
 #if 0
 #ifdef HAVE_TM_NAME
 				/*
@@ -583,11 +602,22 @@ rb_strftime_with_timespec(char *s, size_
 			} else {
 				*s++ = '+';
 			}
-			off = off/60*100 + off%60;
-			i = snprintf(s, endp - s, (padding == ' ' ? "%*ld" : "%.*ld"),
-				     precision - (precision > 4), off);
+			i = snprintf(s, endp - s, (padding == ' ' ? "%*ld" : "%.*ld"),
precision, off / 3600);
+			if (i < 0) goto err;
+			s += i;
+                        off = off % 3600;
+                        if (1 <= colons)
+                            *s++ = ':';
+			i = snprintf(s, endp - s, "%02d", off / 60);
 			if (i < 0) goto err;
 			s += i;
+                        off = off % 60;
+                        if (2 <= colons) {
+                            *s++ = ':';
+                            i = snprintf(s, endp - s, "%02d", off);
+                            if (i < 0) goto err;
+                            s += i;
+                        }
 			continue;
 #endif /* MAILHEADER_EXT */

@@ -839,6 +869,11 @@ rb_strftime_with_timespec(char *s, size_
 			padding = ' ';
 			goto again;

+		case ':':
+			FLAG_FOUND();
+                        colons++;
+			goto again;
+
 		case '0':
 			padding = '0';
 		case '1':  case '2': case '3': case '4':
-- 
[田中 哲][たなか あきら][Tanaka Akira]