Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea : Date library #1

Open
bmichotte opened this issue Dec 14, 2015 · 29 comments
Open

Idea : Date library #1

bmichotte opened this issue Dec 14, 2015 · 29 comments

Comments

@bmichotte
Copy link
Contributor

It's always a mess to play with dates. Especially with NSDate on iOS and Calendar stuff on Android.

Based on the api from Sugarcube, we could add

Fixnum

  • Fixnum.hour(s), Fixnum.minute(s), Fixnum.second(s), Fixnum.day(s), Fixnum.month(s), Fixnum.year(s)
Time.now + 3.days
Time.now - 1.year

Time

  • Time.from_hash() # .from_parts, .from_???
Time.from_hash(hour: 5, day: 8, month: 10)
  • Time.yesterday?
  • Time.tomorrow?
  • Time.same_day?(another_time)
  • Time.start_of_day
  • Time.end_of_day
  • Time.start_of_week
  • Time.start_of_week(:monday)
  • Time.end_of_week
  • Time.end_of_week(:monday)
  • Time.days_in_month
  • Time.days_in_year
  • Time.days_in_month
  • Time.days_in_year
  • Time.date_array => [2012, 9, 13]
  • Time.time_array => [9, 19, 6]
  • Time.datetime_array => [2012, 9, 13, 9, 19, 6]
  • Time.to_s(format) # using ruby notation %
@jjaffeux
Copy link
Contributor

Hi @bmichotte,

I fully agree that we need some cross platform common time/date/datetime API.

I'm stil not sure about the strategy however :

  • Add some helper methods
  • Write a fully working time/date/datetime (and may be add some of the activesupport methods)
  • Write a full featured library to work with dates like moment.js for example

We would need some popular dates usage and see what we would need to write on android and ios, and see how we could write it cross platform, on top of my mind:

  • parsing iso dates coming from api
  • parsing timestamps
  • working with timezones
  • what about some timeago like API ?

@jjaffeux
Copy link
Contributor

To clarify my point of view, I don't like first solution that much, we provide the same thing people expect from ruby or we provide a full new API, but I don't like that mix of ruby and other things. it's not good for discoverability and people don't expect it. I would never be sure how Time would respond.

@bmichotte
Copy link
Contributor Author

I like the idea of something like moment.js more than Time, Date, and Datetime classes.

And I think we can mix it with helpers for fixnum. And helper for String also can be cool... like '2015-12-14'.to_date

@lrz
Copy link
Member

lrz commented Dec 14, 2015

A date library would be useful, I'm not sure if monkey-patching Fixnum is a good idea for this API, at the same time we already add Object#to_json...

@lrz
Copy link
Member

lrz commented Dec 14, 2015

Maybe a first approach would be to look at date.rb in CRuby's standard library and see what would make sense to be implemented?

@bmichotte
Copy link
Contributor Author

@lrz The same thing could be achieved using something like my_time.add(days: 5) or something like that, but I think my_time + 5.days is really visual and clear

@jjaffeux
Copy link
Contributor

I think the safest best is working on an independent API, we can always pollute std lib methods with call to this independent API after, but creating a cross platform independent API should be our first move IMO.

If it's not clear, what I would do :

module Flow
  class Moment
    def self.parse(string)
      # xxx
    end

    def to_date
      # xxx
    end
  end
end

class String
  def to_date
    Flow::Moment.parse(self).to_date
  end
end

@jjaffeux
Copy link
Contributor

Or we could just get rid of all the Time/Date/DateTime/Calendar/Whatever... and just introduce the concept of 'moment', and pollute string and other classes with ".to_moment"

That would be very nice, easy to use and would still allow other libs/code from people to work without issues.

@jjaffeux
Copy link
Contributor

What I propose:

  • Create top level API, based on moment.js
  • Write tests for this top level API
  • Write implementation for iOS / Android
  • Implement .to_moment on some std class (String, Time, NSDate, ...)
  • Consider and may be implement syntaxic sugar on top of Fixnum

