[前][次][番号順一覧][スレッド一覧]

ruby-changes:7254

From: nobu <ko1@a...>
Date: Fri, 22 Aug 2008 14:28:22 +0900 (JST)
Subject: [ruby-changes:7254] Ruby:r18773 (trunk): * strftime.c (rb_strftime): supported flags and precision for most

nobu	2008-08-22 14:28:08 +0900 (Fri, 22 Aug 2008)

  New Revision: 18773

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=18773

  Log:
    * strftime.c (rb_strftime): supported flags and precision for most
      conversions.  [ruby-dev:35906]
    
    * lib/date/format.rb (Date#strftime): left-justifies %L and %N.
      [ruby-dev:35909]

  Modified files:
    trunk/ChangeLog
    trunk/lib/date/format.rb
    trunk/strftime.c
    trunk/test/ruby/test_time.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 18772)
+++ ChangeLog	(revision 18773)
@@ -1,3 +1,11 @@
+Fri Aug 22 14:28:05 2008  Nobuyoshi Nakada  <nobu@r...>
+
+	* strftime.c (rb_strftime): supported flags and precision for most
+	  conversions.  [ruby-dev:35906]
+
+	* lib/date/format.rb (Date#strftime): left-justifies %L and %N.
+	  [ruby-dev:35909]
+
 Fri Aug 22 14:04:04 2008  Martin Duerst  <duerst@i...>
 
 	* test/ruby/test_transcode.rb: test_shift_jis:
Index: lib/date/format.rb
===================================================================
--- lib/date/format.rb	(revision 18772)
+++ lib/date/format.rb	(revision 18773)
@@ -153,8 +153,8 @@
       s[0,0] = sign
     end
 
-    if f[:p] != '-'
-      s = s.rjust(f[:w], f[:p])
+    if f[:w]
+      s = f[:n] ? s.ljust(f[:w], f[:p]) : s.rjust(f[:w], f[:p])
     end
 
     if f[:s] && f[:p] != "\s"
@@ -257,12 +257,18 @@
       when 'j'; emit_n(yday, 3, f)
       when 'k'; emit_a(hour, 2, f)
       when 'L'
-	emit_n((sec_fraction / MILLISECONDS_IN_SECOND).floor, 3, f)
+        f[:n] = true
+        w = f[:w]
+        s = emit_n((sec_fraction / MILLISECONDS_IN_SECOND).floor, 3, f)
+        w ? s[0, w] : s
       when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f)
       when 'M', 'OM'; emit_n(min, 2, f)
       when 'm', 'Om'; emit_n(mon, 2, f)
       when 'N'
-	emit_n((sec_fraction / NANOSECONDS_IN_SECOND).floor, 9, f)
+        f[:n] = true
+        w = f[:w]
+        s = emit_n((sec_fraction / NANOSECONDS_IN_SECOND).floor, 9, f)
+        w ? s[0, w] : s
       when 'n'; "\n"
       when 'P'; emit_ad(strftime('%p').downcase, 0, f)
       when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f)
Index: strftime.c
===================================================================
--- strftime.c	(revision 18772)
+++ strftime.c	(revision 18773)
@@ -1,3 +1,5 @@
+/* -*- c-file-style: "linux" -*- */
+
 /*
  * strftime.c
  *
@@ -176,6 +178,7 @@
 {
 	char *endp = s + maxsize;
 	char *start = s;
+	const char *sp, *tp;
 	auto char tbuf[100];
 	long off;
 	int i, w;
@@ -194,33 +197,36 @@
 #endif /* HAVE_TIMEZONE */
 #endif /* HAVE_TM_NAME */
 #endif /* HAVE_TM_ZONE */
-	int precision = -1;
+	int precision, flags;
+	enum {LEFT, CHCASE, LOWER, UPPER, SPACE, ZERO};
+#define BIT_OF(n) (1U<<(n))
 
 	/* various tables, useful in North America */
