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

request: mechanism to parse date strings limited to future or past #141

Closed
ziIizeqQ opened this issue May 8, 2012 · 32 comments
Closed

request: mechanism to parse date strings limited to future or past #141

ziIizeqQ opened this issue May 8, 2012 · 32 comments

Comments

@ziIizeqQ
Copy link

ziIizeqQ commented May 8, 2012

There are many applications in which a user can be expected to input a date that must be in the future or must be in the past. Sugar's Date.create() mechanism is powerful but doesn't seem to expose a way for the programmer to hint the user's future or past intention if it is known. This limits its utility.
In these cases, the user is required what seems to be redundant information. For instance, if I'm scheduling an appointment I should only need to say '4 hours' rather than 'in 4 hours'. Likewise, if I say 'Tuesday' or 'January' I'd expect to get the closest match in the future and rather than having to say 'next Tuesday' or 'next January'.
It's possible for a user of the library to work around this by adding prefix/postfix hints to the user input, but this solution isn't portable and can go wrong in unexpected ways. (As an example, try passing the string 'feb of next month' to Date.create())

@andrewplummer
Copy link
Owner

It seems like a bit of a stretch. For example 4 hours is intelligible as a duration, but not a specific point in time. Date.create is meant to parse out specific points in time. I also don't understand your example about "feb of next month". There are other more intelligent ways about trying to get what you want that won't muddle the API, but I'd need a more specific example to think of one...

@ziIizeqQ
Copy link
Author

ziIizeqQ commented May 8, 2012

It's possible that I'm just missing something in the API. Here are a few more examples, please tell me what is the best way to handle these. Let's say the use case is scheduling an appointment, for which only future values are valid.

  • On Friday, the user enters Wed
  • On Sep 4th, the user enters Jan 15th
  • At 11am, the user enters 5:00

@andrewplummer
Copy link
Owner

Well let's say for example that the user enters Jan 15th on Sep 4th, like the middle example. If you know that they are intending to be the future:

var d = Date.create(userInput);
if(d.isPast()) {
  d = d.advance({ year: 1});
}

@ziIizeqQ
Copy link
Author

ziIizeqQ commented May 8, 2012

In order to advance the date I'd have to pick an interval. 1 year doesn't work for the other examples, but picking the right interval doesn't seem possible without knowing what kind of input the user has provided (eg day-of-week, date, time, etc). As far as I can tell sugar doesn't expose that.

@andrewplummer
Copy link
Owner

Hm... well this really begs a larger question now I think. Let's say that you want the date to be in the future, but what they enter is June 11, 1998.

What then?

@ziIizeqQ
Copy link
Author

ziIizeqQ commented May 8, 2012

If I've called an API to parse a future date, a date string that cannot be parsed as a future value should be rejected. Just like any other invalid input.

@andrewplummer
Copy link
Owner

I see... Well even if something like this were to exist, it wouldn't belong as part of create.... what about something like this:

var d = Date.create(userInput);
if(d.isPast()) {
  d = Date.create('next ' + userInput);
}
if(d.isPast() || !d.isValid()) {
  // Need to enter a valid date!
}

@ziIizeqQ
Copy link
Author

ziIizeqQ commented May 8, 2012

Agreed, create would feel overloaded. parseFuture and parsePast would be one way to expose it.

As I mentioned in the original comment, it is possible to move towards this behavior be adding prefix/postfix strings to the users' input. But this means you don't get to take advantage of the localization built into sugar. Also, in practice you need to try lots of different mutations to get this to work out well. (+ ' tomorrow', + ' of next week', etc). And that is how you discover that sugar will accept feb of next month and create a valid (but nonsensical) Date object with it.

@andrewplummer
Copy link
Owner

Yea... I'm trying to fight it but I get the need... In fact Sugar does have an internal property that is the specificity of the date as it was parsed. For example next week would be week, a year from now would be year, tomorrow, is day, etc.

This could I suppose be retooled in this way. Perhaps Date.future() and Date.past() makes sense....

@jikamens
Copy link
Contributor

I would also really like to see this feature. Personally, I would like to see the future/past thing be a preference rather than an absolute restriction. So the question above about what to do if the user enters something like "June 11, 1998" would, in my opinion, be answered, "Sugar should return a Date object for June 11, 1998." The only time the past/future preference would come into play is if the date is ambiguous.

@jikamens
Copy link
Contributor

Note that the trick of adding suffixes like tomorrow, of next week, of next month, of next year, etc. breaks down when the user has entered a time as well as a date. So, for example, "May 28 of next year" might work, but "May 28 00:00:00 of next year" does not. This really needs to be implemented inside the library to work properly.

@andrewplummer
Copy link
Owner

So how do you feel about Date.future and Date.past?

