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

Fractional deltas result in incoherent results. #100

Closed
fake-name opened this issue Jun 18, 2015 · 5 comments
Closed

Fractional deltas result in incoherent results. #100

fake-name opened this issue Jun 18, 2015 · 5 comments

Comments

@fake-name
Copy link
Contributor

I'm working with a lot of "human readable" delta times, e.g. "5.1 hours ago".

The sensibility of fractional hour representation aside, parsing strings like that seems to lead to inconsistent, and frankly odd results a lot of the time.

Here is a set of test examples for this:

('Ago:', '7.2 days ago',     datetime.datetime(2015, 6, 19, 22, 39, 8),  datetime.timedelta(-2, 0, 197324),     '-2 days',                       1)
('Ago:', '7.3 days ago',     datetime.datetime(2015, 6, 20, 22, 39, 8),  datetime.timedelta(-3, 0, 198012),     '-3 days',                       1)
('Ago:', '1.4 months ago',   datetime.datetime(2015, 10, 17, 22, 39, 8), datetime.timedelta(-122, 0, 198693),   '-122 days',                     1)
('Ago:', '7.6 days ago',     datetime.datetime(2015, 6, 23, 22, 39, 8),  datetime.timedelta(-6, 0, 199327),     '-6 days',                       1)
('Ago:', '17.7 days ago',    datetime.datetime(2015, 6, 24, 22, 39, 8),  datetime.timedelta(-7, 0, 199967),     '-7 days',                       1)
('Ago:', '3.4 months ago',   datetime.datetime(2015, 10, 17, 22, 39, 8), datetime.timedelta(-122, 0, 200605),   '-122 days',                     1)
('Ago:', '11.3 days ago',    datetime.datetime(2015, 6, 20, 22, 39, 8),  datetime.timedelta(-3, 0, 201246),     '-3 days',                       1)
('Ago:', '11.6 days ago',    datetime.datetime(2015, 6, 23, 22, 39, 8),  datetime.timedelta(-6, 0, 201877),     '-6 days',                       1)
('Ago:', '4.8 months ago',   datetime.datetime(2016, 2, 17, 22, 39, 8),  datetime.timedelta(-245, 0, 202624),   '-245 days',                     1)
('Ago:', '5.1 months ago',   datetime.datetime(2015, 7, 17, 22, 39, 8),  datetime.timedelta(-30, 0, 203289),    '-30 days',                      1)
('Ago:', '13 hours ago',     datetime.datetime(2015, 6, 17, 9, 39, 8),   datetime.timedelta(0, 46800, 203899),  '13 hours',                      2)
('Ago:', '1.3 days ago',     datetime.datetime(2015, 6, 20, 22, 39, 8),  datetime.timedelta(-3, 0, 204543),     '-3 days',                       1)
('Ago:', '3.6 months ago',   datetime.datetime(2015, 12, 17, 22, 39, 8), datetime.timedelta(-183, 0, 205183),   '-183 days',                     1)
('Ago:', '23.2 days ago',    datetime.datetime(2015, 6, 19, 22, 39, 8),  datetime.timedelta(-2, 0, 205816),     '-2 days',                       1)
('Ago:', '2.1 days ago',     datetime.datetime(2015, 6, 18, 22, 39, 8),  datetime.timedelta(-1, 0, 206438),     '-1 days',                       1)
('Ago:', '2.2 days ago',     datetime.datetime(2015, 6, 19, 22, 39, 8),  datetime.timedelta(-2, 0, 207091),     '-2 days',                       1)
('Ago:', '9.8 days ago',     datetime.datetime(2015, 6, 25, 22, 39, 8),  datetime.timedelta(-8, 0, 207924),     '-8 days',                       1)
('Ago:', '1.1 days ago',     datetime.datetime(2015, 6, 18, 22, 39, 8),  datetime.timedelta(-1, 0, 208615),     '-1 days',                       1)
('Ago:', '23.2 days ago',    datetime.datetime(2015, 6, 19, 22, 39, 8),  datetime.timedelta(-2, 0, 209261),     '-2 days',                       1)
('Ago:', '6.1 days ago',     datetime.datetime(2015, 6, 18, 22, 39, 8),  datetime.timedelta(-1, 0, 209927),     '-1 days',                       1)
('Ago:', '1.7 months ago',   datetime.datetime(2016, 1, 17, 22, 39, 8),  datetime.timedelta(-214, 0, 210577),   '-214 days',                     1)
('Ago:', '1.1 days ago',     datetime.datetime(2015, 6, 18, 22, 39, 8),  datetime.timedelta(-1, 0, 211228),     '-1 days',                       1)
('Ago:', '17.6 days ago',    datetime.datetime(2015, 6, 23, 22, 39, 8),  datetime.timedelta(-6, 0, 211882),     '-6 days',                       1)
('Ago:', '1.8 days ago',     datetime.datetime(2015, 6, 25, 22, 39, 8),  datetime.timedelta(-8, 0, 212524),     '-8 days',                       1)
('Ago:', '24.5 days ago',    datetime.datetime(2015, 6, 22, 22, 39, 8),  datetime.timedelta(-5, 0, 213210),     '-5 days',                       1)
('Ago:', '12.6 days ago',    datetime.datetime(2015, 6, 23, 22, 39, 8),  datetime.timedelta(-6, 0, 213938),     '-6 days',                       1)
('Ago:', '13.3 hours ago',   datetime.datetime(2015, 6, 18, 1, 39, 8),   datetime.timedelta(-1, 75600, 214606), '-1 days, 21 hours',             2)
('Ago:', '11.3 days ago',    datetime.datetime(2015, 6, 20, 22, 39, 8),  datetime.timedelta(-3, 0, 215255),     '-3 days',                       1)
('Ago:', '2.7 months ago',   datetime.datetime(2016, 1, 17, 22, 39, 8),  datetime.timedelta(-214, 0, 215926),   '-214 days',                     1)
('Ago:', '5.1 days ago',     datetime.datetime(2015, 6, 18, 22, 39, 8),  datetime.timedelta(-1, 0, 216627),     '-1 days',                       1)
('Ago:', '4.5 months ago',   datetime.datetime(2015, 11, 17, 22, 39, 8), datetime.timedelta(-153, 0, 217278),   '-153 days',                     1)
('Ago:', '9.4 minutes ago',  datetime.datetime(2015, 6, 17, 22, 43, 8),  datetime.timedelta(-1, 86160, 858860), '-1 days, 23 hours, 56 minutes', 2)
('Ago:', '30.2 minutes ago', datetime.datetime(2015, 6, 17, 22, 41, 8),  datetime.timedelta(-1, 86280, 859567), '-1 days, 23 hours, 58 minutes', 2)
('Ago:', '37.3 minutes ago', datetime.datetime(2015, 6, 17, 22, 42, 8),  datetime.timedelta(-1, 86220, 860243), '-1 days, 23 hours, 57 minutes', 2)
('Ago:', '58.4 minutes ago', datetime.datetime(2015, 6, 17, 22, 43, 8),  datetime.timedelta(-1, 86160, 860912), '-1 days, 23 hours, 56 minutes', 2)
('Ago:', '1.1 hours ago',    datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 861561), '-1 days, 23 hours',             2)
('Ago:', '1.2 hours ago',    datetime.datetime(2015, 6, 18, 0, 39, 8),   datetime.timedelta(-1, 79200, 862214), '-1 days, 22 hours',             2)
('Ago:', '1.2 hours ago',    datetime.datetime(2015, 6, 18, 0, 39, 8),   datetime.timedelta(-1, 79200, 863003), '-1 days, 22 hours',             2)
('Ago:', '2.4 hours ago',    datetime.datetime(2015, 6, 18, 2, 39, 8),   datetime.timedelta(-1, 72000, 863662), '-1 days, 20 hours',             2)
('Ago:', '2.7 hours ago',    datetime.datetime(2015, 6, 18, 5, 39, 8),   datetime.timedelta(-1, 61200, 864327), '-1 days, 17 hours',             2)
('Ago:', '4.8 hours ago',    datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 864985), '-1 days, 16 hours',             2)
('Ago:', '5.1 hours ago',    datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 865636), '-1 days, 23 hours',             2)
('Ago:', '6 hours ago',      datetime.datetime(2015, 6, 17, 16, 39, 8),  datetime.timedelta(0, 21600, 866243),  '6 hours',                       2)
('Ago:', '6.6 hours ago',    datetime.datetime(2015, 6, 18, 4, 39, 8),   datetime.timedelta(-1, 64800, 866888), '-1 days, 18 hours',             2)
('Ago:', '7.4 hours ago',    datetime.datetime(2015, 6, 18, 2, 39, 8),   datetime.timedelta(-1, 72000, 867537), '-1 days, 20 hours',             2)
('Ago:', '7.6 hours ago',    datetime.datetime(2015, 6, 18, 4, 39, 8),   datetime.timedelta(-1, 64800, 868185), '-1 days, 18 hours',             2)
('Ago:', '7.7 hours ago',    datetime.datetime(2015, 6, 18, 5, 39, 8),   datetime.timedelta(-1, 61200, 868837), '-1 days, 17 hours',             2)
('Ago:', '8.3 hours ago',    datetime.datetime(2015, 6, 18, 1, 39, 8),   datetime.timedelta(-1, 75600, 869659), '-1 days, 21 hours',             2)
('Ago:', '8.7 hours ago',    datetime.datetime(2015, 6, 18, 5, 39, 8),   datetime.timedelta(-1, 61200, 870356), '-1 days, 17 hours',             2)
('Ago:', '9.7 hours ago',    datetime.datetime(2015, 6, 18, 5, 39, 8),   datetime.timedelta(-1, 61200, 871014), '-1 days, 17 hours',             2)
('Ago:', '10.9 hours ago',   datetime.datetime(2015, 6, 18, 7, 39, 8),   datetime.timedelta(-1, 54000, 871677), '-1 days, 15 hours',             2)
('Ago:', '11.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 872350), '-1 days, 23 hours',             2)
('Ago:', '11.4 hours ago',   datetime.datetime(2015, 6, 18, 2, 39, 8),   datetime.timedelta(-1, 72000, 873005), '-1 days, 20 hours',             2)
('Ago:', '11.9 hours ago',   datetime.datetime(2015, 6, 18, 7, 39, 8),   datetime.timedelta(-1, 54000, 873659), '-1 days, 15 hours',             2)
('Ago:', '12.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 874313), '-1 days, 16 hours',             2)
('Ago:', '13 hours ago',     datetime.datetime(2015, 6, 17, 9, 39, 8),   datetime.timedelta(0, 46800, 874918),  '13 hours',                      2)
('Ago:', '13.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 875577), '-1 days, 23 hours',             2)
('Ago:', '13.3 hours ago',   datetime.datetime(2015, 6, 18, 1, 39, 8),   datetime.timedelta(-1, 75600, 876230), '-1 days, 21 hours',             2)
('Ago:', '14.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 877112), '-1 days, 16 hours',             2)
('Ago:', '15.2 hours ago',   datetime.datetime(2015, 6, 18, 0, 39, 8),   datetime.timedelta(-1, 79200, 877782), '-1 days, 22 hours',             2)
('Ago:', '15.9 hours ago',   datetime.datetime(2015, 6, 18, 7, 39, 8),   datetime.timedelta(-1, 54000, 878457), '-1 days, 15 hours',             2)
('Ago:', '16 hours ago',     datetime.datetime(2015, 6, 17, 6, 39, 8),   datetime.timedelta(0, 57600, 879065),  '16 hours',                      2)
('Ago:', '16.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 879727), '-1 days, 16 hours',             2)
('Ago:', '17.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 880377), '-1 days, 23 hours',             2)
('Ago:', '17.3 hours ago',   datetime.datetime(2015, 6, 18, 1, 39, 8),   datetime.timedelta(-1, 75600, 881031), '-1 days, 21 hours',             2)
('Ago:', '18.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 8),  datetime.timedelta(-1, 82800, 881682), '-1 days, 23 hours',             2)
('Ago:', '18.3 hours ago',   datetime.datetime(2015, 6, 18, 1, 39, 8),   datetime.timedelta(-1, 75600, 882506), '-1 days, 21 hours',             2)
('Ago:', '18.7 hours ago',   datetime.datetime(2015, 6, 18, 5, 39, 8),   datetime.timedelta(-1, 61200, 883510), '-1 days, 17 hours',             2)
('Ago:', '18.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 884192), '-1 days, 16 hours',             2)
('Ago:', '18.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 8),   datetime.timedelta(-1, 57600, 884889), '-1 days, 16 hours',             2)
('Ago:', '8.3 hours ago',    datetime.datetime(2015, 6, 18, 1, 39, 9),   datetime.timedelta(-1, 75600, 432885), '-1 days, 21 hours',             2)
('Ago:', '18.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 9),   datetime.timedelta(-1, 57600, 433580), '-1 days, 16 hours',             2)
('Ago:', '11.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 9),  datetime.timedelta(-1, 82800, 434277), '-1 days, 23 hours',             2)
('Ago:', '1.3 days ago',     datetime.datetime(2015, 6, 20, 22, 39, 9),  datetime.timedelta(-3, 0, 434951),     '-3 days',                       1)
('Ago:', '1.4 days ago',     datetime.datetime(2015, 6, 21, 22, 39, 9),  datetime.timedelta(-4, 0, 435612),     '-4 days',                       1)
('Ago:', '10.9 hours ago',   datetime.datetime(2015, 6, 18, 7, 39, 9),   datetime.timedelta(-1, 54000, 436276), '-1 days, 15 hours',             2)
('Ago:', '22.3 hours ago',   datetime.datetime(2015, 6, 18, 1, 39, 9),   datetime.timedelta(-1, 75600, 436961), '-1 days, 21 hours',             2)
('Ago:', '1.5 days ago',     datetime.datetime(2015, 6, 22, 22, 39, 9),  datetime.timedelta(-5, 0, 437740),     '-5 days',                       1)
('Ago:', '1.4 days ago',     datetime.datetime(2015, 6, 21, 22, 39, 9),  datetime.timedelta(-4, 0, 438396),     '-4 days',                       1)
('Ago:', '6.6 hours ago',    datetime.datetime(2015, 6, 18, 4, 39, 9),   datetime.timedelta(-1, 64800, 439045), '-1 days, 18 hours',             2)
('Ago:', '11.4 hours ago',   datetime.datetime(2015, 6, 18, 2, 39, 9),   datetime.timedelta(-1, 72000, 439711), '-1 days, 20 hours',             2)
('Ago:', '13.1 hours ago',   datetime.datetime(2015, 6, 17, 23, 39, 9),  datetime.timedelta(-1, 82800, 440467), '-1 days, 23 hours',             2)
('Ago:', '12.8 hours ago',   datetime.datetime(2015, 6, 18, 6, 39, 9),   datetime.timedelta(-1, 57600, 441127), '-1 days, 16 hours',             2)
('Ago:', '15.9 hours ago',   datetime.datetime(2015, 6, 18, 7, 39, 9),   datetime.timedelta(-1, 54000, 441800), '-1 days, 15 hours',             2)

Where each line is a tuple: ("Ago:", {instr}, {parsed_time}, {datetime.datetime.now()-parsed_time}, {Pretty-Printed datetime.datetime.now()-parsed_time}, {result_type}).

I'll probably have a poke at looking about inside the library this weekend, but frankly I have NO idea how /any/ of the dates are somehow winding up in the future, when the input string clearly has "ago" in it.

For that matter, the fractional months ago ('2.7 months ago') is somehow being parsed as 1 day, 23 hours and 53 minutes in the future.

@bear
Copy link
Owner

bear commented Jun 18, 2015

I'm surprised it worked at all to be honest :)

The issue is that the bit of code that converts the "2.7 months" into a quantity (2.7) and unit (months) is a regex that probably doesn't understand anything other than integers.

The evalModifier code will first pull off "ago" and then send "2.7 months" to be evaluated.

But 2.7 is caught by the RE_NUMBER regex and mangled because it assumes an integer. So yea, the assumption at line 978 or so of init.py that the quanity will only ever be an integer probably needs to be fixed.

The RE_NUMBER regex assumes word boundaries:

(\b(?:{numbers})\b|\d+)

that's the \b part of the above - that would have to change to also include the locale's decimal character (. or ,) but that could also brings in other edge cases.

@idpaterson
Copy link
Collaborator

Fortunately the word boundaries only affect written numbers like "one" and "two" so just the righthand side would need to be tweaked. Similar to what is done for seconds (\d\d(?:[.,]\d+)?, you could probably get away with (untested)

    (
        \b(?:{numbers})\b
        |
        \d+(?:[.,]\d+)?
        |
        [.,]\d+
    )

with the first still matching numbers as words, the second matching numbers with optional decimals like 4 0.3 and 4.34, and the third matching decimals with no preceding number like .25. Not sure what might be needed to handle the match but that should take care of the regex. Locale-aware separators would be nice in case people decide to use thousands separators like 1.000,50 but probably not a common problem.

@fake-name
Copy link
Contributor Author

I'm surprised it worked at all to be honest :)

I didn't expect it to work, but I'm surprised even the sign was so wrong.

If '5.1 hours ago'' flattens to {"5."}, {"1 hours ago"}, I'd expect to get a delta of 1 hours. Somehow, it's producing a delta of datetime.timedelta(-1, 82800, 865636).

I have no idea where the days component is coming from at all.

@philiptzou
Copy link
Collaborator

I think I have fixed this problem in my latest pull-request (#111).

But to be honest the delta of datetime.timedelta(-1, 82800) is exactly what you get when you inputed datetime.timedelta(hours=-1). It basically means minus 1 day and then plus 82800 seconds, and it is -1 hour (or -3600 seconds). That is how datetime.timedelta works.

@fake-name
Copy link
Contributor Author

I'm going to close this because it sounds like it's been fixed.

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