MotionModel icon indicating copy to clipboard operation
MotionModel copied to clipboard

Date parsing

Open Dan2552 opened this issue 12 years ago • 8 comments

I'm getting weird results with date parsing

(main)> a.date_column = "2013-04-01T09:00:00+01:00"
=> "2013-04-01T09:00:00+01:00"
(main)> a.date_column
=> 2013-04-01 02:00:00 +0100

edit--- ooer now I'm confused, it uses the NSDate.dateWithNaturalLanguageString method which isn't even an iOS NSDate method but rather an OS X one

Dan2552 avatar Apr 01 '13 15:04 Dan2552

The current version uses a deprecated conversion, 'NSDate.dateWithNaturalLanguageString:`. There's been some discussion about whether this kind of date conversion actually belongs here, but I came up with another solution, which I proposed to @colinta for SugarCube. You can see it here:

https://github.com/rubymotion/sugarcube/blob/master/lib/sugarcube/date_parser.rb

    def cast_to_date(arg)
      case arg
        when String
          return NSDate.dateWithNaturalLanguageString(arg.gsub('-','/'), locale:NSUserDefaults.standardUserDefaults.dictionaryRepresentation)
        when Time
          return NSDate.dateWithNaturalLanguageString(arg.strftime('%Y/%m/%d %H:%M'), locale:NSUserDefaults.standardUserDefaults.dictionaryRepresentation)
        else
          return arg
      end
    end

I do consider this an issue in MotionModel, and if you want a quick fix, there are two paths to take:

Do the cast yourself. I.e., my_model.my_date = MakeADateOutOf(my_form_data)

-or-

Try overriding the cast:

module MotionModel
  module Model
    def cast_to_date(arg)
      case arg
        when String
          return arg.to_date # uses the SugarCube data detector
        when Time
          return arg.strftime('%Y/%m/%d %H:%M').to_date # uses the SugarCube data detector
        else
          return arg
      end
    end
  end
end

I haven't tried this, but I did supply specs for the SugarCube implementation.

If you know your date will always be in the 8601 format, you might be able to use Time.parse.

I'll leave this issue open until I can get it "fixed".

sxross avatar Apr 01 '13 17:04 sxross

turns out Sugarcube doesn't like 8601 either

(main)> SugarCube::DateParser.parse_date("2013-04-01T09:00:00+01:00")
=> 2013-04-01 00:00:00 +0100

If anybody else comes across this, seems you can use NSDateFormatter:

> formatter = NSDateFormatter.new
> formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
> formatter.dateFromString "2013-04-01T09:00:00+01:00"
=> 2013-04-01 09:00:00 +0100

Dan2552 avatar Apr 01 '13 18:04 Dan2552

Then it's not SugarCube -- it's Apple's data detector. Time.parse does understand 8601 according to the documentation. Have you given this a try? Again, if you absolutely know it's 8601, you have significant latitude to override this cast method in MotionModel.

On Apr 1, 2013, at 11:38 AM, Dan2552 [email protected] wrote:

turns out Sugarcube doesn't like 8601 either

(main)> SugarCube::DateParser.parse_date("2013-04-01T09:00:00+01:00") => 2013-04-01 00:00:00 +0100 — Reply to this email directly or view it on GitHub.

sxross avatar Apr 01 '13 19:04 sxross

Where is Time.parse supposed to be defined? I get

#<NoMethodError: undefined method `parse' for Time:Class>

Dan2552 avatar Apr 01 '13 19:04 Dan2552

Bubblewrap has an iso8601 method, alas it doesn't like the time zone

> Time.iso8601 "2012-05-31T19:41:33Z"
=> 2012-05-31 21:41:33 +0200
> Time.iso8601 "2013-04-01T09:00:00+0100"
=> nil

I'm personally fine with using the NSDateFormatter way that I added earlier (I just had a thought that you maybe missed my edit if you were replying by email rather than web)

Dan2552 avatar Apr 01 '13 20:04 Dan2552

Arrrrgh. Ruby 1.8.7 Docs point to this:

http://ruby-doc.org/stdlib-1.8.7/libdoc/time/rdoc/Time.html#method-c-parse

Ruby 1.9.2 docs point to this:

http://ruby-doc.org/stdlib-1.9.2/libdoc/time/rdoc/Time.html#method-c-parse

However... This appears not to have been implemented in RubyMotion as Time is a wrapper for NSDate. I think that's the wrong answer and submitted a ticket: [#648] Time.parse is not implemented.

In the meantime, try this:

class Time def self.from_8601(str) regex8601 = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.)(\d{2})(\d{2})/ matches = str.match(regex8601) return nil if matches.nil? time = Time.new(matches[1].to_i, matches[2].to_i, matches[3].to_i, matches[4].to_i, matches[5].to_i, matches[6].to_i, matches[7] + matches[8] + ':' + matches[9] ) end end

