
Arrow: Better dates and times for Python - amarsahinovic
http://crsmithdev.com/arrow/
======
jessedhillon
Seems like it's written by someone who prefers Ruby or JavaScript -- where
there exists already a culture using names which are cute first, even if they
are opaque -- over Python. These method naming choices are baffling

Arrow.to() converts to a new timezone? And .replace() applies a relative
offset!? Replace the hour field of this object with -1 should not return an
object having hour=11. Arrow.get() is doing some kind of quadruple duty,
neither of which would I describe as "getting."

And what about that class name? Arrow as the name of a package is fine, but
what do you expect someone to make of <Arrow [...]> \-- what's wrong with
arrow.DateTime?

Great work on making and releasing something, but this API is surprising -- as
in, one would be unable to predict how it works. I will continue using python-
dateutil

~~~
tripzilch
Thank you for putting into words my same initial concerns about this library.

Naming should be much clearer than this, with the intention to make code sort-
of read like English text as much as possible. Arrow is a fine name[0], but
the actual class/object names should be descriptive.

Also, not only the fact that .replace() can apply relative time-offsets, the
quirky way it signifies it, using singular and plural keyword-parameters, it's
a "clever" solution but it's not _at all_ intuitive. When I figured it out, it
was more of a relief that at least .replace() does not _always_ apply relative
time-offsets (which would have been terrible naming).

I wish it was more pythonic, on the whole, agreeing with the way people expect
things to behave in Python modules. It says it's inspired by Requests, which
gets that very much right, so I was getting my hopes up a little.