-	static const char *days_a[] = {
+	static const char days_a[][4] = {
 		"Sun", "Mon", "Tue", "Wed",
 		"Thu", "Fri", "Sat",
 	};
-	static const char *days_l[] = {
+	static const char days_l[][10] = {
 		"Sunday", "Monday", "Tuesday", "Wednesday",
 		"Thursday", "Friday", "Saturday",
 	};
-	static const char *months_a[] = {
+	static const char months_a[][4] = {
 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 	};
-	static const char *months_l[] = {
+	static const char months_l[][10] = {
 		"January", "February", "March", "April",
 		"May", "June", "July", "August", "September",
 		"October", "November", "December",
 	};
-	static const char *ampm[] = { "AM", "PM", };
+	static const char ampm[][3] = { "AM", "PM", };
 
 	if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
 		return 0;
 
 	/* quick check if we even need to bother */
 	if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) {
+	err:
 		errno = ERANGE;
 		return 0;
 	}
@@ -261,12 +267,31 @@
 #endif	/* POSIX_SEMANTICS */
 
 	for (; *format && s < endp - 1; format++) {
-		tbuf[0] = '\0';
+#define NEEDS(n) do if (s + (n) >= endp - 1) goto err; while (0)
+#define FMT(fmt, def_prec, val) \
+		do { \
+			int l; \
+			if (precision == 0) break; \
+			if (precision < 0) precision = (def_prec); \
+			l = snprintf(s, endp - s, fmt, precision, val); \
+			if (l < 0) goto err; \
+			s += l; \
+		} while (0)
+#define STRFTIME(fmt, tm) \
+		do { \
+			i = strftime(s, endp - s, fmt, tm); \
+			if (!i) return 0; \
+			s += i; \
+		} while (0)
+
 		if (*format != '%') {
 			*s++ = *format;
 			continue;
 		}
+		tp = tbuf;
+		sp = format;
 		precision = -1;
+		flags = 0;
 	again:
 		switch (*++format) {
 		case '\0':
@@ -279,16 +304,16 @@
 
 		case 'a':	/* abbreviated weekday name */
 			if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
-				strcpy(tbuf, "?");
+				tp = "?";
 			else
-				strcpy(tbuf, days_a[timeptr->tm_wday]);
+				tp = days_a[timeptr->tm_wday];
 			break;
 
 		case 'A':	/* full weekday name */
 			if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
-				strcpy(tbuf, "?");
+				tp = "?";
 			else
-				strcpy(tbuf, days_l[timeptr->tm_wday]);
+				tp = days_l[timeptr->tm_wday];
 			break;
 
 #ifdef SYSV_EXT
@@ -296,31 +321,31 @@
 #endif
 		case 'b':	/* abbreviated month name */
 			if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
-				strcpy(tbuf, "?");
+				tp = "?";
 			else
-				strcpy(tbuf, months_a[timeptr->tm_mon]);
+				tp = months_a[timeptr->tm_mon];
 			break;
 
 		case 'B':	/* full month name */
 			if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
-				strcpy(tbuf, "?");
+				tp = "?";
 			else
-				strcpy(tbuf, months_l[timeptr->tm_mon]);
+				tp = months_l[timeptr->tm_mon];
 			break;
 
 		case 'c':	/* appropriate date and time representation */
-			strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
-			break;
+			STRFTIME("%a %b %e %H:%M:%S %Y", timeptr);
+			continue;
 
 		case 'd':	/* day of the month, 01 - 31 */
 			i = range(1, timeptr->tm_mday, 31);
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'H':	/* hour, 24-hour clock, 00 - 23 */
 			i = range(0, timeptr->tm_hour, 23);
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'I':	/* hour, 12-hour clock, 01 - 12 */
 			i = range(0, timeptr->tm_hour, 23);
@@ -328,65 +353,69 @@
 				i = 12;
 			else if (i > 12)
 				i -= 12;
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'j':	/* day of the year, 001 - 366 */
-			sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
-			break;
+			FMT("%.*d", 3, timeptr->tm_yday + 1);
+			continue;
 
 		case 'm':	/* month, 01 - 12 */
 			i = range(0, timeptr->tm_mon, 11);
-			sprintf(tbuf, "%02d", i + 1);
-			break;
+			FMT("%.*d", 2, i + 1);
+			continue;
 
 		case 'M':	/* minute, 00 - 59 */
 			i = range(0, timeptr->tm_min, 59);
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'p':	/* am or pm based on 12-hour clock */
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(LOWER);
+			}
 			i = range(0, timeptr->tm_hour, 23);
 			if (i < 12)
-				strcpy(tbuf, ampm[0]);
+				tp = ampm[0];
 			else
-				strcpy(tbuf, ampm[1]);
+				tp = ampm[1];
 			break;
 
 		case 'S':	/* second, 00 - 60 */
 			i = range(0, timeptr->tm_sec, 60);
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'U':	/* week of year, Sunday is first day of week */
-			sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
-			break;
+			FMT("%.*d", 2, weeknumber(timeptr, 0));
+			continue;
 
 		case 'w':	/* weekday, Sunday == 0, 0 - 6 */
 			i = range(0, timeptr->tm_wday, 6);
-			sprintf(tbuf, "%d", i);
-			break;
+			FMT("%.*d", 0, i);
+			continue;
 
 		case 'W':	/* week of year, Monday is first day of week */
-			sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
-			break;
+			FMT("%.*d", 2, weeknumber(timeptr, 1));
+			continue;
 
 		case 'x':	/* appropriate date representation */
-			strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
-			break;
+			STRFTIME("%m/%d/%y", timeptr);
+			continue;
 
 		case 'X':	/* appropriate time representation */
-			strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
-			break;
+			STRFTIME("%H:%M:%S", timeptr);
+			continue;
 
 		case 'y':	/* year without a century, 00 - 99 */
 			i = timeptr->tm_year % 100;
-			sprintf(tbuf, "%02d", i);
-			break;
+			FMT("%.*d", 2, i);
+			continue;
 
 		case 'Y':	/* year with century */
-			sprintf(tbuf, "%ld", 1900L + timeptr->tm_year);
-			break;
+			FMT("%.*ld", 0, 1900L + timeptr->tm_year);
+			continue;
 
 #ifdef MAILHEADER_EXT
 		/*
@@ -406,8 +435,8 @@
 		 */
  		case 'z':	/* time zone offset east of GMT e.g. -0600 */
 			if (gmt) {
-			    strcpy(tbuf, "+0000");
-			    break;
+				FMT("%+.*d", 4, 0);
+				continue;
 			}
 #ifdef HAVE_TM_NAME
 			/*
@@ -438,37 +467,40 @@
 #endif /* !HAVE_TM_ZONE */
 #endif /* !HAVE_TM_NAME */
 			if (off < 0) {
-				tbuf[0] = '-';
 				off = -off;
+				off = -(off/60*100 + off%60);
 			} else {
-				tbuf[0] = '+';
+				off = off/60*100 + off%60;
 			}
-			sprintf(tbuf+1, "%02u%02u", (unsigned)off/60, (unsigned)off%60);
-			break;
+			FMT("%+.*ld", 4, off);
+			continue;
 #endif /* MAILHEADER_EXT */
 
 		case 'Z':	/* time zone name or abbrevation */
+			if (flags & BIT_OF(CHCASE)) {
+				flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
+				flags |= BIT_OF(LOWER);
+			}
 			if (gmt) {
-			    strcpy(tbuf, "UTC");
-			    break;
+				tp = "UTC";
+				break;
 			}
 #ifdef HAVE_TZNAME
 			i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
-			strcpy(tbuf, tzname[i]);
+			tp = tzname[i];
 #else
 #ifdef HAVE_TM_ZONE
-			strcpy(tbuf, timeptr->tm_zone);
+			tp = timeptr->tm_zone;
 #else
 #ifdef HAVE_TM_NAME
-			strcpy(tbuf, timeptr->tm_name);
+			tp = timeptr->tm_name;
 #else
 #ifdef HAVE_TIMEZONE
 			gettimeofday(& tv, & zone);
 #ifdef TIMEZONE_VOID
-			strcpy(tbuf, timezone());
+			tp = timezone();
 #else
-			strcpy(tbuf, timezone(zone.tz_minuteswest,
-						timeptr->tm_isdst > 0));
+			tp = timezone(zone.tz_minuteswest, timeptr->tm_isdst > 0);
 #endif /* TIMEZONE_VOID */
 #endif /* HAVE_TIMEZONE */
 #endif /* HAVE_TM_NAME */
@@ -478,40 +510,40 @@
 
 #ifdef SYSV_EXT
 		case 'n':	/* same as \n */
-			tbuf[0] = '\n';
-			tbuf[1] = '\0';
-			break;
+			NEEDS(1);
+			*s++ = '\n';
+			continue;
 
 		case 't':	/* same as \t */
-			tbuf[0] = '\t';
-			tbuf[1] = '\0';
-			break;
+			NEEDS(1);
+			*s++ = '\t';
+			continue;
 
 		case 'D':	/* date as %m/%d/%y */
-			strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
-			break;
+			STRFTIME("%m/%d/%y", timeptr);
+			continue;
 
 		case 'e':	/* day of month, blank padded */
-			sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
-			break;
+			FMT("%.*d", 2, range(1, timeptr->tm_mday, 31));
+			continue;
 
 		case 'r':	/* time as %I:%M:%S %p */
-			strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
-			break;
+			STRFTIME("%I:%M:%S %p", timeptr);
+			continue;
 
 		case 'R':	/* time as %H:%M */
-			strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
-			break;
+			STRFTIME("%H:%M", timeptr);
+			continue;
 
 		case 'T':	/* time as %H:%M:%S */
-			strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
-			break;
+			STRFTIME("%H:%M:%S", timeptr);
+			continue;
 #endif
 
 #ifdef SUNOS_EXT
 		case 'k':	/* hour, 24-hour clock, blank pad */
-			sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
-			break;
+			FMT("%*d", 2, range(0, timeptr->tm_hour, 23));
+			continue;
 
 		case 'l':	/* hour, 12-hour clock, 1 - 12, blank pad */
 			i = range(0, timeptr->tm_hour, 23);
@@ -519,28 +551,30 @@
 				i = 12;
 			else if (i > 12)
 				i -= 12;
-			sprintf(tbuf, "%2d", i);
-			break;
+			FMT("%*d", 2, i);
+			continue;
 #endif
 
 
 #ifdef VMS_EXT
 		case 'v':	/* date as dd-bbb-YYYY */
-			sprintf(tbuf, "%2d-%3.3s-%4ld",
-				range(1, timeptr->tm_mday, 31),
-				months_a[range(0, timeptr->tm_mon, 11)],
-				timeptr->tm_year + 1900L);
+			w = snprintf(s, endp - s, "%2d-%3.3s-%4ld",
+				     range(1, timeptr->tm_mday, 31),
+				     months_a[range(0, timeptr->tm_mon, 11)],
+				     timeptr->tm_year + 1900L);
+			if (w < 0) goto err;
 			for (i = 3; i < 6; i++)
-				if (islower(tbuf[i]))
-					tbuf[i] = toupper(tbuf[i]);
-			break;
+				if (islower(s[i]))
+					s[i] = toupper(s[i]);
+			s += w;
+			continue;
 #endif
 
 
 #ifdef POSIX2_DATE
 		case 'C':
-			sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 100);
-			break;
+			FMT("%.*ld", 2, (timeptr->tm_year + 1900L) / 100);
+			continue;
 
 
 		case 'E':
@@ -549,14 +583,13 @@
 			goto again;
 
 		case 'V':	/* week of year according ISO 8601 */
-			sprintf(tbuf, "%02d", iso8601wknum(timeptr));
-			break;
+			FMT("%.*d", 2, iso8601wknum(timeptr));
+			continue;
 
 		case 'u':
 		/* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
-			sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
-					timeptr->tm_wday);
-			break;
+			FMT("%*d", 0, timeptr->tm_wday == 0 ? 7 : timeptr->tm_wday);
+			continue;
 #endif	/* POSIX2_DATE */
 
 #ifdef ISO_DATE_EXT
@@ -580,16 +613,17 @@
 				y = 1900L + timeptr->tm_year;
 
 			if (*format == 'G')
-				sprintf(tbuf, "%ld", y);
+				FMT("%.*ld", 0, y);
 			else
-				sprintf(tbuf, "%02ld", y % 100);
-			break;
+				FMT("%.*ld", 2, y % 100);
+			continue;
 #endif /* ISO_DATE_EXT */
 
-		case 'L':	/* millisecond, 000 - 999 */
-			sprintf(tbuf, "%03ld", ts->tv_nsec / 1000000);
-			break;
 
+		case 'L':
+			w = 3;
+			goto subsec;
+
 		case 'N':
 			/*
 			 * fractional second digits. default is 9 digits
@@ -599,60 +633,84 @@
 			 * %6N  microsecond (6 digits)
 			 * %9N  nanosecond (9 digits)
 			 */
+			w = 9;
+		subsec:
 			{
-				char fmt[10];
 				long n = ts->tv_nsec;
 
-				if (precision < 0 || precision > 9) {
-				    precision = 9;
+				if (precision == 0) continue;
+				if (precision < 0) {
+				    precision = w;
 				}
-				if (precision == 0) break;
-				n /= pow(10, 9 - precision);
-				sprintf(fmt, "%%0%dld", precision);
-				sprintf(tbuf, fmt, n);
+				NEEDS(precision);
+				if (precision < w) {
+					snprintf(tbuf, w + 1, "%.*ld", w, n);
+					memcpy(s, tbuf, precision);
+				}
+				else {
+					snprintf(s, endp - s, "%.*ld", w, n);
+					memset(s + w, '0', precision - w);
+				}
+				s += precision;
 			}
-			break;
+			continue;
 
 		case 'F':	/*  Equivalent to %Y-%m-%d */
 			{
 				int mon, mday;
 				mon = range(0, timeptr->tm_mon, 11) + 1;
 				mday = range(1, timeptr->tm_mday, 31);
-				sprintf(tbuf, "%ld-%02d-%02d",
-					1900L + timeptr->tm_year, mon, mday);
+				i = snprintf(s, endp - s, "%ld-%02d-%02d",
+					     1900L + timeptr->tm_year, mon, mday);
+				if (i < 0)
+					goto err;
+				s += i;
 			}
-			break;
+			continue;
 
-		default:
-			if (isdigit(*format)) {
-			    const char *p = format;
-			    while (isdigit(*p)) p++;
-			    if (*p == 'N') {
-				precision = atoi(format);
-				format = p - 1;
+		case '-':
+			flags |= BIT_OF(LEFT);
+			goto again;
+
+		case '^':
+			flags |= BIT_OF(UPPER);
+			goto again;
+
+		case '#':
+			flags |= BIT_OF(CHCASE);
+			goto again;
+
+		case '_':
+			flags |= BIT_OF(SPACE);
+			goto again;
+
+		case '0':
+			flags |= BIT_OF(ZERO);
+		case '1':  case '2': case '3': case '4':
+		case '5': case '6':  case '7': case '8': case '9':
+			{
+				char *e;
+				precision = (int)strtoul(format, &e, 10);
+				format = e - 1;
 				goto again;
-			    }
 			}
-			tbuf[0] = '%';
-			tbuf[1] = *format;
-			tbuf[2] = '\0';
-			break;
+
+		default:
+			tp = sp;
+			i = format - sp + 1;
+			goto copy;
 		}
-		i = strlen(tbuf);
+		i = strlen(tp);
+	copy:
 		if (i) {
-			if (s + i < endp - 1) {
-				strcpy(s, tbuf);
-				s += i;
-			} else {
-				errno = ERANGE;
-				return 0;
-			}
+			NEEDS(i);
+			memcpy(s, tp, i);
+			s += i;
 		}
 	}
 out:
 	if (s >= endp) {
-		errno = ERANGE;
-		return 0;
+		goto err;
 	}
 	if (*format == '\0') {
 		*s = '\0';
Index: test/ruby/test_time.rb
===================================================================
--- test/ruby/test_time.rb	(revision 18772)
+++ test/ruby/test_time.rb	(revision 18773)
@@ -381,17 +381,12 @@
     assert_equal("Sat", Time.at(946684800).strftime("%a"))
 
     t = Time.at(946684800, 123456.789)
-    assert_equal("123", t.strftime("%L"))
-    assert_equal("123456789", t.strftime("%N"))
     assert_equal("123", t.strftime("%3N"))
     assert_equal("123456", t.strftime("%6N"))
     assert_equal("123456789", t.strftime("%9N"))
-    assert_equal("123456789", t.strftime("%10N"))
-    assert_equal("123456789", t.strftime("%1" + "0" * 100 + "N"))
+    assert_equal("1234567890", t.strftime("%10N"))
     assert_equal("", t.strftime("%0N"))
-    assert_equal("%3S", t.strftime("%3S"))
-    fmt = "%1" + "0" * 100 + "S"
-    assert_equal(fmt, t.strftime(fmt))
+    assert_equal("000", t.strftime("%3S"))
 
     t = Time.mktime(2001, 10, 1)
     assert_equal("2001-10-01", t.strftime("%F"))

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]