[#8484] strptime fails to properly parse certain inputs — <noreply@...>

Bugs item #5263, was opened at 2006-08-01 23:14

13 messages 2006/08/02
[#8485] Re: [ ruby-Bugs-5263 ] strptime fails to properly parse certain inputs — Yukihiro Matsumoto <matz@...> 2006/08/02

Hi,

[#8538] Re: [ ruby-Bugs-5263 ] strptime fails to properly parse certain inputs — nobu@... 2006/08/06

Hi,

[#8561] sandbox timers & block scopes — why the lucky stiff <ruby-core@...>

Two puzzles I am trying to solve:

28 messages 2006/08/08
[#8624] Re: sandbox timers & block scopes — why the lucky stiff <ruby-core@...> 2006/08/15

raise ThisDecayingInquisition, "anyone? anyone at all?"

[#8627] Re: sandbox timers & block scopes — MenTaLguY <mental@...> 2006/08/15

On Wed, 2006-08-16 at 00:35 +0900, why the lucky stiff wrote:

[#8628] Re: sandbox timers & block scopes — why the lucky stiff <ruby-core@...> 2006/08/15

On Wed, Aug 16, 2006 at 02:46:30AM +0900, MenTaLguY wrote:

[#8629] Re: sandbox timers & block scopes — "Charles O Nutter" <headius@...> 2006/08/15

On 8/15/06, why the lucky stiff <ruby-core@whytheluckystiff.net> wrote:

[#8690] a ruby-core primer — why the lucky stiff <ruby-core@...>

Hello, all. I've been working on the ruby-core page for the new Ruby site.

21 messages 2006/08/22

Re: [ ruby-Bugs-5263 ] strptime fails to properly parse certain inputs

From: nobu@...
Date: 2006-08-06 12:03:55 UTC
List: ruby-core #8538
Hi,

At Wed, 2 Aug 2006 17:40:13 +0900,
Yukihiro Matsumoto wrote in [ruby-core:08485]:
> |Date.strptime and DateTime.strptime do not properly parse
> |certain forms of input in which there are no non-numeric
> |separators between the fields.
> |
> |For example, the following code fails:
> |
> |Date.strptime("20060401", "%Y%m%d")
> 
> Just because strptime() %Y parsed 20060401 as a year of 20,060,401
> (year twenty million sixty thousand four hundred and one a.d.), and
> did not find month and day.

How about to accept an optional width?


Index: lib/date/format.rb
===================================================================
RCS file: /cvs/ruby/src/ruby/lib/date/format.rb,v
retrieving revision 1.15
diff -p -u -2 -r1.15 format.rb
--- lib/date/format.rb	6 Aug 2006 11:38:58 -0000	1.15
+++ lib/date/format.rb	6 Aug 2006 11:58:25 -0000
@@ -52,134 +52,139 @@ class Date
 
   def self.__strptime(str, fmt, elem)
-    fmt.scan(/%[EO]?.|./mo) do |c|
-      cc = c.sub(/\A%[EO]?(.)\z/mo, '%\\1')
-      case cc
-      when /\A\s/o
-	str.sub!(/\A[\s\v]+/o, '')
-      when '%A', '%a'
-	return unless str.sub!(/\A([a-z]+)\b/io, '')
+    fmt.scan(/%([EO]|\d+)?(.)|(\s+)|([^%]+)/mo) do |w, c, sp, s|
+      if sp
+	next str.sub!(/\A[\s\v]+/o, '')
+      end
+      if s
+	return unless str.sub!(/\A#{Regexp.quote(s)}/, '')
+	next
+      end
+      w = nil unless w and /\d/ =~ w # ignore E and O flag
+      case c
+      when 'A', 'a'
+	return unless str.sub!(w ? /\A([a-z]{1,#{w}})/i : /\A([a-z]+)\b/io, '')
 	val = DAYS[$1.downcase] || ABBR_DAYS[$1.downcase]
 	return unless val
 	elem[:wday] = val
-      when '%B', '%b', '%h'
-	return unless str.sub!(/\A([a-z]+)\b/io, '')
+      when 'B', 'b', 'h'
+	return unless str.sub!(w ? /\A([a-z]{1,#{w}})/i : /\A([a-z]+)\b/io, '')
 	val = MONTHS[$1.downcase] || ABBR_MONTHS[$1.downcase]
 	return unless val
 	elem[:mon] = val
-      when '%C'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'C'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	elem[:cent] = val
-      when '%c'
+      when 'c'
 	return unless __strptime(str, '%a %b %e %H:%M:%S %Y', elem)
-      when '%D'
+      when 'D'
 	return unless __strptime(str, '%m/%d/%y', elem)
-      when '%d', '%e'
-	return unless str.sub!(/\A ?(\d+)/o, '')
+      when 'd', 'e'
+	return unless str.sub!(w ? /\A ?(\d{1,#{w}})/ : /\A ?(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..31) === val
 	elem[:mday] = val
-      when '%F'
+      when 'F'
 	return unless __strptime(str, '%Y-%m-%d', elem)
-      when '%G'
-	return unless str.sub!(/\A([-+]?\d+)/o, '')
+      when 'G'
+	return unless str.sub!(w ? /\A([-+]?\d{1,#{w}})/ : /\A([-+]?\d+)/o, '')
 	val = $1.to_i
 	elem[:cwyear] = val
-      when '%g'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'g'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..99) === val
 	elem[:cwyear] = val
 	elem[:cent] ||= if val >= 69 then 19 else 20 end
-      when '%H', '%k'
-	return unless str.sub!(/\A ?(\d+)/o, '')
+      when 'H', 'k'
+	return unless str.sub!(w ? /\A ?(\d{1,#{w}})/ : /\A ?(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..24) === val
 	elem[:hour] = val
-      when '%I', '%l'
-	return unless str.sub!(/\A ?(\d+)/o, '')
+      when 'I', 'l'
+	return unless str.sub!(w ? /\A ?(\d{1,#{w}})/ : /\A ?(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..12) === val
 	elem[:hour] = val
-      when '%j'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'j'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..366) === val
 	elem[:yday] = val
-      when '%M'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'M'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..59) === val
 	elem[:min] = val
-      when '%m'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'm'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..12) === val
 	elem[:mon] = val
 =begin
-      when '%N'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'N'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i.to_r / (10**9)
 	elem[:sec_fraction] = val
 =end
-      when '%n'
+      when 'n'
 	return unless __strptime(str, ' ', elem)
-      when '%p', '%P'
+      when 'p', 'P'
 	return unless str.sub!(/\A([ap])(?:m\b|\.m\.)/io, '')
 	elem[:merid] = if $1.downcase == 'a' then 0 else 12 end
-      when '%R'
+      when 'R'
 	return unless __strptime(str, '%H:%M', elem)
-      when '%r'
+      when 'r'
 	return unless __strptime(str, '%I:%M:%S %p', elem)
-      when '%S'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'S'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..60) === val
 	elem[:sec] = val
-      when '%s'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 's'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	elem[:seconds] = val
-      when '%T'
+      when 'T'
 	return unless __strptime(str, '%H:%M:%S', elem)
-      when '%t'
+      when 't'
 	return unless __strptime(str, ' ', elem)
-      when '%U', '%W'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'U', 'W'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..53) === val
-	elem[if c[-1,1] == 'U' then :wnum0 else :wnum1 end] = val
-      when '%u'
-	return unless str.sub!(/\A(\d+)/o, '')
+	elem[if c == 'U' then :wnum0 else :wnum1 end] = val
+      when 'u'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..7) === val
 	elem[:cwday] = val
-      when '%V'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'V'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (1..53) === val
 	elem[:cweek] = val
-      when '%v'
+      when 'v'
 	return unless __strptime(str, '%e-%b-%Y', elem)
-      when '%w'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'w'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..6) === val
 	elem[:wday] = val
-      when '%X'
+      when 'X'
 	return unless __strptime(str, '%H:%M:%S', elem)
-      when '%x'
+      when 'x'
 	return unless __strptime(str, '%m/%d/%y', elem)
-      when '%Y'
-	return unless str.sub!(/\A([-+]?\d+)/o, '')
+      when 'Y'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A([-+]?\d+)/o, '')
 	val = $1.to_i
 	elem[:year] = val
-      when '%y'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when 'y'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i
 	return unless (0..99) === val
 	elem[:year] = val
 	elem[:cent] ||= if val >= 69 then 19 else 20 end
-      when '%Z', '%z'
+      when 'Z', 'z'
 	return unless str.sub!(/\A([-+:a-z0-9]+(?:\s+dst\b)?)/io, '')
 	val = $1
@@ -187,35 +192,16 @@ class Date
 	offset = zone_to_diff(val)
 	elem[:offset] = offset
-      when '%%'
+      when '%'
 	return unless str.sub!(/\A%/o, '')
-      when '%+'
+      when '+'
 	return unless __strptime(str, '%a %b %e %H:%M:%S %Z %Y', elem)
 =begin
-      when '%.'
-	return unless str.sub!(/\A(\d+)/o, '')
+      when '.'
+	return unless str.sub!(w ? /\A(\d{1,#{w}})/ : /\A(\d+)/o, '')
 	val = $1.to_i.to_r / (10**$1.size)
 	elem[:sec_fraction] = val
 =end
-      when '%1'
-	if $VERBOSE
-	  warn("warning: %1 is deprecated; forget this")
-	end
-	return unless str.sub!(/\A(\d+)/o, '')
-	val = $1.to_i
-	elem[:jd] = val
-      when '%2'
-	if $VERBOSE
-	  warn("warning: %2 is deprecated; use '%Y-%j'")
-	end
-	return unless __strptime(str, '%Y-%j', elem)
-      when '%3'
-	if $VERBOSE
-	  warn("warning: %3 is deprecated; use '%F'")
-	end
-	return unless __strptime(str, '%F', elem)
-      when /\A%(.)/m
-	return unless str.sub!(Regexp.new('\\A' + Regexp.quote($1)), '')
       else
-	return unless str.sub!(Regexp.new('\\A' + Regexp.quote(c)), '')
+	return unless str.sub!(/\A%#{Regexp.quote(c)}/, '')
       end
     end
@@ -493,58 +479,62 @@ class Date
   def strftime(fmt='%F')
     o = ''
-    fmt.scan(/%[EO]?.|./mo) do |c|
-      cc = c.sub(/\A%[EO]?(.)\z/mo, '%\\1')
-      case cc
-      when '%A'; o << DAYNAMES[wday]
-      when '%a'; o << ABBR_DAYNAMES[wday]
-      when '%B'; o << MONTHNAMES[mon]
-      when '%b'; o << ABBR_MONTHNAMES[mon]
-      when '%C'; o << '%02d' % (year / 100.0).floor		# P2,ID
-      when '%c'; o << strftime('%a %b %e %H:%M:%S %Y')
-      when '%D'; o << strftime('%m/%d/%y')			# P2,ID
-      when '%d'; o << '%02d' % mday
-      when '%e'; o <<  '%2d' % mday
-      when '%F'; o << strftime('%Y-%m-%d')			# ID
-      when '%G'; o << '%.4d' %  cwyear				# ID
-      when '%g'; o << '%02d' % (cwyear % 100)			# ID
-      when '%H'; o << '%02d' %   hour
-      when '%h'; o << strftime('%b')				# P2,ID
-      when '%I'; o << '%02d' % ((hour % 12).nonzero? or 12)
-      when '%j'; o << '%03d' % yday
-      when '%k'; o <<  '%2d' %   hour				# AR,TZ,GL
-      when '%l'; o <<  '%2d' % ((hour % 12).nonzero? or 12)	# AR,TZ,GL
-      when '%M'; o << '%02d' % min
-      when '%m'; o << '%02d' % mon
+    m = nil
+    form = proc {|m0, f, v| "%#{m || m0}#{f}" % v}
+    fmt.scan(/%([EO]|[-+\#]?\d*\.?\d+)?(.)|([^%]+)/mo) do |m, c, s|
+      next o << s if s
+      s = $&
+      m = "" unless m and /\d/ =~ m # ignore E and O flag
+      case c
+      when 'A'; o << DAYNAMES[wday]
+      when 'a'; o << ABBR_DAYNAMES[wday]
+      when 'B'; o << MONTHNAMES[mon]
+      when 'b'; o << ABBR_MONTHNAMES[mon]
+      when 'C'; o << form['02', 'd', (year / 100.0).floor]	# P2,ID
+      when 'c'; o << strftime('%a %b %e %H:%M:%S %Y')
+      when 'D'; o << strftime('%m/%d/%y')			# P2,ID
+      when 'd'; o << form['02', 'd', mday]
+      when 'e'; o << form['2', 'd', mday]
+      when 'F'; o << strftime('%Y-%m-%d')			# ID
+      when 'G'; o << form['.4', 'd', cwyear]			# ID
+      when 'g'; o << form['02', 'd', (cwyear % 100)]		# ID
+      when 'H'; o << form['02', 'd', hour]
+      when 'h'; o << strftime('%b')				# P2,ID
+      when 'I'; o << form['02', 'd', ((hour % 12).nonzero? or 12)]
+      when 'j'; o << form['03', 'd', yday]
+      when 'k'; o << form['2', 'd', hour]			# AR,TZ,GL
+      when 'l'; o << form['2', 'd', ((hour % 12).nonzero? or 12)] # AR,TZ,GL
+      when 'M'; o << form['02', 'd', min]
+      when 'm'; o << form['02', 'd', mon]
 =begin
-      when '%N'							# GNU date
-	o << '%09d' % (sec_fraction / (1.to_r/86400/(10**9)))
+      when 'N'							# GNU date
+	o << form['09', 'd', (sec_fraction / (1.to_r/86400/(10**9)))]
 =end
-      when '%n'; o << "\n"					# P2,ID
-      when '%P'; o << if hour < 12 then 'am' else 'pm' end	# GL
-      when '%p'; o << if hour < 12 then 'AM' else 'PM' end
-      when '%R'; o << strftime('%H:%M')				# ID
-      when '%r'; o << strftime('%I:%M:%S %p')			# P2,ID
-      when '%S'; o << '%02d' % sec
-      when '%s'							# TZ,GL
+      when 'n'; o << "\n"					# P2,ID
+      when 'P'; o << if hour < 12 then 'am' else 'pm' end	# GL
+      when 'p'; o << if hour < 12 then 'AM' else 'PM' end
+      when 'R'; o << strftime('%H:%M')				# ID
+      when 'r'; o << strftime('%I:%M:%S %p')			# P2,ID
+      when 'S'; o << form['02', 'd', sec]
+      when 's'							# TZ,GL
 	d = ajd - self.class.jd_to_ajd(self.class.civil_to_jd(1970,1,1), 0)
 	s = (d * 86400).to_i
-	o << '%d' % s
-      when '%T'; o << strftime('%H:%M:%S')			# P2,ID
-      when '%t'; o << "\t"					# P2,ID
-      when '%U', '%W'
+	o << form['', 'd', s]
+      when 'T'; o << strftime('%H:%M:%S')			# P2,ID
+      when 't'; o << "\t"					# P2,ID
+      when 'U', 'W'
 	a = self.class.civil_to_jd(year, 1, 1, ns?) + 6
 	k = if c[-1,1] == 'U' then 0 else 1 end
 	w = (jd - (a - ((a - k) + 1) % 7) + 7) / 7
-	o << '%02d' % w
-      when '%u'; o <<   '%d' % cwday				# P2,ID
-      when '%V'; o << '%02d' % cweek				# P2,ID
-      when '%v'; o << strftime('%e-%b-%Y')			# AR,TZ
-      when '%w'; o <<   '%d' % wday
-      when '%X'; o << strftime('%H:%M:%S')
-      when '%x'; o << strftime('%m/%d/%y')
-      when '%Y'; o << '%.4d' %  year
-      when '%y'; o << '%02d' % (year % 100)
-      when '%Z'; o << (if offset.zero? then 'Z' else strftime('%z') end)
-      when '%z'							# ID
+	o << form['02', 'd', w]
+      when 'u'; o << form[  '', 'd', cwday]			# P2,ID
+      when 'V'; o << form['02', 'd', cweek]			# P2,ID
+      when 'v'; o << strftime('%e-%b-%Y')			# AR,TZ
+      when 'w'; o << form[  '', 'd', wday]
+      when 'X'; o << strftime('%H:%M:%S')
+      when 'x'; o << strftime('%m/%d/%y')
+      when 'Y'; o << form['.4', 'd', year]
+      when 'y'; p year; o << form['02', 'd', (year % 100)]
+      when 'Z'; o << (if offset.zero? then 'Z' else strftime('%z') end)
+      when 'z'							# ID
 	o << if offset < 0 then '-' else '+' end
 	of = offset.abs
@@ -553,29 +543,12 @@ class Date
 	o << '%02d' % hh
 	o << '%02d' % mm
-      when '%%'; o << '%'
-      when '%+'; o << strftime('%a %b %e %H:%M:%S %Z %Y')	# TZ
+      when '%'; o << '%'
+      when '+'; o << strftime('%a %b %e %H:%M:%S %Z %Y')	# TZ
 =begin
       when '%.'
 	o << '%06d' % (sec_fraction / (1.to_r/86400/(10**6)))
 =end
-      when '%1'
-	if $VERBOSE
-	  warn("warning: %1 is deprecated; forget this")
-	end
-	o <<   '%d' % jd
-      when '%2'
-	if $VERBOSE
-	  warn("warning: %2 is deprecated; use '%Y-%j'")
-	end
-	o << strftime('%Y-%j')
-      when '%3'
-	if $VERBOSE
-	  warn("warning: %3 is deprecated; use '%F'")
-	end
-	o << strftime('%F')
-      when /\A%(.)/m
-	o << $1
       else
-	o << c
+	o << s
       end
     end


-- 
Nobu Nakada

In This Thread