Arrow.get() is totally named the wrong thing. It does more like a .create(),
maybe I would've put that functionality into the constructor. A method called
.get() should implement something very similar to what most .get* methods do
on standard types and in the standard library. Which is, extract part of an
object that is a container-type (optionally with defaults, etc). Of course
that operation does not make sense on an Arrow object (and please don't try),
so it probably just shouldn't have a .get() method.

[0] actually it's not. I've found myself accidentally muscle-memory-typing
"Array" a couple of times already. I'd probably have renamed this module early
on in the process for that reason alone.

------
rhettg
I think Arrow still leaves too much confusion around how to handle timezones.
That's why I wrote
DMC([https://github.com/rhettg/dmc](https://github.com/rhettg/dmc)).

Not to be confused with Delorean
([https://github.com/myusuf3/delorean](https://github.com/myusuf3/delorean))
which is a lot like Arrow.

~~~
vbit
I did not know about DMC and am very happy to see this as I've often desired a
simple but proper API for time. I always wondered why so many date+time
libraries complicate matters so much.

I strongly agree with the principles you chose - specificaly - all time
storage and math should be done in UTC (the 'unicode' of time). Timezones only
come in when you display or parse times (the 'encodings' of time).

I hope more and more libraries apply this idea. It makes the API a lot simpler
and error retardant.

Good job!

~~~
csirac2

        I strongly agree with the principles you chose - specificaly - all time storage and math should be done in UTC
    

I've had this argument many times before. Throwing away TZ is in fact throwing
away important contextual information.

Having worked on a few data cleaning projects to re-purpose old scientific
datasets, I can tell you that the quality of these data would have suffered
markedly, and many corrections would not have been possible if the TZ/offsets
had been discarded, Eg: "The geolocation for these records cross a TZ boundary
_here_ but the UTC offset doesn't reflect that until _here_ so that explains
the 1hr gap in the record where the user realized her mistake half-way into
the new TZ so we should add an hour to these 860 records"

Yes, it would be better if raw data was recorded in UTC in the first place but
when working with such data the ability to make decisions and inferences from
the TZ is very useful.

~~~
vbit
> Yes, it would be better if raw data was recorded in UTC in the first place
> but when working with such data the ability to make decisions and inferences
> from the TZ is very useful.

I maintain that time and location are orthogonal. If the application needs
both, store both - properly in separate fields. That's better than shoving the
granular location in the form of a timezone in the time field.

~~~
gpvos
No.

It is in fact best to store the _local_ time and the _location_ (i.e., not
"+01:00", but for example "Europe/Amsterdam"). Not UTC with or without time
zone. For example, if you have an appointment in the future, and the time zone
or daylight savings time rules change, this is the only way that the
appointment can still be at the correct moment. And time zone rules _do_
change.

And this way you can still represent absolute UTC time if you wish. With
another representation, you cannot represent human-society time, which is
often what you actually want.

~~~
vbit
Good point. However this is a specific use case requiring future local time,
and as you mention, even TZ aware local time fails here. In this case you
cannot come up with a guaranteed physical time in the future that the event
will take place.

IOW, I think storing local time and location as a rule is not a good idea. The
rule should be to use UTC and store location if needed. Only if that doesn't
work (e.g. '5 minutes after the game is over') come up with some other
representation.

~~~
gpvos
One extra thing (one could call it a weakness) with the local+location scheme
is that you will need to have a way to distinguish the "repeated" hour after
daylight savings time sets the clock backward one hour from the hour before
it.

------
calpaterson
Suggestion: rename arrow.now() to arrow.localnow() to make it even clearer
that it does not generate utc. I've run into this mistake many times with
datetime.now() vs datetime.utcnow()

~~~
timClicks
I'm the other way around, consistency with the standard library (even if that
decision was a mistake) will increase learn-ability.

~~~
coherentpony
From the webpage:

> Python’s standard library and some other low-level modules have near-
> complete date, time and time zone functionality but don’t work very well
> from a usability perspective:

Why be consistent with something that suffers from usability problems?

~~~
gtaylor
It's a shame that Python's time/date stuff is so wonky. I see stuff like
dateutils and arrow and just wish I didn't have to go elsewhere for such core
functionality.

Not that it's a big deal to loop in external dependencies or anything, I just
see newer (and experienced) Python devs trip up on stuff like TZ aware/naive
times. Also, dateutil's relativedelta is so nice compared to the built-in
timedelta!

~~~
coherentpony
> It's a shame that Python's time/date stuff is so wonky. I see stuff like
> dateutils and arrow and just wish I didn't have to go elsewhere for such
> core functionality.

That's on point, and you're not alone. It's a little frustrating when you have
to take a detour to get something done. There's that extra hurdle. That said,
user friendliness, developer friendliness, and maintainability are all about
using the best tools for the job. If arrow is that tool, then so be it.

~~~
gtaylor
> user friendliness, developer friendliness, and maintainability are all about
> using the best tools for the job

Definitely. The most unfortunate part of this is that people who are newer to
the ecosystem won't really know where to go or what they're even looking for,
necessarily.

------
googletron
Arrow is ambiguous when dealing with timezones, a lot of the high
functionality ideas were copied from Delorean. Delorean has many more sensible
default and is clear about educating people about naive vs. localized
datetimes and a lot more sane.

Documentation is very educational too well worth the read.
[http://delorean.readthedocs.org/en/latest/](http://delorean.readthedocs.org/en/latest/)

~~~
stuaxo
It would be good if the manual had all the examples that moment.js has (which
arrow was inspired by).

------
fletchowns
Is there any language that got the date & time library right the first time?
It seems like people always suggest to use a third party library. JavaScript
has Moment.js, Java has Joda-Time, and now Python has Arrow.

~~~
jbarham
Go's time package is nice
([http://golang.org/pkg/time/](http://golang.org/pkg/time/)), although some
people complain about parsing date/time strings using a reference string ("Mon
Jan 2 15:04:05 -0700 MST 2006") vs. PHP or Python style format strings.

~~~
sjy
That's a really cool idea. I'm always commenting my code with reference
strings because format strings are so unreadable.

------
mardix
Y'all may wanna check out python dateutil. [https://labix.org/python-
dateutil](https://labix.org/python-dateutil)

------
yeukhon
Nice. One thought: why do you call it arrow? I know you can call it anything
you want, but one reason requests is so popular because the name totally makes
sense. My little 0.2 is why not rename it to pydate or something more
intuitive.

What's the difference between .utcnow() and .now()? Is one supposed to be an
alias or is .now() default to the time the platform is set to?

I've opened an issue regarding calling convention. What do HNer think?

[https://github.com/crsmithdev/arrow/issues/125](https://github.com/crsmithdev/arrow/issues/125)

~~~
arjie
I imagine it's because of "time flies like an arrow".

~~~
kazagistar
"fruit flies like a bannana"

------
zimbatm
The library looks really nice usability wise.

The arrow.get function tries a bit too hard to be user-friendly ; it lets me
wondering if a value could have an unexpected interpretation.

One use-case that seem to be missing is time deltas. There is a support for
time iteration but I don't see a good way to transform two timestamps into a
time interval.

~~~
cshimmin
> One use-case that seem to be missing is time deltas.

I just fired up ipython and tried:

    
    
        >>> t1 = arrow.now()
        >>> t2 = arrow.now()
        >>> t2-t1
        datetime.timedelta(0, 4, 864716)
    

It appears the difference operator returns a datetime.timedelta object.

------
hyperliner
This is a very interesting problem to tackle for all runtimes.

In my experience, the main issues are: [1] How do we store the date / time
fields in the database tier [2] How do we display those fields to users in
multiple time zones and cultures and gather validated input from those
cultures [3] How do we do date / time math in a simple manner

Issue [1] has implications for manipulating data directly (i.e. querying or
reporting). Should the data be stored in UST or in some "local timeframe"?

Issue [2] always seems to trip some developers: how do we render a time or
date in a way that makes sense in a global world (i.e. "8/5/2014" vs.
"5/8/2014" vs. "5-8-2014" or 3:26 PM vs 3:26 etc).

Issue [3] is always a pain too, based on whatever decisions were made for [1]
and [2].

Test of awesomeness: if your code works in Hebrew or Thai cultures!

------
batbomb
It would be cool if python could support date/timestamp literals.

I've implemented something similar in a query language I wrote, where dates
are represented similar to ISO 8601 format:

    
    
         d'2014-12-01'
         t'2014-12-01 12:52'
         t'2014-12-01 12:52 PST'
         t'2014-12-01T12:52:20.0820Z'
         t'2014-12-01T12:52:20.0820+08.00'

~~~
wtbob
Of you just go with pure ISO8601 then you won't need to differentiate between
dates, times, durations and intervals; they can each be specified
unambiguously. Up to you, of course; it might not make sense in your use-case.

~~~
batbomb
That would be part of the plan.

My use case is the iPython astrophysicist use case, where users tend to be
very interactive with their dates and times. In fact, that's why I made the
query language, because it was necessary for dataset discovery (i.e. return
datasets between this time and that time). Of course, we often use a variety
of types of times, from Julian day to Mission Event Time (where an arbitrary
epoch is chosen that's usually close to some significant event, usually full
funding, mission launch, first light, etc..)

Of course we could always just use function calls, but the ability to have
literals is really nice.

Maybe I'm just a dreamer, but a similar extension to javascript and JSON would
be really nice as well, given that you need a schema to understand the proper
parsing of dates, otherwise you end up with a string, number, additional type
field, or a fallback to Hungarian notation.

------
dirtyaura
Looks promising! Although author said that moment.js was an inspiration, I'm
glad that he didn't implement .replace functionality as mutation like moment
does. It was a constant source of errors.

~~~
crsmithdev
Fun fact, in the oldest, original version, arrow objects _were_ mutable, and
this was ripped out in 0.2 for exactly that reason.

------
iamwithnail
Ah, this is lovely. If there's one routine thing I trip up over again and
again, it's times, every time. Big fan, nice work. Will definitely look at
using this next project.

~~~
kylelibra
It's so easy to overlook something and not realize it is causing issues for
users for a very long time.

------
tiramisou
Looks nice. I noticed the ceil function has an offset of one microsecond, this
way it is not consistent with e.g. ceil in numpy. What was the reason for
this?

------
e1ven
Very nice! This replaces a lot of code I'm manually implementing now, to do
things like humanize() and floor()/ceil()

~~~
masklinn
> This replaces a lot of code I'm manually implementing now, to do things like
> humanize()

Why would you do it by hand anyway when Babel already does it?
[http://babel.pocoo.org/docs/api/dates/#babel.dates.format_ti...](http://babel.pocoo.org/docs/api/dates/#babel.dates.format_timedelta)

------
dangayle
I've used this in the past. Good, well-written library. The stdlib
datetime/time modules are such a PITA sometimes.

------
Tinned_Tuna
There are too many ways to do something, best implement the One True Way.

There's an obligatory xkcd somewhere on this...

~~~
alagappanr
[http://xkcd.com/927/](http://xkcd.com/927/) :)

~~~
mVChr
Funny and true, but this is a separate case. We're not talking about
implementing a new standard, we're talking about easing development on your
next project.

------
erikb
This might be what I am looking for since I looked at the datetime docs the
first time years ago.

------
masklinn
re. parsing and formatting tokens: because what we needed was yet another
date/time pattern format which looks like LDML's but behaves completely
differently.

------
noise
Very nice, thank you, definitely using this in new code.

------
pekk
It would be better to stop reinventing this wheel.

~~~
njharman
We can, but only after someone actually produces a round wheel, with no gaps,
sufficient spokes and a hole for an axle. I've yet to see such a beast.