@bmichotte
Copy link
Contributor Author

👍 (very constructive answer, but I'm totally for this idea, so don't have much to say)

@jjaffeux
Copy link
Contributor

@bmichotte If @lrz agrees with this idea, I can work on 1 and 2, and then we can split on 3

@jjaffeux
Copy link
Contributor

Basic top level API proposal

Mostly inspired by momentjs http://momentjs.com/docs. We might add more sugar in the future, but I would like the base API to be most easy and straightforward possible, and I think mommentjs totally achieve this goal. My only grief being Timezone management.

# Default works like Time.now (we could also add Moment.now later)
Moment.new

# Can parse a string
Moment.new("1995-12-25")

# Can parse a string with format
Moment.new("1995-12-25", "YYYY-MM-DD")

# Can parse an array
Moment.new([2010, 1, 14, 15, 25, 50, 125])

# Can parse a hash
Moment.new({ hour:15, minute:10 })

# Can format any Moment
Moment.new.format("MM-DD")

# Can substract and add time
Moment.new.subtract(1, 'day')
Moment.new.add(1, 'day')
Moment.new.subtract(2, 'hours')
Moment.new.add(4, 'hours')

# Support for start_of and end_of
Moment.new.start_of('year')
Moment.new.end_of('month')

# Most of Moment methods return an instance of Moment, chainable
Moment.new.minutes(0).seconds(0).milliseconds(0)

# Comparison
Moment.new <= Moment.new
Moment.new >= Moment.new
Moment.new == Moment.new

# Difference
a = Moment.new
b = Moment.new
a.diff(b, 'seconds') # 0

# Timezones support
Moment.zone = "Europe/Paris"
Moment.new.utc_offset # 60
a = Moment.new
a.zone = "America/Los_Angeles"
a.utc_offset #-480

b = Moment.new
b.zone = "America/Los_Angeles"
b.zone # "America/Los_Angeles"

@bmichotte
Copy link
Contributor Author

What about

# properties
a = Moment.new
a.year | a.year=
a.month | a.month=
# and so on

# Difference
(a - b).days # .day ?
=> 8

# Compatibility
t = Moment.new.to_date
t.class
=> Time

Moment.new(Time.now)

@bmichotte
Copy link
Contributor Author

a = Moment.new
a.zone = :america_los_angeles

@lrz
Copy link
Member

lrz commented Dec 22, 2015

Why not using Date instead of Moment?

@bmichotte
Copy link
Contributor Author

Why not both

(sorry, too tempting)

@jjaffeux
Copy link
Contributor

@bmichotte @lrz

  • accessors instead of chainable API, why not, I'm ok with this, it's probably more Ruby to follow your proposal. However, I really like the fact that every method return a moment (only formatting methods don't) once again it's very easy to work with and to understand. What do you think ?
  • I dont like timezones as symbols, ruby/rails people are used to timezones as strings, it's standard. Moreover, most of the time you don't enter the timezone yourself, it's coming from a database or other code, so adding this convenience wouldn't help much I think.
  • I wanted a different name, and don't use Date or Time, to avoid any class or confusion, I know that I'm working with a Moment it's easy to understand and follow. We provide convenience methods to convert it to Time, NSDate, whatever... to work with other code, but that's all.

@jjaffeux
Copy link
Contributor

One thing I forgot to add in my proposal, I would like the library to support similar method than https://github.com/travisjeffery/timecop to facilitate testing, we would need it to test the lib itself anyways :)

Moment.new("1995-12-25").freeze do |moment|
  Moment.new.to_s.should == "xxxxxxx"
end

@jjaffeux
Copy link
Contributor

See this for example, is very confusing, I would like to avoid this :

t = Moment.new.to_date
t.class
=> Time

I'm working with a Moment, sending to_date, and I get a Time. WAT?

@lrz
Copy link
Member

lrz commented Dec 22, 2015

But Date doesn't exist in RubyMotion, neither in CRuby core (it's defined in date.rb).