On Apr 1, 2013, at 12:54 PM, Dan2552 [email protected] wrote:

Where is Time.parse supposed to be defined? I get

#<NoMethodError: undefined method `parse' for Time:Class> — Reply to this email directly or view it on GitHub.

sxross avatar Apr 01 '13 21:04 sxross

Follow-up to this. HipByte points out, correctly, that Time.parse is in stdlib and not core. Looks like we'll have to roll our own solutions here.

I'll leave this issue open for discussion about what kind of date input MotionModel should recognize:

  • arbitrary user strings like "tomorrow", "12/18/16", "three days from now"
  • specific strings like rfc8601 (and others?)
  • none of the above
  • all of the above

One place to put core extensions is in the active_support gem (https://github.com/hookercookerman/motion_support). That takes the date-related concerns outside of the data-modeling domain and places it in a more logical location.

On Apr 1, 2013, at 2:03 PM, Steve Ross [email protected] wrote:

Arrrrgh. Ruby 1.8.7 Docs point to this:

http://ruby-doc.org/stdlib-1.8.7/libdoc/time/rdoc/Time.html#method-c-parse

Ruby 1.9.2 docs point to this:

http://ruby-doc.org/stdlib-1.9.2/libdoc/time/rdoc/Time.html#method-c-parse

However... This appears not to have been implemented in RubyMotion as Time is a wrapper for NSDate. I think that's the wrong answer and submitted a ticket: [#648] Time.parse is not implemented.

In the meantime, try this:

class Time def self.from_8601(str) regex8601 = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.)(\d{2})(\d{2})/ matches = str.match(regex8601) return nil if matches.nil? time = Time.new(matches[1].to_i, matches[2].to_i, matches[3].to_i, matches[4].to_i, matches[5].to_i, matches[6].to_i, matches[7] + matches[8] + ':' + matches[9] ) end end

On Apr 1, 2013, at 12:54 PM, Dan2552 [email protected] wrote:

Where is Time.parse supposed to be defined? I get

#<NoMethodError: undefined method `parse' for Time:Class> — Reply to this email directly or view it on GitHub.

sxross avatar Apr 01 '13 21:04 sxross

You may want to have a look at https://github.com/archan937/MocRuby/blob/master/lib/moc_ruby/mocks/mac_ruby-0.12/time.rb.

This and lock-o-motion are becoming valuable additions to my project. I think there may be a lot of MacRuby code that can be leveraged, and good for it to come from a common place.

IMO parsing RFC dates is one thing, natural language is entirely another. I always vote for being explicit on format expectations as incorrect results often go unnoticed.

On Monday, April 1, 2013 at 2:36 PM, s.ross wrote:

Follow-up to this. HipByte points out, correctly, that Time.parse is in stdlib and not core. Looks like we'll have to roll our own solutions here.

I'll leave this issue open for discussion about what kind of date input MotionModel should recognize:

  • arbitrary user strings like "tomorrow", "12/18/16", "three days from now"
  • specific strings like rfc8601 (and others?)
  • none of the above
  • all of the above

One place to put core extensions is in the active_support gem (https://github.com/hookercookerman/motion_support). That takes the date-related concerns outside of the data-modeling domain and places it in a more logical location.

On Apr 1, 2013, at 2:03 PM, Steve Ross <[email protected] (mailto:[email protected])> wrote:

Arrrrgh. Ruby 1.8.7 Docs point to this:

http://ruby-doc.org/stdlib-1.8.7/libdoc/time/rdoc/Time.html#method-c-parse

Ruby 1.9.2 docs point to this:

http://ruby-doc.org/stdlib-1.9.2/libdoc/time/rdoc/Time.html#method-c-parse

However... This appears not to have been implemented in RubyMotion as Time is a wrapper for NSDate. I think that's the wrong answer and submitted a ticket: [#648] Time.parse is not implemented.

In the meantime, try this:

class Time
def self.from_8601(str)
regex8601 = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.)(\d{2})(\d{2})/
matches = str.match(regex8601)
return nil if matches.nil?
time = Time.new(matches[1].to_i,
matches[2].to_i,
matches[3].to_i,
matches[4].to_i,
matches[5].to_i,
matches[6].to_i,
matches[7] + matches[8] + ':' + matches[9]
)
end
end

On Apr 1, 2013, at 12:54 PM, Dan2552 <[email protected] (mailto:[email protected])> wrote:

Where is Time.parse supposed to be defined? I get

#<NoMethodError: undefined method `parse' for Time:Class>

Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHub (https://github.com/sxross/MotionModel/issues/29#issuecomment-15738877).

DougPuchalski avatar Apr 01 '13 21:04 DougPuchalski