@jikamens
Copy link
Contributor

jikamens commented Jun 4, 2012

Personally, my preference is for APIs that have a limited number of entry-points with a mechanism for passing behavior options into the entry points, rather than for APIs that have many entry-points that do slightly different things.
Therefore, if this were my API (and it's not! you should implement it however you prefer; I'm just giving you my personal preferences for you to take into consideration however you see fit), what I would do is allow the second argument to Date.create by an object rather than a locale, with options specified in that object. This allows for essentially infinite room for enhancements in the future that require new configuration options. For example:
Date.create(string, { locale: locale-variable, preferFuture: true });

@ziIizeqQ
Copy link
Author

ziIizeqQ commented Jun 4, 2012

Andrew, I like your proposal for Date.future and Date.past. It's possible to compose preferential behavior from these two (e.g. Date.future(x) || Date.past(x)) and I think the semantics would be clear from the names of the functions.

@inossidabile
Copy link

+1 to future and past

@andrewplummer
Copy link
Owner

I have to agree... I like how explicit that interface is.

@andrewplummer
Copy link
Owner

Ok, now that I'm getting around to this there I'd like to list out my thinking in this area. I'm going to list out all the date formats brought up in this thread:

  1. 4 hours
  2. Tuesday
  3. January
  4. Wednesday (entered on friday)
  5. January 15th (entered on September 4th)
  6. 5:00am (entered at 11am)

First, #1 is clearly the odd man out. "What time are you going?" - "4 hours" is not an intelligible answer in the first place and needs more context, so that's out (there are a lot better ways to do this depending on an apps logic, anyway... Note that I understand that "in 4 hours" IS intelligible, and I'm going to add this one, as I already have "4 hours from now" and that is equivalent).

So, 2-5 are clearly valid, and they all have something in common. They contain an ambiguity one unit higher than their highest defined unit. In other words, if "tuesday" is a "weekday", then the "week", which is one unit higher than "day", is ambiguous, and can be subject to being shifted. Likewise, if "January" is a month, then the "year" is ambiguous. Finally, "5:00am" has a defined "hour", and therefore the "day" is ambiguous... it could be yesterday, today, or tomorrow.

Note that what I'm calling "highest defined unit" is not the same as the specificity that I have to work with internally. For example, "tuesday" would have "weekday" as both it's "highest defined unit" and it's "specificity" (most specific unit), however "tuesday 3:25pm" is also ambiguous, but has "minutes" as it's specificity, so something other than specificity is required here.

Also note that these 3 units... "month", "weekday", and "hour", seem to be the only ones for which this ambiguity applies. Here is a list of other units and a description of why they don't work:

  1. "year": 2001
  2. "week": the 2nd week
  3. "date": the 15th
  4. "minute/second/millisecond": the 45th minute

#1 is invalid because there is only one 2001

#2 is perhaps arguable, but I can't imagine a situation in which someone would refer to "the 2nd week" without a point of reference such as "the 2nd week of March". Also there is another ambiguity that would make this difficult as ordinal weeks may be in relation to months ("2nd week of March"), or in relation to years (ISO weeks are 0-52), so I think this one is best left untouched.

#3 is not even supported as a format in the first place, (which I think I may fix), however I think this is also invalid for the purposes of the conversation here. Intuition is what drives my thinking here, but then again such is human language. When discussing your plans, saying "sunday" pretty clearly means "the next sunday" (coming up). Conversely, when discussing something you did, it means "the last sunday" (that just passed). However, saying "I'm going on the 15th" on the 30th of a given month is still pretty ambiguous. Granted, in the full context of a month it's understandable (let's say you're talking about the month of November and say "on the 15th"), but barring that extra, non-existent context, it's pretty much safe to assume that the speaker means "the 15th of THIS month", unless they explicitly say "on the 15th of next month" (which is a format Sugar can already parse... w00t!).

#4 makes no sense because no one (that I know of) refers to a specific minute, second, or millisecond with an ambiguous parent unit. This would be the equivalent of saying "I'm going on the 45th minute" ... well which 45th minute?... the last hour's, this hour's, or the next hour's? People just don't talk that way.

So, that's where I'm at. It seems like the required units that need to have this ambiguity handled are "month", "weekday", and "hour" only...

Also note that as suggested above, these would be preferences only. Sugar will never return an invalid date if the date can be parsed so if you have, for example, free user input for a "todo" app or something, and they still enter a date in the past, you will have to manually check the output of Date.future to ensure that it actually is in the future. (both isFuture and isPast exist for this purpose).

Any other thoughts?

@andrewplummer
Copy link
Owner

Next question:

If user input is Sunday, and today is Sunday, should Date.future consider that to mean today or next Sunday? Keep in mind that Sunday is not yet over :)

Edit: Considering creating a date resets the time, I'm leaning toward Date.future('Sunday') as always being in the future. Therefore if you run that code on a Sunday, you will get next sunday, as it would otherwise actually be in the past.... thoughts?

@Mottie
Copy link

Mottie commented Jun 24, 2012

I'd vote for making it next sunday as the user should use today for today and not Sunday.

@jikamens
Copy link
Contributor

Personally, I think your third case above, "the 15th" (or better "on the 15th") is a reasonable format to support for past and future dates. I can certainly imagine someone trying to specify a date in that format. Having said that, it would probably be above-and-beyond the minimum viable product, i.e., I certainly don't think it's a format you must support.

I agree that Date.future('Sunday') (if it's currently Sunday) should return next Sunday, not today.

@ziIizeqQ
Copy link
Author

Andrew, I think most of your reasoning makes sense here.

There's at least one important case I can think of that might not be covered:

  • 5:00 (entered at 11am)
    In this case a user accustomed to a 12hr clock (as in the US generally) means 5pm the same day, rather than 5am the next. I can imagine wanting different behaviors here depending on the locale.

I'm in agreement with general consensus on Sunday (should interpret as next Sunday).

I don't think I follow your reasoning wrt the 15th. Saying "I'm going on the 15th" on the 30th of June seems unambiguously to refer to July 15th, and is the kind of thing I hear (and say) in conversation all the time. Perhaps it would be useful here to think about things in terms of an "implied unit" rather than an ambiguity which is always "one unit higher". In the case of the 15th, the month is implied but the implication depends on context (past/future/nearest).

Finally, wrt the 4 hours example: I agree that semantically this is duration and not a date. I'd still like to be able to let a user enter either and make sense of it where a duration might make sense in a given context (this depends heavily on what question you've asked the user). Is there a way to parse a string as a duration? Date.create('now').advance('4 hours') does not seem to do the trick.

@andrewplummer
Copy link
Owner

Okay... first I want to note that we're walking a fine line here.... most of the points made could be argued either way. The challenge for me here is to figure out what can be intuited beyond a reasonable doubt and without any other context. The second part is key because with the proper context, most of these things make sense, and of course it is reasonable to expect them. However the separation comes in the fact that "context" in this sense effectively means application logic that the developer provides to Sugar... for example in the act of using Date.future instead of Date.create he does this. My goal is to provide that means for context to be passed.

So, in the first case, yes you're right that 5:00 probably means "5pm of the same day" and not "5am of the next day", however, think about how often saying "5:00" prompts a response for clarification... It could mean either of those, or even neither... it entirely depends on context. It's this context that the application needs to provide. Furthermore, you're right that this would probably be locale dependent, and I would have no idea how to go about determining what locales would allow this, as everyone's opinion differs on the issue. Even for English, it's likely you'll find people who disagree that 5:00 means 5pm when it's 11am... Even if you could get Americans to agree upon it, Brits may disagree, etc. etc. The problem with this is that the API would provide what would appear to be erratic behavior by default based on rules that are at least partially subjective. Just now, I was about to say "this needs to be handled case-by-case, as I'm sure other subjective rules exist for locales". But looking for an example, there actually don't seem to be. The locale-specific rules are all very basic ones that can be agreed on by any speaker of that language. The bottom line is that the cost of handling something that is this subjective (an erratic API) is too high for this case I think. Last, compare this to the weekday format (Sunday entered on a Monday meaning the next Sunday). This is also slightly subjective but I think it can be agreed upon with a much higher rate of consensus. Also, it maintains logical consistency within its own rules (create will always be this week, future will always be in the future, past will always be in the past), and most importantly, it does not behave differently across locales. This is key as having subtle parsing differences based on subjective concepts would make for a very frustrating experience. Human language is subtle, I know, but there are some cases when consistency trumps being too smart. You see this ALL OVER the web.

Similarly, saying the 15th on the 30th of June... or to make it more clear let's say the 20th of June, is clearly the 15th of July when there is a context of "next month" or when July has been mentioned, but without that context, if it were a conversation my first response would be to clarify the month. Without that additional context I, assuming the next month is quite a stretch. I can imagine hearing "I'm going on the 15th" when already talking about July or "next month", but if someone said that with no context, the next sentence would almost inevitably be "you mean July 15th?". Note here that the nth as a standalone date format doesn't even work with create at the momtent, and I'm still debating it... it's just too vague.

With the final example, the point is that durations don't make sense as dates unless your application is forcing that connection somehow. That may be perfectly valid, but it's that extra context that your application has to add. The sentence "and make sense of it where a duration might make sense in a given context (this depends heavily on what question you've asked the user)" is entirely the responsibility of the application, not Sugar. The example with advance isn't correct because parsing is for creating dates, but advance is for manipulating dates (you pass an object, not a string in the first place). To give a hard example here, an application that provides added context by assuming that 4 hours is 4 hours in the future would have to do something like this:

var match, d;
match = input.match(/(\d+) hours/);
if(match) {
  d = new Date().advance({ hours: match[1] });
} else {
  d = Date.create(input);
}

This code is perfectly fine... it's an example of how extra context needs to be applied by the application if needed before reaching Sugar (or to be more clear, the Sugar parsing mechanism).

Note however that I have added in 4 hours as a valid format, so this is a bit of a contrived example... adding "in" to the front of the string would be a much more robust solution in this case.

@ziIizeqQ
Copy link
Author

ziIizeqQ commented Jul 7, 2012

No question this is tricky stuff. At the library level there's really no way to anticipate all the different types of interactions the programmer might want to have with the user. I agree that its to no one's benefit to have a library that seems to be making subjective decisions that the programmer can't predict.
Here's a proposal then to try make things more well-defined:

  • Date.future accepts an optional anchor date (implicitly now)
  • Date.future returns the smallest possible Date value for the input string that is greater than the anchor date

This gives the programmer a clear contract and provides a mechanism for behaviors to be fine-tuned based on specific contexts/interactions. In the 5:00 case for example it would be easy for the programmer to advance to the next possible value if 5pm was not appropriate in context by feeding the result to another call to Date.future as the anchor date.

I completely agree that durations should be treated differently than dates, and that it would not make sense for a function that parses dates to also parse a duration (sorry if that wasn't clear). I wasn't sure if there was a duration parsing function somewhere in the library that I wasn't aware of: the answer seems to be no. I think it would be useful (again, as a distinct function) - perhaps I should file that as a separate request though.
Just to reiterate a point from earlier: in some cases the desired result can be achieved by massaging the input string (adding "in" as a prefix, etc), but this limits the utility of the library to the programmer since the advantage of the library understanding different languages and locales is lost.

@andrewplummer
Copy link
Owner

ok thanks to everyone who weighed in here... v1.3 now gives you Date.past and Date.future!

@ziIizeqQ
Copy link
Author

ziIizeqQ commented Aug 9, 2012

Thanks Andrew, this is certainly a nice addition to the API.
A few issues I noticed after upgrading:

  • Date.advance() will throw an exception for some inputs, such as ''. This seems reasonable but doesn't appear to be documented and is inconsistent with the behavior of Date.create(). Date.advance also accepts invalid input strings and does nothing, so if you want to know if the input string worked you need to check to see if the date was changed. It would be easier if it returned null or threw an exception in this case.
  • Date.future() sometimes fails to live up to its contract and returns a date in the past. For instance, at 9pm calling Date.future('7pm') returns a date 2 hours into the past

@andrewplummer
Copy link
Owner

I'll have a look at the advance vs. create inconsistencies...

The second part is something I have fixed recently... I'm assuming you're using 1.3? Can you give it a shot in the edge build (in the releases/edge directory in the repo?)

@andrewplummer andrewplummer reopened this Aug 9, 2012
@ziIizeqQ
Copy link
Author

ziIizeqQ commented Aug 9, 2012

I tried out the edge build and still get the same problem with Date.future()

@andrewplummer
Copy link
Owner

Hm that's weird... it seems to be working for me... can you provide an exact repro and environment?

Also note that if the date is explicitly in the past, Date.future will return it in the past. It's only a guide when ambiguity exists.

@ziIizeqQ
Copy link
Author

ziIizeqQ commented Aug 9, 2012

Same test case but this time I was testing at 9am and used the input string '8am'

I made an argument earlier for having Date.future accept an optional anchor date - that would also make testing this type of thing easier.

Having Date.future return a value in the past in any circumstances is confusing. I think it would be better to have it return null if a future value can't be parsed.

@jikamens
Copy link
Contributor

jikamens commented Aug 9, 2012

On 8/9/2012 1:00 PM, ziIizeqQ wrote:

I made an argument earlier for having |Date.future| accept an optional
anchor date - that would also make testing this type of thing easier.
+1

Having |Date.future| return a value in the past in any circumstances
is confusing. I think it would be better to have it return |null| if a
future value can't be parsed.

-1 I prefer the current behavior. I consider .future and .past to be
hints, not absolute. Your code can check if the date is before or after
now if you need to know for certain.

jik

@andrewplummer
Copy link
Owner

Definitely don't want to have it return null... that's logic that belongs in your application.

Mostly against the anchor date because of overloading the arguments... also I think that if you're going that far what you need can be accomplished somehow else.

@andrewplummer
Copy link
Owner

Ok this issue is now fixed in 1.3.1.

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

5 participants