The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Date::Tolkien::Shire::Data - Data functionality for Shire calendars.

SYNOPSIS

 use Date::Tolkien::Shire::Data;
 
 say __on_date( 1, 2 ) // "Nothing happened\n";

DESCRIPTION

This Perl module carries common functionality for implementations of the Shire calendar as described in Appendix D of J. R. R. Tolkien's novel "Lord Of The Rings". What it really contains is anything that was common to Date::Tolkien::Shire and DateTime::Fiction::JRRTolkien::Shire and I felt like factoring out.

The Shire calendar has 12 months of 30 days each, plus 5 holidays (6 in a leap year) that are not part of any month. Two of these holidays (Midyear's day and the Overlithe) are also part of no week.

In all of the following, years are counted from the founding of the Shire. Months are numbered 1-12, and days in months from 1-30. Holidays are specified by giving a month of 0 and the holiday number, as follows:

1 - 2 Yule
2 - 1 Lithe
3 - Midyear's day
4 - Overlithe (which occurs in leap years only)
5 - 2 Lithe (so numbered even in non-leap years)
6 - 1 Yule

SUBROUTINES

This class supports the following public subroutines. Anything documented below is public unless its documentation explicitly states otherwise. The names begin with double underscores because it is anticipated that, although they are public as far as this package is concerned, they will be package-private for the purposes of any code that uses this package.

All of the following are exportable to your name space, but none are exported by default.

__date_to_day_of_year

 say __date_to_day_of_year( 1420, 3, 25 );

This subroutine takes as input a year, month, and day and returns the day number in the year. An exception will be thrown if you specify the Overlithe ("month" 0, day 4) and it is not a leap year.

__day_of_week

 say '3 Astron is day ', __day_of_week( 4, 3 );

Given a month number and day number in the month, computes the day of the week that day falls on, as a number from 1 to 7, 1 being Sterday. If the day is Midyear's day or the Overlithe (month 0, days 3 or 4) the result is 0.

__day_of_year_to_date

 my ( $month, $day ) = __day_of_year_to_date( 1419, 182 );

Given a year and a day number in the year (from 1), compute the month and day of month. An exception will be thrown unless the day number is between 1 and 365, or 366 in a leap year.

__format

 say __format( $date, '%c' );

This method formats a date, in a manner similar to strftime(). The $date is either an object that supports the necessary methods, or a reference to a hash having the necessary keys (same as the methods). The methods (or keys) used are:

  year
  month
  day
  holiday
  hour
  minute
  second
  day_of_week
  nanosecond
  epoch
  offset
  time_zone_short_name

The first seven are heavily used. The last four are used only by '%N', '%s', '%z', and '%Z' respectively.

If you pass a hash, the canonical say specification is either a non-zero month and day and zero holiday, or vice versa. But if you pass a zero month and holiday, the method will assume that you were specifying a holiday and adjust the hash accordingly, though this adjustment will not be visible outside __format(). Also, your hash need not specify day_of_week since it can be computed from month and day, or holiday.

The following conversion specifications (to use strftime() terminology) or patterns (to use DateTime terminology) are supported. Note that these have been extended by the use of '%E*' patterns, which generally represent holidays. E-prefixed patterns not defined below are (consistent with strftime()) implemented as if the 'E' were not present, but this use is discouraged because additional E-prefixed (or O-prefixed, which strftime() also allows) patterns may prove to be necessary.

%A

The full weekday name, or '' for holidays that are part of no week.

%a

The abbreviated weekday name, or '' for holidays that are part of no week.

%B

The full month name, or '' for holidays.

%b

The abbreviated month name, or '' for holidays.

%C

The century number (year/100) as a 2-digit integer.

%c

For normal days this is the abbreviated weekday name ('%a'), the day number ('%e'), the abbreviated month name (%b), and the full year ('%Y'), followed by the time of day.

For holidays the abbreviated holiday name ('%Ee') replaces the day and month, and the weekday name is omitted if the holiday is part of no week. So (assuming times for all events):

 Sun 25 Ret 1419  3:00:00 PM # Ring destroyed
 Myd 1419 12:00:OO PM        # Wedding of Aragorn and Arwen
%D

The equivalent of '%m/%d/%y', or '%Ee/%y' on holidays. This format is discouraged, because it may not be clear whether it is month/day/year (as the United States does it) or day/month/year (as Europe does it).

%d

The day of the month as a decimal number, zero-filled (range 01 to 30). On holidays it is the holiday number, zero-filled (range 01 to 06).

%EA

The full traditional weekday name, or '' for holidays that are part of no week.

%Ea

The abbreviated traditional weekday name, or '' for holidays that are part of no week.

%ED

The __on_date_accented() text for the given date.

You can get a leading "\n" if there was an actual event using '%En%ED'.

%Ed

The __on_date() text for the given date.

You can get a leading "\n" if there was an actual event using '%En%Ed'. So to mimic Date::Tolkien::Shire on_date(), use '%Ex%n%En%Ed'.

%EE

The full holiday name, or '' for non-holidays.

%Ee

The abbreviated holiday name, or '' for non-holidays.

%En

Inserts nothing, but causes the next %Ed or %ED (and only the next one) to have a "\n" prefixed if there was an actual event on the date.

%Ex

Like '%c', but without the time of day, and with full names rather than abbreviations.

%e

The day of the month as a decimal number, space-filled (range ' 1' to '30'). On holidays it is the holiday number, space-filled (range ' 1' to ' 6').

%F

For normal dates this is equivalent to '%Y-%m-%d' (i.e. the ISO 8601 date format). For holidays it is equivalent to '%Y-%Er', which is something ISO had nothing to do with.

%G

The ISO 8601 year number. Given how the Shire calendar is defined, the ISO year number is the same as the calendar year (i.e. '%Y').

%H

The hour, zero-filled, in the range '00' to '23'.

%h

Equivalent to '%b'.

%I

The hour, zero-filled, in the range '01' to '12'.

%j

The day of the year, zero-filled, in the range '001' to '366'.

%k

The hour, blank-filed, in the range ' 0' to '23'.

%l

The hour, blank-filled, in the range ' 1' to '12'.

%M

The minute, zero-filled, in the range '00' to '59'.

%m

The month number, zero filled, in the range '01' to '12'. On holidays it is '00'.

%N

The fractional seconds. A decimal digit may appear between the percent sign and the 'N' to specify the precision: '3' gives milliseconds, '6' microseconds, and '9' nanoseconds. The default is '9'.

%n

A newline character.

%P

The meridian indicator, 'am' or 'pm'.

%p

The meridian indicator, 'AM' or 'PM'.

%R

The time in hours and minutes, on a 24-hour clock. Equivalent to '%H:%M'.

%r

The time in hours, minutes and seconds on a 12-hour clock. Equivalent to '%I:%M:%S %p'.

%S

The second, zero-filled, in the range '00' to '61', though only to '59' unless you are dealing with times when the leap second has been invented.

%s

The number of seconds since the epoch.

%T

The time in hours, minutes and seconds on a 24-hour clock. Equivalent to '%H:%M:%S'.

%t

A tab character.

%U

The week number in the current year, zero-filled, in the range '01' to '52', or '' if the day is not part of a week.

%u

The day of the week, as a number in the range '1' to '7', or '' if the day is not part of a week.

%V

I have made this the same as '%U', because all Shire years start on the same day of the week, and I do not think the hobbits would understand or condone the idea of different starting days to a week.

%v

This is from the BSD version of strftime(), and is equivalent to '%{{%e %b %Y||%Ee %Y}}'.

%W

I have made this the same as '%U'. For my reasoning, see above under '%V'.

%w

I have made this the same as '%u', my argument being similar to the argument for making '%V' the same as '%U'.

%X

I have made this the same as '%r'. We know the hobbits had clocks, because in "The Hobbit" Thorin Oakenshield left Bilbo Baggins a note under the clock on the mantelpiece. We know they spoke of time as "o'clock" because in the chapter "Of Herbs and Stewed Rabbit" in "The Lord Of The Rings", Sam Gamgee speaks of the time as being nine o'clock. But this was in the morning, and we have no evidence that I know of whether mid-afternoon would be three o'clock or fifteen o'clock. But my feeling is for the former. If I get evidence to the contrary this implementation will change.

%x

I have made this day, abbreviated month, and full year. Holidays are abbreviated holiday name and year.

%Y

The year number.

%y

Year of century, zero filled, in the range '00' to '99'.

%Z

The time zone abbreviation.

%z

The time zone offset.

%%

A literal percent sign.

%{

A literal left curly bracket.

%}

A literal right curly bracket.

%|

A literal vertical bar.

%{method_name}

Any method actually implemented by the $date object can be specified. This method will be called without arguments and its results replace the conversion specification. If the method does not exist, the pattern is left as-is.

%{{format||format||format}}

The formatter chooses the first format for normal days (i.e. part of a month), the second for holidays that are part of a week (i.e. 2 Yule, 1 Lithe, 2 Lithe and 1 Yule), or the third for holidays that are not part of a week (i.e. Midyear's day and the Overlithe). If the second or third formats are omitted, the preceding format is used. Trailing || operators can also be omitted. If you need to specify more than one right curly bracket or vertical bar as part of a format, separate them with percent signs (i.e. '|%|%|'.

Some of the Glibc extensions are implemented on some of the conversion specifications, typically where the author felt a need for them. The following flag characters may be specified immediately after the '%':

- (dash)

This flag specifies no padding at all. That is to say, on the first of the month, both '%-d' and '%-e' produce '1', not '01' or ' 1' respectively.

If an explicit field width is specified (see below), this specifies space padding.

_ (underscore)

This flag specifies padding with spaces. That is to say, on the first of the month, '%_d' produce ' 1', not '01'. So does '%_e', in case you were wondering.

0 (zero)

This flag specifies padding with zeroes. That is to say, on the first of the month, '%0e' produce '01', not ' 1'. So does '%0d', in case you were wondering.

^

This flag specifies making the result upper-case.

#

If applied to '%p' or '%Z', this flag converts the output to lower case. It also overrides the '^' flag if both are specified, regardless of order.

If applied to '%A', '%a', '%B', '%b' '%EA', '%Ea' '%EE', '%Ee', or '%h', this flag converts the output to lower case.

If applied to anything else, this flag has no effect.

Immediately after the flags (if any) you can specify a field width that overrides the default. If you explicitly specify field width, flag '-' pads with spaces, even in numeric fields.

__holiday_name

 say __holiday_name( 3 );

Given a holiday number (1-6), this subroutine returns that holiday's name. If the holiday number is 0 (i.e. the day is not a holiday), an empty string is returned. Otherwise, undef is returned.

__holiday_name_to_number

 say __holiday_name_to_number( 'overlithe' );

Given a holiday name, this subroutine normalizes it by converting it to lower case and removing spaces and punctuation, and then returns the number of the holiday. Unique abbreviations of names or short names (a.k,a. abbreviations) are allowed. Arguments consisting entirely of digits are returned unmodified. Anything unrecognized causes 0 to be returned.

__holiday_short

 say __holiday_short( 3 );

Given a holiday number (1-6), this subroutine returns that holiday's three-letter abbreviation. If the holiday number is 0 (i.e. the day is not a holiday), an empty string is returned. Otherwise, undef is returned.

__is_leap_year

 say __is_leap_year( 1420 );  # 1

Given a year number, this subroutine returns 1 if it is a leap year and 0 if it is not.

__month_name

 say __month_name( 3 );

Given a month number (1-12), this subroutine returns that month's name. If the month number is 0 (i.e. a holiday), the empty string is returned. Otherwise undef is returned.

__month_name_to_number

 say __month_name_to_number( 'forelithe' );

Given a month name, this subroutine normalizes it by converting it to lower case and removing spaces and punctuation, and then returns the number of the month. Unique abbreviations of names or short names (a.k,a. abbreviations) are allowed. Arguments consisting entirely of digits are returned unmodified. Anything unrecognized causes 0 to be returned.

__month_short

 say __month_short( 3 );

Given a month number (1-12), this subroutine returns that month's three-letter abbreviation. If the month number is 0 (i.e. a holiday), the empty string is returned. Otherwise undef is returned.

__on_date

 say __on_date( $month, $day ) // '';
 say __on_date( $holiday ) // '';

Given month and day numbers (or a holiday number), returns text representing the events during and around the War of the Ring that occurred on that date. If nothing happened or any argument is out of range, undef is returned.

The actual text returned is from Appendix B of "Lord Of The Rings", and is copyright J. R. R. Tolkien, renewed by Christopher R. Tolkien et al.

__on_date_accented

 binmode STDOUT, ':encoding(utf-8)';
 say __on_date_accented( $month, $day ) // '';
 say __on_date( $holiday ) // '';

This wrapper for __on_date() accents those names and words that are accented in the text of the Lord Of The Rings. How these display depends on how your Perl is configured. Note that the example above assumes Perl 5.10 (for say() and //). Even without those, the two-argument binmode() requires 5.8. Your mileage may vary.

Note that the first call incurs the overhead of accenting all events.

__quarter

 say __quarter( $month, $day );

Given month and day numbers, returns the relevant quarter number. If the date specified is Midyear's day or the Overlithe ("month" 0, days 3-4), the result is 0; otherwise it is a number in the range 1-4.

There is nothing I know of about hobbits using calendar quarters in anything Tolkien wrote. But if they did use them I suspect they would be rationalized this way.

__rata_die_to_year_day

 my ( $year, $day ) = __rata_die_to_year_day( $rata_die );

Given a Rata Die day, returns the year and day of the year corresponding to that Rata Die day.

The algorithm used was inspired by Howard Hinnant's "chrono-Compatible Low-Level Date Algorithms" at http://howardhinnant.github.io/date_algorithms.html, and in particular his civil_from_days() algorithm at http://howardhinnant.github.io/date_algorithms.html#civil_from_days.

This subroutine assumes no particular calendar, though it does assume the Gregorian year-length rules, which have also been adopted for the Shire calendar. If you feed it am honest-to-God Rata Die day (i.e. days since December 31 of proleptic Gregorian year 0) you get back the Gregorian year and the day of that year (1-366). If you feed it a so-called Shire Rata Die (i.e. days since 1 Yule of Shire year 0) you get back the Shire year and the day of that year.

__trad_weekday_name

 say 'Day 1 is ', __trad_weekday_name( 1 );

This subroutine computes the traditional (i.e. old-style) name of a weekday given its number (1-7). If the weekday number is 0 (i.e. Midyear's day or the Overlithe) the empty string is returned. Otherwise, undef is returned.

__trad_weekday_short

 say 'Day 1 is ', __trad_weekday_short( 1 );

This subroutine computes the three-letter abbreviation of a traditional (i.e. old-style) weekday given its number (1-7). If the weekday number is 0 (i.e. Midyear's day or the Overlithe) the empty string is returned. Otherwise, undef is returned.

__weekday_name

 say 'Day 1 is ', __weekday_name( 1 );

This subroutine computes the name of a weekday given its number (1-7). If the weekday number is 0 (i.e. Midyear's day or the Overlithe) the empty string is returned. Otherwise, undef is returned.

__weekday_short

 say 'Day 1 is ', __weekday_short( 1 );

This subroutine computes the three-letter abbreviation of a weekday given its number (1-7). If the weekday number is 0 (i.e. Midyear's day or the Overlithe) the empty string is returned. Otherwise, undef is returned.

__week_of_year

 say '25 Rethe is in week ', __week_of_year( 3, 25 );

This subroutine computes the week number of the given month and day. Weeks start on Sterday, and the first week of the year is week 1. If the date is part of no week (i.e. Midyear's day or the Overlithe), 0 is returned.

__year_day_to_rata_die

Given the year and day of the year, this subroutine returns the Rata Die day of the given year and day. The day of the year defaults to 1.

This subroutine assumes no particular calendar, though it does assume the Gregorian year-length rules, which have also been adopted for the Shire calendar. If you feed it a Gregorian year, you get an honest-to-God Rata Die, as in days since December 31 of proleptic Gregorian year 0. If you feed it a Shire year, you get a so-called Shire Rata Die, as in the days since 1 Yule of Shire year 0.

MANIFEST CONSTANTS

The following manifest constants are exportable to your name space. None is exported by default.

GREGORIAN_RATA_DIE_TO_SHIRE

This manifest constant represents the number of days to add to a real Rata Die value (days since December 31 of proleptic Gregorian year 0) to get a so-called Shire Rata Die (days since 1 Yule of Shire year 0.)

The value was determined by the following computation.

  my $dts = DateTime::Fiction::JRRTolkien::Shire->new(
      year    => 1,
      holiday => 1,
  );
  my $gdt = DateTime->new(
      year    => 1,
      month   => 1,
      day     => 1,
  );
  my $rd_to_shire = ( $gdt->utc_rd_values() )[0] -
      ( $dts->utc_rd_values() )[0];

using DateTime::Fiction::JRRTolkien::Shire version 0.21. This is after I adopted that module but before I started messing with the computational internals.

SEE ALSO

Date::Tolkien::Shire

DateTime::Fiction::JRRTolkien::Shire

SUPPORT

Support is by the author. Please file bug reports at http://rt.cpan.org, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT AND LICENSE

Copyright (C) 2017 by Thomas R. Wyant, III

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.