Iāve been thinking about this a lot on a long plane flight so here goes:
Calendar module responsibilities
This part is not about formatting but separation and encapsulation of concerns. But if there is agreement with this, and my proposal about augmenting the format flags, then it greatly simplifies the contract between strftime
and a calendar.
I think a calendar should encapsulate everything relevant to calculating days, weeks, months, years etc. That is, any configuration should be baked into the module that implements the Calendar
behaviour.
This seems obvious when we talk about the iSO
calendar versus the Balinese
, or Ancient Egyptian
or the Islamic
calendars. But its less obvious if we consider the myriad of calendars derived from the Gregorian.
For example, in the US, a company may have a fiscal calendar that starts on an arbitrary day of the year. All months, quarters, weeks and days are calculated from this date. Cisco Systems has a financial year calendar that ends on the last Saturday of July. There are a whole class of calendars called 445 calendars and their cousins the 454
and 544
which are common in retail and are designed to make it better for comparing this year to prior year periods.
Although I started thinking this would be best done as configuration passed around in each structurally compatible Date
, Time
or DateTime
struct or map, the variations are too many. I now feel it is better that this detail be backed into a module that implements the Calendar
behaviour. This doesnāt make implementing a calendar any more complex. It does potentially have the side effect of having to generate calendar modules at runtime for some use cases.
Calendar Behaviour
If you accept that a Calendar
module encapsulates all the required configuration and implementation required for the behaviour
then implementing strftime
comes back to mostly calling the behaviour functions. And week_of_year
should definitely stay - in all of those business calendars is used a lot and the notion of a week is, I think we would all agree, a very common case.
If you got this far then the missing pieces of the Calendar
behaviour
that would be required to support strftime
would be day_name
, month_name
(short and long) and the am/pm
functionality described by @josevalim above. Since these will need to be implemented anyway it feels as if they should be part of the Calendar
behaviour.
Formatting
Formatting for strftime
should not be the calendars concern (in my opinion). Thats a representational issue and other than names for days and months, formatting should be the strftime
formats responsibility. In examining the Ruby and Python implementations I see that the Python version is quite sparse but the Ruby version includes an approach that I think is pragmatic.
A Proposal
@spec strftime(date_or_time_or_datetime, format :: String.t(), options :: Map.t()) ::
String.t | {:error, String.t}
format
is a format string as described in the original proposal however it includes an optional width
that is used to determine padding. This is similar to how printf
and friends in C
define padding.
Format encoding: %<flags><width><conversion>
.
Flags (based upon the Ruby implementation)
Flag |
Description |
- |
donāt pad a numerical output |
_ |
use spaces for padding |
0 |
use zeros for padding |
^ |
upcase the result string |
# |
change case |
: |
use colons for %z |
Using this approach allows a calendar implementor to use the %x
directive to return a format string that uncludes the required padding. That is, %x
is really just an interpolation of a format defined in the calendar. And that format can include the appropriate padding. Same would apply for %X
and %c
. The format for these āpreferred formatsā can also then vary based upon locale without too much difficulty in any locale-aware calendar.
Examples
Format |
Output |
%m |
1, 12 |
%2m |
01, 12 |
%A |
Monday |
%^A |
MONDAY |
Summary
This proposal intents to:
-
Recommend reinforcing the Calendar
behaviour and add to it those missing pieces that support formatting which are primarily the day_name
and month_name
(short and long) functions.
-
Simplify the interaction between strftime
and a Calendar
module by reverting to the original plan of simply calling the relevant functions in the calendar module.
-
Making formatting decisions part of the format
string (ie flags and width modifiers)
Options
I have included an options
argument to strftime
above because for localisation a locale name needs to be specified. In Python a locale
is part of the stdlib and runtime environment but not so Elixir. Therefore it should be passed in to the calendar. It could be argued that since there is no other part of Elixir that embodies the idea of localisation then this is not the place to start. I admit my bias given my work on Cldr, but I think the idea of a locale should be able to be specified.
A Calendar
should feel free to return {:error, "unknown locale"}
if a locale is requested that it doesnāt support. Of if a more loose contract is preferred then treat the locale as entirely optional.