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

Made DateFormat Extensible #12799

Merged
merged 10 commits into from
Aug 28, 2015
Merged

Made DateFormat Extensible #12799

merged 10 commits into from
Aug 28, 2015

Conversation

omus
Copy link
Member

@omus omus commented Aug 25, 2015

When I was working on TimeZones.jl I found out that the DateFormat code was written in such a way that made it difficult to extend. As a temporary solution in TimeZones.jl I overwrote some of the Base.Dates methods such that I could include support for new slot letters 'Z' and 'z'.

The following pull request mades the Base.Dates implementation of DateFormat easier for the TimeZones.jl package to extend and additionally fixes some minor bugs.

@StefanKarpinski
Copy link
Sponsor Member

Nice! cc: @quinnj

slotrule(::Type{Val{'M'}}) = Minute
slotrule(::Type{Val{'S'}}) = Second
slotrule(::Type{Val{'s'}}) = Millisecond
slotrule{c}(::Type{Val{c}}) = Union{}
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than dynamically dispatching on Val{::Char}, i think this would be better represented as a Dict{Char, DataType} (less boilerplate and indirection, plus more features like iteration and default get):
e.g:

slotrule['y'] = Year
slotrule['m'] = Month
...

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also implement it with an array indexed by character offset:

const slotrule = Array{DataType}(53)
slotrule['y'-'e'] = Year
...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally had implemented this as a Dict but switched to using value types as made the code appear intended to be extensible rather than just happen to be. The one advantage of switching to a Dict gets us is that the regular expression in DateFormat can restricted to the letters used.

I'm intending to stick with the dynamic dispatch solution unless someone has a solid reason against it.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike the use of multiple dispatch to pun on a Dict (or any other primitive data structure for that matter). This is being used to form a simple Associative mapping, there is no need to hide that in the generic dispatch machinary. The use of Val is a strong indicator to me that this code is not optimal style. This is also much more verbose and less clear than using the actual data structure this is mimicking, it is substantially slower, you can't change or remove items, and extending it at runtime can cause you to run into code statepoints errors (I don't have great terminology for this error, but it's somewhat related to # 265).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a much better argument for not using the value types. I'll make the switch to Dict.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a simple lookup table? A Dict whose keys are bytes seems nuts. If you want it to be extensible to other ASCII characters you've already got the slots.

@omus
Copy link
Member Author

omus commented Aug 26, 2015

@StefanKarpinski: I may switch to using a lookup table yet. I mostly care about extensibility support so the implementation we end up using should be easy to understand.

@StefanKarpinski
Copy link
Sponsor Member

Why not just do it now? Skip subtracting an offset and just pre-allocate a 256-element array of types:

const slotrule = Array{DataType}(256)
slotrule['y'] = Year
...

Now it's extensible simply by assigning new types into the array.

@iamed2
Copy link
Contributor

iamed2 commented Aug 26, 2015

@StefanKarpinski You can't index with char right now so there has to be a wrapper.

@StefanKarpinski
Copy link
Sponsor Member

Right, still pretty simple to extend and likely to become more so.

@omus
Copy link
Member Author

omus commented Aug 28, 2015

@StefanKarpinski: Things look good to you? Who should I mention to get this merged?

@StefanKarpinski
Copy link
Sponsor Member

Looks fine but we're in the 0.4 feature freeze window now, so it'll have to wait until after the release.

@omus
Copy link
Member Author

omus commented Aug 28, 2015

Ok. That's kinda too bad. TimeZones.jl needed this to get rid of some code it needed to overwrite in Base.Dates.

@quinnj
Copy link
Member

quinnj commented Aug 28, 2015

Yeah, this would really be nice to get in if only for proper TimeZone support. Where it's fairly isolated from the rest of Base code, it shouldn't disrupt anything else. Definitely understand though if we're a little too late here.

@StefanKarpinski
Copy link
Sponsor Member

Your call, @quinnj – the date/time code is your domain. I'm ok with it and this does look safe.

@omus
Copy link
Member Author

omus commented Aug 28, 2015

@quinnj I'd be grateful if we could get this in for the 0.4 release. Let me know if you need me to do anything to make this happen.

quinnj added a commit that referenced this pull request Aug 28, 2015
@quinnj quinnj merged commit c321bf8 into JuliaLang:master Aug 28, 2015
@quinnj
Copy link
Member

quinnj commented Aug 28, 2015

Thanks @omus. Just wanted to take some time to review. Looks good. Let the timezones zone!

@omus
Copy link
Member Author

omus commented Aug 28, 2015

Sweet! Thanks for getting this done @quinnj!

@omus omus deleted the dates_extensibility branch August 28, 2015 22:09
@@ -183,6 +183,17 @@ end
@test Dates.lastdayofquarter(Dates.DateTime(2014,8,2)) == Dates.DateTime(2014,9,30)
@test Dates.lastdayofquarter(Dates.DateTime(2014,12,2)) == Dates.DateTime(2014,12,31)

first = Dates.Date(2014,1,1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this causes an imported binding for first overwritten in module TestDates warning, should really use a let block or a different variable name for these

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just rename the variables from first and last to firstday and lastday.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay done in f1e6442

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dates Dates, times, and the Dates stdlib module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants