Open bmichotte opened 8 years ago
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 :
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:
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.
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
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...
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?
@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
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
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.
What I propose:
:+1: (very constructive answer, but I'm totally for this idea, so don't have much to say)
@bmichotte If @lrz agrees with this idea, I can work on 1 and 2, and then we can split on 3
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"
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)
a = Moment.new
a.zone = :america_los_angeles
Why not using Date
instead of Moment
?
(sorry, too tempting)
@bmichotte @lrz
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
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?
But Date
doesn't exist in RubyMotion, neither in CRuby core (it's defined in date.rb).
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
@jjaffeux
a_moment.day
to read the values.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"or to_date => java.util.Date | NSDate
, to_time.class => Time
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 Date
object that is different than the Date
object they are used to. That's why I proposed a totally different name, to avoid any confusion.
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.
Using https://github.com/Watson1978/motion-date ./cc @Watson1978 @lrz should be taking into consideration.
@lrz @bmichotte any comment on this ?
IMHO, I prefer either Date
with all CRuby methods, either Moment
with a "different" api
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:
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.
It's always a mess to play with dates. Especially with
NSDate
on iOS andCalendar
stuff on Android.Based on the api from Sugarcube, we could add
Fixnum
Time
Time.from_hash() # .from_parts, .from_???
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 %