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-sunrise

  • daylight

  • after-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:

  • ☾ or U+263E before sunrise,

  • ☼ or U+263C during daylight,

  • ☽ or U+263D after 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 Ex from Date::Calendar::xxx.specific-format.

  • 2 Entry x from Date::Calendar::xxx.specific-format.

  • 3 Entry Ex from %formatter in Date::Calendar::Strftime

  • 4 Entry x from %formatter in Date::Calendar::Strftime

  • 5 fall-back attribute 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 x from Date::Calendar::xxx.specific-format.

  • 2 Entry x from %formatter in Date::Calendar::Strftime

  • 3 fall-back attribute 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

DateTime

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.

Date::Calendar::Strftime v0.1.1

formatting any Date object or Date::Calendar::whatever object with 'strftime'

Authors

  • Jean Forget

License

Artistic-2.0

Dependencies

Date::Names

Test Dependencies

Provides

  • Date::Calendar::Strftime

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.