@lrz
Copy link
Member

lrz commented Dec 22, 2015

Here is a session from CRuby that isn't confusing?

lrz-mba:motion-game lrz$ irb
irb(main):001:0> require 'date'
=> true
irb(main):002:0> o = Date.parse('2016-02-19')
=> #<Date: 2016-02-19 ((2457438j,0s,0n),+0s,2299161j)>
irb(main):003:0> o.to_time
=> 2016-02-19 00:00:00 +0100
irb(main):004:0> o.to_time.class
=> Time

@bmichotte
Copy link
Contributor Author

@jjaffeux

  • I think we can get both chainable api and accessors. I really like chainable stuff to set stuff, but I think it's more readable to get accessors like a_moment.day to read the values.
  • About the to_date, it's more a RubyMotion thing. You play with Calendar and/or NSDate and it's "converted" to Time. Something like Moment.new.to_date.class => java.util.Date | NSDate could maybe more be relevant, but the Time is more "multiplatform"

@bmichotte
Copy link
Contributor Author

or to_date => java.util.Date | NSDate, to_time.class => Time

@jjaffeux
Copy link
Contributor

Yes Date is not in core CRuby however, people are used to use it, and if we provide it, people will think they are working with the std library CRuby Date.rb So they will be writing ruby, using a Dateobject that is different than the Date object they are used to. That's why I proposed a totally different name, to avoid any confusion.

@jjaffeux
Copy link
Contributor

Moreover, ruby has 3 different concepts : Time, DateTime and Date, which are IMO very confusing to new comers, that's why I don't think we could accurately express those 3 concepts in a Date object, which would loose sense and be too different from regular Date.rb. Moment is a more generic name avoiding those issues.

@jjaffeux
Copy link
Contributor

Using https://github.com/Watson1978/motion-date ./cc @Watson1978 @lrz should be taking into consideration.

Pros

  • A pure ruby implementation would mean only one codebase to maintain
  • Adding new platforms to flow would be facilitated

Cons

  • It probably wouldn't work on android at the moment (how hard would it be to make it work?)
  • Might have a performance hit (we need to test this)
  • Even if only one codebase, it would still be big, around 1,5K loc

Other questions

  • Should we use it as it is ?
  • Use it under the hood of another top level Moment API?

@jjaffeux
Copy link
Contributor

@lrz @bmichotte any comment on this ?

@bmichotte
Copy link
Contributor Author

IMHO, I prefer either Date with all CRuby methods, either Moment with a "different" api

@andrewhavens
Copy link
Collaborator

A few thoughts...I agree that date parsing is not as simple as it should be (in Ruby, RubyMotion, Rails, iOS, etc). Switching between Date, Time, and DateTime objects is confusing. I'm currently using two libraries to do something simple:

  • Parse iso8601 (industry standard?) date string received from my API into a date object that I can work with
  • Display the date in "time ago" format

For the first task, I'm using sugarcube-nsdate to add nsdate method to the string class. This was the only way that I could find to parse my iso8601 date correctly. For the second task, I use the DateTools library for adding the timeAgoSinceNow method. This results in the following code:

date_string_or_nil.to_s.nsdate.timeAgoSinceNow

Not very pretty.

I would rather not have to rely on a handful of large libraries that I only use a small part of. I find that many support libraries end up trying to support every possible use case. However, it seems like a small library (or componentized so you could require just the features you want) which achieve 90% (or even 10%) of the most common use cases would be more helpful than trying to replicate all of Sugarcube, ActiveSupport, etc.

I would be happy with something like this:

Moment.parse(date_string_or_nil).format(:time_ago)
# or even...
Motion::Flow::DateTime.parse_iso8601(date_string_or_nil).to_s_in_time_ago_format

Ultimately, all I want is a relatively intuitive API (no need for monkey patching) from a small library with well documented examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants