sxross / MotionModel

Simple Model and Validation Mixins for RubyMotion
MIT License
192 stars 67 forks source link

Date parsing #29

Open Dan2552 opened 11 years ago

Dan2552 commented 11 years ago

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

sxross commented 11 years ago

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".

Dan2552 commented 11 years ago

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
sxross commented 11 years ago

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 notifications@github.com 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.

Dan2552 commented 11 years ago

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

#<NoMethodError: undefined method `parse' for Time:Class>
Dan2552 commented 11 years ago

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)

sxross commented 11 years ago

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 notifications@github.com 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 commented 11 years ago

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:

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 sxross@gmail.com 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 notifications@github.com 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.

DougPuchalski commented 11 years ago

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 <sxross@gmail.com (mailto:sxross@gmail.com)> 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 <notifications@github.com (mailto:notifications@github.com)> 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).