One thing that is bothering me is that when you ask for `maya.when('tomorrow')`, or give only a date, you get back a timestamp with millisecond precision, representing 00:00 of that day. I understand this simplifies the implementation, but shouldn't `tomorrow` be a range, from 00:00 to 23:59?
Treating imprecise dates as ranges would allow for stuff like
# Did this event happen "yesterday" according to the US/Eastern time zone?
timestamp in maya.when('yesterday', to_timezone='US/Eastern')
# Get me all events that happened last Friday (UTC)
[event for event in events if event.time in maya.when('2016-12-16')]
Maybe I'm being naive, and there's a reason why this won't work, but this seems the way most humans deal with time.PS: it failed to install on Windows, so I opened an issue at https://github.com/kennethreitz/maya/issues/10
Just off the top of my head, ISO8601 is already in the right order, just letting users "leave off" anything that's not important and then having a library interpret those "partial dates" as a range internally so it's easy to check identity or if one date is "in" another.
[1]: https://docs.python.org/3/library/datetime.html#date-objects [2]: https://docs.python.org/3/library/datetime.html#time-objects
For events in the last hour, the following expression should evaluate to True:
(datetime.now() - timedelta(hours=1)) <= event_datetime
Checking whether event_datetime is within May of the current year is a bit more involved, but you may not need to convert everything to a date (would have to check in an interpreter): date.today().replace(month=5, day=1) <= event_datetime.date() <= date.today().replace(month=5, day=31)
The point is that the language already has good support for sensible time operations, but it's parsing is weak. And there's Arrow for that[2]![1]: https://docs.python.org/3/library/datetime.html#timedelta-ob... [2]: http://arrow.readthedocs.io/en/latest/
https://github.com/kennethreitz/maya/issues/10#issuecomment-...
1. Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z".
2. Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
3. Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
4. Duration only, such as "P1Y2M10DT2H30M", with additional context information
If that's too verbose, and we only need to represent integer intervals, one could store a truncated representation, where the omitted parts mean "any" (like * in cron). For example, `2016-11` could mean the entire month of November.
But those are just ideas for the representation. The important feature is that not all objects are precise points in time, but may be a block/interval/range.
https://docs.python.org/3.5/reference/datamodel.html#object....
Exactly this. Makes it so much easier especially when you need to work with other components that only understand the stdlib datetime.
I have a bit of doubt about a time library that defaults to UTC but uses human phrases like "tomorrow". Who thinks about UTC tomorrow?
>>> tomorrow = maya.when('tomorrow')
<MayaDT epoch=1481919067.23>
>>> tomorrow.slang_date()
'tomorrow'
>>> tomorrow.slang_time()
'23 hours from now'
Huh? Given that we never specified timezone, I would expect 24h (unless DST change happens).This is actually my biggest gripe with date+time libs. Imho API should always be explicit in what its default TZ is.
EDIT: still, appreciate what you are doing. Requests rule, and there are many other areas (including datetime) that need libs with better APIs. Thumbs up!
Having spent a lot of time dealing with timezones, I have a strong feeling that naive should be the default unless explicitly given a timezone, and your code should blow up when it is given them (or possibly gracefully fail).
Admittedly I haven't had a chance to play with maya as I haven't been able to get it to install.
I don't like the default of UTC either though. Explicit is better than implicit, so make people always specify a timezone instead which avoids all form of ambiguity.
1) Whenever dealing with users, use local tz.
2) Always save and manipulate in utc.Example: in early 2011, Samoa announced that on December 29th at midnight local they would switch their timezone offset from -11 to +13. Before that announcement (or at least before your timezone database has been updated), store as UTC a meeting in local Samoa time, and the user will miss their meeting by a day.
Of course storing a meeting on December 30th local would also have been fraught as there is no December 30th 2011 in Pacific/Apia but that is a common occurrence historically due to the unsynchronised julian/gregorian switches e.g. none of the dates between February 16th and February 28th 1923 (included) exist in Greece, and the US doesn't have a 9/11 in 1752.
You want a meeting at 1100 local time.
Meeting is stored for that time in UTC, on the correct date (before Samoa changed, we all understood what date you meant)
Samoa changes the rules.
The calendar doesn't change.
This stuff is mind-bending, so I could be missing something, but a more detailed walk through your mental debugger might clarify.
School times, times for religious services, lots of times are relative to local time, which itself is subject to change against UTC (sometimes predictable, sometimes not, the government gets to update the timezone and the DST scheme arbitrarily). So only ever storing UTC and using local time purely for display isn't a sustainable one-size-fits-all option.
On a specific day.
> The calendar doesn't change.
Of course it does, you've stored a UTC datetime, the timezone database is updated, the UTC datetime now maps to the wrong time (and because Samoa changed its offset by 24h it actually maps to the wrong day entirely every single time).
> This stuff is mind-bending, so I could be missing something, but a more detailed walk through your mental debugger might clarify.
* while Pacific/Apia is UTC-11, you create a meeting for January 5th at 11 local
* this is stored as 2012-01-05T22:00:00+0000
* tzinfo gets updated with Pacific/Apia at UTC+13
* your calendar now tells you your meeting is on January 6th at 11 local
* you're a day late
1. How is it not a good argument? It's an actual historical fact which is not yet 5 years old rather than some intellectual exercise, you can hardly go better than "this stuff happened not 5 years ago".
2. The only "extreme" part here is the magnitude of the drift, it's a clear example and demonstration of the issue.
> How often is Samoa going to 'jump sides'?
How is that relevant to the problem specifically and clearly existing? Does it matter if your calendar is 24h off or 6h off? Your system is FUBAR either way.
> or another small dateline-neighbour country
The dateline isn't even relevant, are you somehow trying to win a prize in missing the point?
Any solid datetime implementation better consider that time is not, in fact, immutable - even UTC can and will shift here and there.
Two very common examples: For a recurring scheduled event, "Same time tomorrow" may require adding 24, 23 or 25 hours to the UTC time, depending on the TZ.
The question "how many full days have elapsed between two timestamps?" may need to return 1 for 23.5 hour distance or 0 for 24.5 hour distance depending on the TZ.
I'd be so happy if we all could agree that a day always has 24 hours, but that doesn't seem likely any time soon.
And even if you only need timestamps, "Whenever dealing with users, use local tz" isn't exactly a magic wand either.
Also, "unix timestamp" isn't exactly one data type. Sometimes you need greater precision than one second.
Arrow and pendulum (my current favorite) have a very decent API. The later one is especially well tested for the numerous corner cases of date handling, which I doubt Kenneth got right on first try.
For request, a full rewrite made sense because urllib sucked so much and we had no good alternatives. But for date time, alternative exists and they are good. Do not split the open source effort, join forces!
... nice to see it mentioned.
This library is not going to divert much (any?) effort that could have improved other date-time libraries.
Trying to funnel someone with an itch into a project is a bit like making someone on meth fill out paperwork: kills the buzz and works out poorly. And if just one human understands the nightmare of date handling a bit better, it is a positive thing for the humans.
A lot of Python is really solved. We don't argue about using requests (a not-coincidental example). If you're using Python, and you need to deal with http, you use requests. Everyone knows this.
There are basically 3 platforms for web frameworks. Flask, Pyramid, and Django. Maybe we're a little more dissolute than C# or Ruby folks, but that's pretty impressive considering how much we Python people like to roll our own.
The fact that there is real disagreement about this among ourselves about this particular issue says to me that this is more about the difficulty of the problem than it is anything else.
Python's history of time types was terrible, as well. Particularly the era when we just used tuples of length 8 or 9 that weren't timezone aware.
Boy do we disagree. Between the mutable API, the fuzzy parsing, the lack of tz support and the yet-another-reinvention of date/time formatting DSL[0] moment was one of those things I'd rather have not had in my life.
[0] which, to add insult to injury, is really similar to but not quite compatible with the LDML's
When did you last use it? I love working with momentjs.
In the case of Moment, a little promotion went a very long way, and the community was eager to take what's given so they wouldn't have to think too much about datetime, and focus on debating how to arrange and design their applications instead.
Moment's design is a testament to "let's not think too hard about datetime", and I mean that both as a compliment and a critique.
[1] http://stackoverflow.com/users/272034/timrwood?tab=summary [2] https://news.ycombinator.com/item?id=12175369
It's hard because the expected output is not fully agreed about.
We don't know what we want, and we don't know how to get it. That goes for pretty much every programming language I know of.
It's a hard problem because we suck, as people.
What do you mean by "lack of leap seconds"? A non-unix-timestamp-based time library?
As my co-worker once put it, "time is a four letter word."
At the very least, there is a need for showing the occasional 61st second of a minute, and adding a time and timedelta where leap seconds is taken into account.
I still don't understand what you mean.
> You could use time_t underneath, if that's what you really wanted to do.
Well you've got unix timestamp (UTC) which "skips" leap seconds but as a result is very easy to map to "human time", or you've got TAI[0] which includes leap seconds but doesn't allow dates in the future since you don't know where and when new leap seconds will be added long in advance, and now you need regular updates/permanent connectivity so you can remap TAI onto human time. And you need an NTP replacement, though I guess having a GPS unit in everything would do.
[0] and GPS time which is just TAI + 19s
I have packets read from an instrument which are timestamped in seconds since 1980-01-06 (GPS Epoch). To compute each packet's proper datetime representation in TAI, I need add those seconds to 1980-01-06, and subtract the amount of leap seconds that occurred. The subtracting of leap seconds could be done automatically by a library. When a leap second is added, it will occur on the 61st second of the minute (second = :60). These times aren't supported by the datetime class.
I don't find it that puzzling. It's meant to help deal with timezones, efficiency isn't necessarily part of that equation.
And if I were to write a simple CLI or web app I would avoid anything that would pull in a dependency like numpy or require a C-library plus its bindings.
If you need to manipulate a huge amount of date times and this is something your app is constantly doing then sure, optimise for that.
ps: note that the Maya in Alias Wavefront (now Autodesk but nope) Maya is not that Maya, it's Sanskrit for illusion, which is what Maya was all about considering what it did to your dollars.
So this library would need to be installed as a separate name to be usable there.
Like "JodaTime" and "Golang"
http://stackoverflow.com/questions/13703720/converting-betwe...
I run batch and real time financial feeds into Python from multiple sources, inevitably from different programming language paradigms, and with different time/date/timezone conventions, and then I allow user interaction with it in their own lingo for dates and times, and my experience is that it is indeed hell consolidating all this into a common tongue. The "slang time" idea suggest to me that this library understands the need for flexibility and malleability in this very disjointed and often frustrating part of the Python ecosystem.
One thing is that some libraries bring their own not-really-different-just-different-enough-to-break-things date/time types to the table. That can be worked out over time, not that big of a problem, just inconvenient.
The other is that I've grown to think that the abstraction commonly used, seeing date/time as something like (year, month, day, hour, minute, second, subsecond, timezone) is in itself not really suitable, because the derived interfaces (like accessing and modifying days, months etc. separately) mean that most code working with these will inevitably be bug-ridden.
The root cause for this is imho that date and time are extremely complicated and it's usually unclear what the correct behaviour would be in any of the many, many edge cases.
Some examples:
1.) One week after `this` date. Does this mean, add seven days? What about leap days? Should the result be the same weekday, just in the next week instead?
2.) Start and end time of a process should be recorded. The process starts and finished. All good. The duration of the process is -1 hour and 5 minutes. Should instead a start timestamp be recorded and a monotonic duration? Or perhaps we should rather note the begin of a monotonic period and record an end timestamp, deriving the start timestamp? Which is correct?
3.) One ^W two words: recurrence rules
4.) DST
5.) Combining 3.) and 4.)!
dom's rule of thumb: if code does addition or substraction with some measure of time, it's probably wrong.
Not only do you need to specify the precise dates of recurrences, but you also need to map time intervals to fractions of a year (e.g. if I owe you $1mm a year, is 'one month' equal to 1/12 of a year, or do I count the actual numer of days in between? What about leap days, etc.). Getting it wrong even slightly means you get all the cashflows wrong.
Welcome to the joy of 'day count conventions' [1] and 'date rolling' [2].
[1] https://en.wikipedia.org/wiki/Day_count_convention [2] https://en.wikipedia.org/wiki/Date_rolling
Funny thing is these have all probably be reimplemented from scratch at every single bank.
Anything with date/time calculations is always a pain, probably doesn't really have much to do with the library/language itself, but that the abstraction level that's used (and typically used in other libraries) means that the complexities of calendar and time systems are sprinkled all over application code.
I do have to notice here that always using UTC is not always the right thing to do. For example, evaluating rrules in UTC is rather error-prone (DST).
Here, despite storing everything in UTC and attempting to apply DST as late as possible as if it were a "display issue" — normally the right thing — here results in the wrong outcome.
The user didn't want a precise coordinated time (DST is not a factor for storage/recall); the user wanted a fuzzy reference to local time (DST is a factor for evaluation).
This API avoids that problem entirely.
The problem with TZ is TZ in its core definition.
1) they always change: if you have not updated your TZ since 3 months they are probably inaccurate
2) your TZ definition maybe accurate but they may not have been applied for real in the concerned zone
3) you can have different local time for the same longitude
4) you can have different days on the same longitude ...
5) TZ are not versioned, if the TZ changed between 2 records you may have made, you have inaccurate intervals stored. We do NOT have an API to take TZ change over time in consideration.
6) CEST/DST is breaking the axiom that time is a growing monotonic function
References 8.5.3 TZ in postgresql man https://www.postgresql.org/docs/9.2/static/datatype-datetime...
Computerphile what's wrong with timezones https://www.youtube.com/watch?v=-5wpm-gesOY
Also:
>>> dt = maya.now()
>>> dt.datetime()
datetime.datetime(2016, 12, 18, 19, 24, 50, 212663, tzinfo=<UTC>)
>>> dt.datetime('Europe/Budapest')
datetime.datetime(2016, 12, 18, 20, 24, 50, 212663, tzinfo=<UTC>)
I would not use it...https://github.com/kennethreitz/maya/blob/d57a78c6bc6b5295f7...
And i18n support in humanize is a bit lacking, as it only translates to French, Korean and Russian. Given that most of the translations needed to render human dates can be found in the CLDR database, maintaining their own looks like a bit of a wasted effort.
Reference:
You still need to parse the CLDR (the formats are pretty wonky) and provide APIs for its functions. Of course that's what Babel (http://babel.pocoo.org) does.
# Automatically parse datetime strings and generate naive datetimes.
>>> scraped = '2016-12-16 18:23:45.423992+00:00'
>>> maya.parse(scraped).datetime(to_timezone='US/Eastern', naive=True)
datetime.datetime(2016, 12, 16, 13, 23, 45, 423992)
I'm happy not to have to write formatting arguments to strptime() anymore. Do the other datetime libraries have similar parsing functions?A lot of effort has been put into getting DST right so I hope it will be what you are looking for.
Disclaimer: I am the author of Pendulum :-)
It's not exactly a time library from scratch, if you look at the amount of existing libraries it imports and the small code size (which is a good thing!). Why should he not offer a differently flavored API on top of existing parts? My impression is that most of these libs start with a strong idea what the API should be like, so large changes there are unlikely to happen.
My only real question:
> rand_day = maya.when('2011-02-07', timezone='US/Eastern')
This returning an object representing a DateTime on the 6th (in UTC time) strikes me as perhaps "not for humans."
If I just see that line casually, I think I expect to get a Date and for it to be the 7th.
It looks like, in order to get this (arguably expected) object, I need to take the resulting MayaDT epoch and run its `datetime` method, passing naive=True?
And I also see that _tz can only ever be pytz.timezone('UTC') - is this the result of some belief that timezones are illusions or something? :-)
For a while, I have kinda thought that timezones foment a confused mental model of time and teamwork. I prefer to think in terms of the astronomy - it's not actually a different time anywhere else, it's just that the sun is at a different position relative to the rest of the earth (and thus, ones faraway teammates and loved ones).
Anyway, thanks for yet another set of interesting ideas. Hope you are well.
Same with dates. Instead of trying to deal with and store all possible representations (format, timezones, etc), you convert all dates to a single representation (hence UTC). When you need to output the date, encode it into whatever format and timezone you need.
It's much cleaner this way, because there's less chance you'll mix up representations. So the "for humans" part is more "for developers, which are also humans but like to pretend they aren't and never make mistakes".
This is what happened with Python web frameworks, a scene which was heavily fragmented before Django and Flask basically solidified the two main communities of users ("I need everything and the kitchen sink" / "I need the bare minimum to get going"). The same happened with urllib2/urllib3/httplib/ etc before Requests appeared.
Nobody seems to have pulled this trick for datetime libraries yet, so here's a new contestant.
<MayaDT epoch=1481850660.9>
not the most human readable is it?I guess it would help with like, "X happened before Y" situations, but I don't think I'd trust my eyes for that!
I think I would of preferred the "this happened in X month" case instead, I find it easier to trust my eyes for that, instead of trying to look for differing digits!
- - -
UTC default is a godsend though
>>> import ago
>>> import dateutil
>>> ago.human(dateutil.parser.parse('Fri Jun 29 19:25:55 2012'))
'4 years, 172 days ago'
The current implementation is 66 lines of code including docstrings:
* https://bitbucket.org/russellballestrini/ago/src/tip/ago.py(...) take my very early comments with a grain of salt -- they refer to the progress as of this commit [1].
I love Requests -- its API design is fantastic, and manages to distill down most of a complex problem domain to a clean, dare-I-say, elegant API. So I can eagerly anticipate this design applied to datetimes. But the progress being shown so far is definitely not it.
>>> tomorrow = maya.when('tomorrow')
<MayaDT epoch=1481919067.23>
Why is "tomorrow" a precise-to-centisecond, infinitesimally small point on a giant cosmic timeline? I'm reasonably sure it's an abstract concept that describes the calendar day that starts after the current calendar day ends.
At least, Pendulum normalizes tomorrow() and its ilk to represent midnight on the given day [2], while Delorean's natural language methods [3] like next_day() advance the day while leaving the time-of-day unchanged, but the method name makes this fairly clear.
Even Arrow, which is heavily inspired by Moment.js to the point of conflating every single datetime idea into the same class, opts for mutators that are still more clear [4].
> Timezones fit in here somewhere...
Yeah, this needs more work.
Java 8 / Threeten, and its predecessor Joda-Time took the approach of clearly modeling each and every concept that humans actually use to describe time; even if you take issue with their API, the designers have clearly done their homework, and their data model is solid.
Formats like TOML wrestled with datetimes and realized [5] that datetimes aren't just woefully underspecified in most other specs and APIs, but that they're frequently mis-modeled, so they adopted large portions of Threeten's data model. Cases like this should merit strong consideration from anyone trying to propose new datetime APIs today.
[1] https://github.com/kennethreitz/maya/commit/ecd0166ba215c1a5.... [2] https://pendulum.eustace.io/docs/#instantiation [3] http://delorean.readthedocs.io/en/latest/quickstart.html#nat.... [4] http://crsmithdev.com/arrow/#replace-shift [5] https://news.ycombinator.com/item?id=12364805 [6] https://news.ycombinator.com/item?id=13190314#13190657
> Maya never panics, and always carrys a towel.
Nice reference!