[Datetime-SIG] PEP-495 - Strict Invalid Time Checking

Carl Meyer carl at oddbird.net
Tue Aug 25 21:36:46 CEST 2015

Hi Alexander,

On 08/25/2015 08:51 AM, Alexander Belopolsky wrote:
> On Tue, Aug 25, 2015 at 9:30 AM, Stuart Bishop <stuart at stuartbishop.net
> <mailto:stuart at stuartbishop.net>> wrote:
>     As mentioned elsewhere, pytz requires strict checking to remain
>     backwards compatible.
> Can you provide the specific examples where strict checking is
> required?

I mentioned my most recent use case for pytz's strict checking in
another thread, but I did not describe it thoroughly and you may have
missed it. I'll try to do better here, in case it's helpful.

The system is a calendaring application that allows users to request an
appointment in any open slot, where slots are fixed half-hour blocks. A
day is displayed as a column of half-hour slots (48 per day). Some slots
are open and available for the user to click and request an appointment;
some are not. The system is multi-user and timezone-aware; different
users may be in different timezones and must be able to schedule
appointments with one another.

To generate a day's calendar to display to the user, I `combine()` the
day's date and the start/end time for each slot into a naive datetime,
and `pytz.localize()` that naive datetime into the timezone of the user
viewing the calendar. Those start/end datetimes, and information about
the availability status of the slot, are included in a small data
structure for each slot. This data structure is made available to the
client-side (Javascript) code so it can display the slots with
appropriate styling, and make the right appointment-request to the
server if a user clicks an available slot.

(Now, I'm sure there are various ways this design could be critiqued and
explored; I'd personally find that interesting and enlightening, but I
don't think it'd be useful to this thread. For the purposes of the
thread, the question is whether the design is at least reasonable and
functional. It is currently working well in production, and I believe it
is reasonable.)

Given the unlikelihood of someone trying to schedule an appointment at
2am, I didn't care much about the specific choice of user experience for
the DST gaps and folds; my overriding concerns were to a) not crash the
application when displaying a DST-transition day, and b) minimize the
harm done to my codebase (in fragility or added complexity) for the sake
of handling this rare edge case.

Options I considered:

1) Just set the `is_dst` flag to either True or False always. In a fold,
this inevitably results in at least one slot which ends before it
starts. My slot data structure was built to raise an exception in that
scenario. I didn't want to remove this useful sanity check just for this
rare case. Also in a gap, this could result in apparently
hour-and-a-half long slots, which introduced complexity on the display side.

2) Avoid "ends before it starts" by figuring out which value of `is_dst`
meant "later" or "earlier" in each case and always using the earlier
choice for "start" and the later choice for "end." Translating between
`is_dst` and "earlier/later" isn't trivial to do generically, so I'd
have to localize all datetimes twice. This solution also results in
overlapping slots, which (without further changes to the design) could
in some cases result in a single existing appointment being displayed to
the user twice on the calendar.

3) Consider any slot with an invalid or ambiguous start or end time to
be an invalid slot and always display it to the user as "unavailable",
thus preventing any appointment from ever being requested in that slot.
I implemented this by setting `is_dst=None` and catching
`InvalidTimeError`, and it turned out to be a very simple solution to
the problem. The effect is an always-unavailable couple of slots right
around 2am twice a year, which is totally fine, and better than e.g.
double-displaying appointments.

I went through this exact progression of options and settled on the
third one, and was quite happy with its simplicity and lack of cascading
effects on the rest of the code. I'm sure I could have found other
workable options, but it seems to me that at least this indicates that
there are reasonable solutions to some problems where "strict checking"
is useful.

I'm aware that PEP 495 as it stands would let me write a function to do
the strict checking myself (at the cost of localizing all my datetimes
twice, I think). I could probably live with that, though the ugliness of
it might have pushed me in a different direction, where the simplicity
of `is_dst=None` in pytz made option 3 attractive.

In any case, I don't feel strongly enough to argue any more about this
(especially since I trust Stuart to maintain a backwards-compatible pytz
API regardless -- thanks Stuart!); I'm just providing a real-world use
case for strict checking, since you asked for one.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/datetime-sig/attachments/20150825/c36e7488/attachment-0001.sig>

More information about the Datetime-SIG mailing list