Strftime
NAME
Date::Calendar::Strftime - formatting any Date object or Date::Calendar::whatever object with 'strftime'
SYNOPSIS
Using the strftime function with the core class Date:
use Date::Calendar::Strftime;
my Date $last-day .= new(2019, 12, 31);
say strftime($last-day, "%Y-%m-%d %G-W%V-%u");
# --> 2019-12-31 2020-W01-2
Using the strftime method, with the French Revolutionary calendar
use Date::Calendar::FrenchRevolutionary;
#------> no "use Date::Calendar::Strftime;" is necessary!
my Date::Calendar::FrenchRevolutionary $Bonaparte's-coup-fr;
$Bonaparte's-coup-fr .= new(year => 8, month => 2, day => 18);
say $Bonaparte's-coup-fr.strftime("%Y-%m-%d");
# ---> "0008-02-18" for 18 Brumaire VIII
say $Bonaparte's-coup-fr.strftime("%A %e %B %EY");
# ---> "octidi 18 Brumaire VIII"
DESCRIPTION
Date::Calendar::Strftime is a role providing a strftime method and a strftime function to
format a string representing the date. This method and this function are similar to the
strftime function in C.
This role automatically applies to any Date::Calendar::xxx class
and can be manually applied to instances of the Date core class.
Usage with the core class
The simplest way to use strftime with the code class Date is
using the function and not bothering with the method.
use Date::Calendar::Strftime;
my Date $last-day .= new(2019, 12, 31);
say strftime($last-day, "%A %d %B %Y", "en");
# --> Tuesday 31 December 2019
Using the method wih the core Date class requires some convoluted
code. There are two variants. The first variant assigns the
Date::Calendar::Srftime role to each Date
instance separately. The second variant, shown below, declares an
empty class which merges the core Date class with the
Date::Calendar::Srftime role.
use Date::Calendar::Strftime;
my Date $last-day .= new(2019, 12, 31);
$last-day does Date::Calendar::Strftime;
say $last-day.strftime("%Y-%m-%d ('ISO' date %G-W%V-%u)");
# --> 2019-12-31 ('ISO' date 2020-W01-2)
use Date::Calendar::Strftime;
class My::Date is Date
does Date::Calendar::Strftime {}
my My::Date $last-day .= new(2019, 12, 31);
say $last-day.strftime("%Y-%m-%d %G-W%V-%u");
# --> 2019-12-31 2020-W01-2
Or you can use the Date::Calendar::Gregorian class instead of
Date. See below.
Note: if you need month names and day-of-week names, you cannot choose
the locale when using the "does" version or the "My::Date" version. If
using the strftime function or the Date::Calendar::Gregorian
class, you can choose the locale.
Usage with a Date::Calendar::xxx class
Date::Calendar::Strftime is automatically and implicitly loaded
when using a Date::Calendar::xxx class. There is no need to add
a use statement.
Exceptions: early versions of Date::Calendar::FrenchRevolutionary
and Date::Calendar::Hebrew do not include the loading of this
module and are only partially compatible with it.
Both the strftime method and the strftime function are available
with the Date::Calendar::xxx instances.
EXPORTED SUBROUTINES
Day Parts
The module Date::Calendar::Strftime exports three routines:
before-sunrisedaylightafter-sunset
These subroutines have not input parameters and each one returns a
constant value. They are used in the Date::Calendar::xxx modules
when creating a date object, so all modules will use the same three
values for the same meaning.
These three subroutines are similar to an enum type declaration,
with a different scope (they must be visible in the
Date::Calendar::xxx modules and in the calling programs).
strftime function
The strftime function receives three positional parameters:
the date instance, mandatory
the format string, mandatory
the locale code, optional.
The date instance is an instance of the core class Date or an
instance or a class Date::Calendar::xxx.
The format string is described below, in the documentation for the method.
For the core class Date, the locale parameter can use any value
defined in Dates::Names. If no value is given, the default value is
'en' for English.
For Date::Calendar::xxx classes with only one locale (Hebrew
Hijri, Coptic, Ethiopic, Persian), the locale parameter is ignored.
For Date::Calendar::xxx classes with several possible locales
(Gregorian, Julian, Maya, Aztec, French Revolutionary, Baháʼí), the
locale parameter defaults to the locale attribute of the date
instance.
METHOD
There is only one method in the Date::Calendar::Strftime role.
strftime
This method is very similar to the homonymous functions you can find
in several languages (C, shell, etc). It also takes some ideas from
printf-similar functions. For example
$df.strftime("%04d blah blah blah %-25B")
will give the day number padded on the left with 2 or 3 zeroes to
produce a 4-digit substring, plus the substring " blah blah blah ",
plus the month name, padded on the right with enough spaces to produce
a 25-char substring. Thus, the whole string will be at least 42 chars
long.
A strftime specifier consists of:
A percent sign.
An optional minus sign, to indicate on which side the padding occurs. If the minus sign is present, the value is aligned to the left and the padding spaces are added to the right. If it is not there, the value is aligned to the right and the padding chars (spaces or zeroes) are added to the left.
An optional zero digit, to choose the padding char for a right-aligned left-padded value. If the zero char is present, padding is done with zeroes. Else, it is done wih spaces.
An optional length, which specifies the minimum length of the result substring.
An optional
"E"or"O"modifier. On some older UNIX systems, these were used to give the extended or localized version of the date attribute. Here, they rather give alternate variants of the date attribute.A mandatory type code. A type code is any character other than the characters used in the optional modifiers above. That is, any character except a digit, a dash, a letter
"E"or a letter"O".
The dot-digit optional modifier, used in printf for numbers with a
fractional part, is not implemented. Likewise, the plus optional
modifier, which displays a plus sign for positive numeric values, is
not implemented.
Standard strftime codes
The Date::Calendar::Strftime module implements a few standard type
codes as listed below. Many others are possible, but they must be
provided by the calling Date::Calendar::xxx module.
- %a
The abbreviated name of the day of week.
If not defined (as with the Date core module), the formatter is
returned as is.
- %A
The full name of the day of week.
If not defined (as with the Date core module), the formatter is
returned as is.
- %b
The abbreviated month name.
If not defined (as with the Date core module), the formatter is
returned as is.
- %B
The full month name.
If not defined (as with the Date core module), the formatter is
returned as is.
- %d
The day of the month as a decimal number (usually range 01 to 31).
- %e
Like %d, the day of the month as a decimal number, but a leading
zero is replaced by a space.
- %f
The month as a decimal number (usually 1 to 12). Unlike %m, a
leading zero is replaced by a space. This is still a 2-char string.
- %F
Equivalent to %Y-%m-%d (similar to the ISO 8601 date format for
Gregorian dates)
- %G
The year as a decimal number. By default, strictly similar to %L
and %Y. If the calendar has a concept of week or similar and if the
week is not synchronised with the year, this formatter gives the
number of the "quasi-year" as defined by ISO-8601 for the so-called
"ISO date" for Gregorian dates. This "quasi-year" (method
week-year) is synchronised with the week.
- %j
The day of the year as a three-digit decimal number (usually range 001 to 366).
- %L
The year as a decimal number. By default, strictly similar to %G
and %Y.
- %m
The month as a two-digit decimal number (usually range 01 to 12), including a leading zero if necessary.
- %n
A newline character.
- %Ep
Gives a 1-char string representing the day part:
☾orU+263Ebefore sunrise,☼orU+263Cduring daylight,☽orU+263Dafter sunset.
Rationale: in C or in other programming languages, when strftime
deals with a date-time object, the day is split into two parts, before
noon and after noon. The %p specifier reflects this by giving a
"AM" or "PM" string.
The 3-part splitting in the Date::Calendar::xxx may be
considered as an alternate splitting of a day. To reflect this in
strftime, we use an alternate version of %p, therefore %Ep.
- %t
A tab character.
- %u
If the calendar has a notion of week, this formatter give the day of week as a 1..7 number (or some other range if the week-like concept is not exactly a 7-day span).
If the calendar has no week-like notion, this formatter returns itself
"%u" (or possibly with its would-be length and padding codes, like
"%-3u").
- %V
If the calendar has a notion of week or similar, this formatter gives the week number. If the week and the year are not synchronised, the week number is defined in a fashion similar to the week number in the so-called "ISO date" format for Gregorian dates.
For the Gregorian calendar, this number is within the 1..53 range. For other calendars, the range may be different.
If the calendar has no week-like notion, this formatter returns itself
"%V" (or possibly with its would-be length and padding codes, like
"%05V").
- %Y
The year as a decimal number. By default, strictly similar to %G
and %L.
- %%
A literal % character.
Date::Calendar::xxx Requirements
The Date::Calendar::xxx module must implement at least the
following methods:
year
month
day
day-of-year
The Date::Calendar::xxx module should also implement the
following methods if possible:
month-name
month-abbr
day-name
day-abbr
day-of-week
week-number
week-year
daypart
Specific strftime codes
A Date::Calendar::xxx module can add its specific formats types
by specifying a specific-format method which returns a hash where
the keys are the format types and the values are the callbacks used to
format the date attributes.
Example: a module defines a feast method, which will be inserted in
string with the %Oj or the %* specifiers. This module defines:
method specific-format { %( Oj => { $.feast },
'*' => { $.feast } ); }
The same specific-format method can also be used to override or
inhibit existing standard specifiers. For example, a
Date::Calendar::xxx module deactivates the %a specifier
(abbreviated day) and overrides the month-abbr method with the
abbreviated-month method (%b specifier). This module will
define:
method specific-format { %( a => Nil,
b => { $.abbreviated-month } ); }
Of course, these features can be combined with
method specific-format { %( Oj => { $.feast },
'*' => { $.feast },
a => Nil,
b => { $.abbreviated-month } ); }
So, the sentence a few paragraphs above was not completely true. It should actually read:
The Date::Calendar::xxx module must implement at least the
following methods: [...] except those that are inhibited or overriden
with the specific-format method.
Precedence
When encountering a %Ex specifier, the following entries are tried
and the first existing one is selected (let us ignore Nil values in
the hashes):
1 Entry
ExfromDate::Calendar::xxx.specific-format.2 Entry
xfromDate::Calendar::xxx.specific-format.3 Entry
Exfrom%formatterinDate::Calendar::Strftime4 Entry
xfrom%formatterinDate::Calendar::Strftime5
fall-backattribute of the match object, which gives the"%Ex"string.
And similarly for a %Ox specifier.
For a specifier without alternate form, such as %x, the precedence is:
1 Entry
xfromDate::Calendar::xxx.specific-format.2 Entry
xfrom%formatterinDate::Calendar::Strftime3
fall-backattribute of the match object, which gives the"%x"string.
In the lists above, if at any time a hash entry exists with a Nil
value, the fall-back attribute is immediately chosen without
examining the other possibilities.
Known Bugs, Silly Things and Security Concerns
Make sure the strftime format string comes from a trusted origin.
Here are a few reasons.
You should not truncate a numeric value when formatting it. Especially a year. In the 1950's and the 1960's, when RAM and storage were expensive, programmers had a good reason to truncate numbers and let the users guess the missing parts. But nowadays, kilobytes of RAM and storage are several orders of magnitude cheaper so there is no longer any reason to truncate numbers. Twenty years ago, the worldwide endeavour to fix the Y2K bug was a really necessary endeavour and a mostly successful one, even if some glitches happened (and were hushed). This does not mean that twenty or more years later you can fallback into the old dirty habits of butchering year numbers.
On the other hand, using unambiguous abbreviations for day names and month names is OK. Be sure there is no ambiguity.
About the optional length, the module does not impose a maximum value.
A format such as "%123456789A" is valid and accepted. Yet, it will
drain your free RAM very fast. So do not use such a ridiculous length
for a single string.
Zero-padding should apply only to numbers. Yet, nothing in the module prevents you from padding alphabetic strings with zeroes.
Numeric values in calendars are rarely negative and string values
rarely begin with a dash. When left-padding with zeroes, the program
checks the first char of the value. If this is a minus sign or a dash
char, the padding zeroes are inserted between the minus sign and the
rest of the value. Thus negative numbers look right ("-000123"
instead of "000-123"). At the same time, a string beginning with a
dash looks silly when zero-padded. You cannot have your cake and eat
it too. Anyhow, you should not zero-pad strings, only numbers should
be zero-padded.
Why three specifiers for the year?
The main year specifier is the %Y specifier. What is the use of
%G and %L? First, let us examine %G.
ISO Date and %G Specifier
While %Y is the year of the day, %G is the "year of the week of
the day". Here is the explanation of this convoluted formula.
Between the day and the year, most calendars have two different intermediary time units, the week and the month. In all calendars, the year is synchronised with the month: a change from a year to the next always occurs simultaneously with a change from a month to the next. On the other hand, the year is not synchronised with the week: a change from the year to the next may happen at the middle of a week.
Because the year and the month are synchronised, the usual scheme for designating a date is the year-month-day scheme. Yet, it may be convenient to use a week-based scheme sometimes. So the ISO-8601 standard defines the following scheme:
1 Weeks span from Monday to Sunday.
2 If a week is fully within a year, it is assigned to this year.
3 If a week is across a year change, it is assigned to the year with which it shares at least 4 days.
4 Once all weeks have been assigned to a year, the weeks belonging to a given year are numbered 1 to 52 (or 53).
The year obtained with these steps is the "year of the week of the day". From these steps, you can infer a few facts.
From 4th January to 28th December, the year of the week of the day always coincides with the year of the day. On the first three days of the year, 1st Jan to 3rd Jan, the years may differ or they may coincide. Same thing for the last three days, 29th Dec to 31st Dec.
No matter how the year and the week are unsynchronised, week 1 is the week containing 4th Jan, week 2 is the week containing 11th Jan, week 3 is the week containing 18th Jan and so on.
No matter how the year and the week are unsynchronised, on any Thursday, the year of the week of the day is always equal to the year of the day. Also, week 1 is the week containing the first Thursday of the year, week 2 is the week containing the second Thursday of the year, and so on.
Other calendars use unsynchronised weeks, like the Hebrew calendar, the Coptic calendar and the Ethiopic calendar. A difference with the Gregorian calendar is that in threse three calendars weeks are Sunday → Saturday spans. So we can define rules similar to the Gregorian calendar's ISO date rules, but there, Wednesday / Yom Reviʻi / Peftoou / Rob play a central role (pun intended) instead of Thursday. Also, for the Hebrew calendar, the number range for week numbers will not be 1..52 or 1..53, but 1..50, 1..51, 1..55 or 1..56 depending on the type of the year.
A more remote case: the French Revolutionary calendar use décades,
not weeks. Décades are 10-day long and are synchronised with the
year, and even with the 30-day months. The last décade of the year
is shortened to 5 or 6 days, to keep the synchronisation with the
year. So, what is told above about the relations between the week and
the year may not apply to this calendar. In this calendar, the %G
specifier always gives the same result as the %Y specifier.
The %L Specifier
Why a %L specifier in Date::Calendar::Strftime? Because there
was already a %L specifier in the Perl module
DateTime::Calendar::FrenchRevolutionary (which I wrote). And why a
%L specifier in DT::C::FR? Because there is already one in
Perl's Date::Convert::French_Rev (which I wrote, too). And why a
%L specifier in D::C::F_R? Well...
So why did I include a %L specifier in the first release of
Date::Convert::French_Rev in early 2001? I cannot remember. Maybe
there was another week-based scheme, that fell into disuse and then
into oblivion. Therefore this specifier is deprecated, starting with
the release of version 0.0.4 in 2024. It will be removed in 2026
(or a later release). You are advised not to use it and to use either
%Y or %G depending on the context.
SEE ALSO
Raku Software
https://raku.land/zef:lizmat/DateTime::strftime or https://github.com/lizmat/DateTime-strftime.
Perl Software
AUTHOR
Jean Forget <J2N-FORGET at orange dot fr>
SUPPORT
You can send me a mail using the address above. Please be sure to include a subject sufficiently clear and sufficiently specific to be green-flagged by my spam filter.
Or you can send a pull request to the Github repository for this module.
COPYRIGHT AND LICENSE
Copyright (c) 2019, 2020, 2024, 2025 Jean Forget, all rights reserved
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.