DateTime-Fiction-JRRTolkien-Shire

 view release on metacpan or  search on metacpan

tools/lib/DateTime/Fiction/JRRTolkien/Shire.pm  view on Meta::CPAN

package DateTime::Fiction::JRRTolkien::Shire;

use vars qw($VERSION);
use strict;
use DateTime;
our $VERSION = '0.900_04';

# This assumes all the values in the info hashref are valid, and doesn't do validation
# However, the day and month parameters will be given defaults if not present
sub _recalc_DateTime {
    my ($self, %dt_args) = @_;
    my ($prevleap, $gregleap, $modyear, $yday, $arg);
    my @monthlen = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

    $dt_args{year} = $self->{year} - 5464;
    # $prevleap refers to shire calendar
    $prevleap = 0;
    $prevleap = 1 if ((($self->{year} - 1) % 4 == 0) and (($self->{year} - 1) % 100 != 0));
    $prevleap = 1 if (($self->{year} - 1) % 400 == 0);

    if ($self->{holiday}) {
	if ($self->{leapyear}) {
	    $yday = (0, 1, 182, 183, 184, 185, 366)[$self->{holiday}];
	} else {
	    $yday = (0, 1, 182, 183, 0, 184, 365)[$self->{holiday}];
	}
    } else {
	$yday = ($self->{month} - 1) * 30 + $self->{day} + 1; # The +1 is for 2 Yule
	$yday += 3 if $yday > 181; #Account for the Lithe and Midyear day
	++$yday if $self->{leapyear} and $yday > 183; #Account for Overlithe
    }
    $yday -= 9; # Different Start of years.  We'll adjust this based on the year momentarily

    #Now for adjustments for various years being off by a day
    $modyear = $self->{year} % 400;
    if (($modyear > 300) && ($modyear < 364)) {
	--$yday;
    } elsif ($modyear == 364) {
	--$yday;
    } elsif ((($modyear > 64) && ($modyear < 100)) || (($modyear > 164) && ($modyear < 200))) {
	++$yday;
    } elsif (($modyear == 100) || ($modyear == 200)) {
	++$yday;
    }

    if ($yday < 1) {
	--$dt_args{year};
	$gregleap = 0;
	$gregleap = 1 if (($dt_args{year} % 4 == 0) and ($dt_args{year} % 100 != 0));
	$gregleap = 1 if ($dt_args{year} % 400 == 0);
	if ($gregleap) { 
	    $yday += 366;
	} else { 
	    $yday += 365; 
	}
    } else {
	$gregleap = 0;
	$gregleap = 1 if (($dt_args{year} % 4 == 0) and ($dt_args{year} % 100 != 0));
	$gregleap = 1 if ($dt_args{year} % 400 == 0);
	if ($gregleap and $yday > 366) {
	    ++$dt_args{year};
	    $yday -= 366;
	    $gregleap = 0;
	} elsif ($yday > 365) {
	    ++$dt_args{year};
	    $yday -= 365;
	    $gregleap = 0;
	    $gregleap = 1 if (($dt_args{year} % 4 == 0) and ($dt_args{year} % 100 != 0));
	    $gregleap = 1 if ($dt_args{year} % 400 == 0);
	}
    }

    #now convert the year-day to the month and month-day
    $monthlen[2] = 29 if $gregleap;
    $dt_args{month} = 1;
    while ($yday > $monthlen[$dt_args{month}] and $dt_args{month} < 12) {
	$yday -= $monthlen[$dt_args{month}];
	++$dt_args{month};
    }
    $dt_args{day} = $yday;

    # Now for time parameters, if any
    if ($self->{dt}) {
	foreach $arg (qw(hour minute second nanosecond time_zone locale)) {
	    $dt_args{$arg} = $self->{dt}->$arg if not defined $dt_args{$arg};
	}
    }

    $self->{dt} = new DateTime(%dt_args);
} # end sub _recalc_DateTime

sub _recalc_Shire {
    my $self = shift;
    my ($yday, $modyear);

    $self->{year} = $self->{dt}->year + 5464;
    $self->{holiday} = 0; #assume this unless we find otherwise
    $yday = $self->{dt}->day_of_year + 9; # + 9 to account fora different year starting points

    # year adjustments are needed since "except every 100 except every
    # 400 year rule applies to different years in the two calendars"
    $modyear = $self->{year} % 400;
    if (($modyear > 300) && ($modyear < 364)) {
	++$yday;
    } elsif ($modyear == 364) {
	++$yday;
    } elsif ((($modyear > 64) && ($modyear < 100)) || (($modyear > 164) && ($modyear < 200))) {
	--$yday;
    } elsif (($modyear == 100) || ($modyear == 200)) {
	--$yday;
    }
    $self->{leapyear} = 0;
    $self->{leapyear} = 1 if ($self->{year} % 4 == 0) and ($self->{year} % 100 != 0);
    $self->{leapyear} = 1 if $self->{year} % 400 == 0;
    if ($self->{leapyear} and $yday > 366) {
	++$self->{year};
	$yday -= 366;
	$self->{leapyear} = 0;
    } elsif (! $self->{leapyear} and $yday > 365) { 
	++$self->{year};
	$yday -= 365;
	$self->{leapyear} = 0;
	$self->{leapyear} = 1 if ($self->{year} % 4 == 0) and ($self->{year} % 100 != 0);
	$self->{leapyear} = 1 if $self->{year} % 400 == 0;
    }

    # The overlithe only occurs on a leapyear.  By checking for it first, we can ignore leap years after this point
    if ($self->{leapyear}) {
	$self->{holiday} = 4 if ($yday == 184); #Overlithe
	--$yday if $yday > 184;
    }
    unless ($self->{holiday}) { # this will only be true if its the Overlithe
	if ($yday == 1) {$self->{holiday} = 1;} #2 Yule-first day of new year
	elsif ($yday == 182) {$self->{holiday} = 2;} #1 Lithe
	elsif ($yday == 183) {$self->{holiday} = 3;} #Midyear's day
	elsif ($yday == 184) {$self->{holiday} = 5;} #2 Lithe
	elsif ($yday == 365) {$self->{holiday} = 6;} #1 Yule
    }

    # Midyear's day (and Overlithe when applicable) are not in any week, while every other day is
    # Therefore, subtract out midyear's day to make the calculations nice
    --$yday if $yday > 183;
    if ($self->{holiday} == 3 or $self->{holiday} == 4) {
	$self->{wday} = 0;
    } else {
	$self->{wday} = (($yday - 1) % 7) + 1;
    }

    if ($self->{holiday}) { # holidays are not part of any month
	$self->{month} = 0;
	$self->{day} = 0;
    } else {
	--$yday; #ignore 2 Yule
	$yday -= 2 if $yday > 180; #ignore the Lithes (correct plural???) -- Midyear's day already subtracted out
	$self->{day} = (($yday - 1) % 30) + 1;
	$self->{month} = int(($yday - 1) / 30) + 1;
    }

    $self->{recalc} = 0;
} #end sub _date_info

tools/lib/DateTime/Fiction/JRRTolkien/Shire.pm  view on Meta::CPAN

	  if $args{day} and (int($args{day}) < 1) || (int($args{day}) > 30);
	$self->{month} = $args{month};
	$self->{day} = $args{day} || 1;
    } else {
	$self->{holiday} = 1;
    }

    foreach $arg (qw(hour minute second nanosecond time_zone locale)) { # for DateTime compatibility
	$dt_args{$arg} = $args{$arg} if defined $args{$arg};
    }
    $self->{recalc} = 1; # for weekday

    bless $self, $class;
    $self->_recalc_DateTime(%dt_args);

    return $self;
} # end sub new

sub from_epoch {
    my ($class, %args) = @_;
    my $self;

    $self->{dt} = DateTime->from_epoch(%args);
    $self->{recalc} = 1;

    return bless $self, $class;
} # end sub from_epoch

sub now {
    my ($class, %args) = @_;
    my $self;

    $self->{dt} = DateTime->now(%args);
    $self->{recalc} = 1;

    return bless $self, $class;
} # end sub now

sub today {
    my ($class, %args) = @_;
    my $self;

    $self->{dt} = DateTime->today(%args);
    $self->{recalc} = 1;

    return bless $self, $class;
} # end sub today

sub from_object {
    my ($class, %args) = @_;
    my $self;

    $self->{dt} = DateTime->from_object(%args);
    $self->{recalc} = 1;

    return bless $self, $class;
} # end sub from_object

sub last_day_of_month {
    my ($class, %args) = @_;
    $args{day} = 30; # The shire calendar is nice this way
    return $class->new(%args);
} # end sub last_day_of_month

sub from_day_of_year {
    my ($class, %args) = @_;
    my ($doy, $self, $leap);

    $doy = $args{day_of_year};
    delete $args{day_of_year};

    die "DateTime::Fiction::JRRTolkien::Shire: No year given to from_day_of_year constructor.\n" if not $args{year};
    $leap = 1 if $args{year} % 4 == 0 and $args{year} % 100 != 0;
    $leap = 1 if $args{year} % 400 == 0;
    if ($leap) {
	die "DateTime::Fiction::JRRTolkien::Shire: Invalid day given to from_day_of_year constructor.\n" if $doy > 366 or $doy < 1;
	if ($doy == 1) {
	    $args{holiday} = 1;
	} elsif ($doy == 182) {
	    $args{holiday} = 2;
	} elsif ($doy == 183) {
	    $args{holiday} = 3;
	} elsif ($doy == 184) {
	    $args{holiday} = 4;
	} elsif ($doy == 185) {
	    $args{holiday} = 5;
	} elsif ($doy == 366) {
	    $args{holiday} = 6;
	} else {
	    $doy -= 4 if $doy > 185; # Lithe's, midyear's day, and Overlithe
	    --$doy; # 2 Yule
	    $args{month} = int(($doy - 1) / 30) + 1;
	    $args{day} = (($doy - 1) % 30) + 1;
	}
    } else {
	die "DateTime::Fiction::JRRTolkien::Shire: Invalid day given to from_day_of_year constructor.\n" if $doy > 365 or $doy < 1;
	if ($doy == 1) {
	    $args{holiday} = 1;
	} elsif ($doy == 182) {
	    $args{holiday} = 2;
	} elsif ($doy == 183) {
	    $args{holiday} = 3;
	} elsif ($doy == 184) {
	    $args{holiday} = 5;
	} elsif ($doy == 365) {
	    $args{holiday} = 6;
	} else {
	    $doy -= 3 if $doy > 184; # Lithe's and midyear's day
	    --$doy; # 2 Yule
	    $args{month} = int(($doy - 1) / 30) + 1;
	    $args{day} = (($doy - 1) % 30) + 1;
	}
    }

    return $class->new(%args);
} # end sub from_day_of_year

sub clone { bless { %{ $_[0] } }, ref $_[0] } # Stolen from DateTime.pm

# Get methods
sub year { 

tools/lib/DateTime/Fiction/JRRTolkien/Shire.pm  view on Meta::CPAN


sub wday { return $_[0]->day_of_week; }
sub dow { return $_[0]->day_of_week; }

sub day_name {
    my $self = shift;
    my @days = ('', 'Sterday', 'Sunday', 'Monday', 'Trewsday', 'Hevensday', 'Mersday', 'Highday');
    return $days[$self->day_of_week];
} # end sub day_name

sub day_name_trad {
    my $self = shift;
    my @days = ('', 'Sterrendei', 'Sunnendei', 'Monendei', 'Trewesdei', 'Hevenesdei', 'Meresdei', 'Highdei');
    return $days[$self->day_of_week];
} # end sub trad_day_name

sub holiday {
    my $self = shift;  
    $self->_recalc_Shire if $self->{recalc}; 
    return $self->{holiday};
} # end sub holiday

sub holiday_name {
    my $self = shift;
    my @holidays = ('', '2 Yule', '1 Lithe', "Midyear's day", 'Overlithe', '2 Lithe', '1 Yule');
    return $holidays[$self->holiday];
} # end sub holiday_name

sub is_leap_year { 
    my $self = shift;  
    $self->_recalc_Shire if $self->{recalc}; 
    return $self->{leapyear};
} # end is_leap_year

sub day_of_year {
    my $self = shift;
    my $yday;
    $self->_recalc_Shire if $self->{recalc};

    if ($self->{month}) { # holidays aren't part of any month
	$yday = 30 * ($self->{month} - 1) + $self->{day} + 1;  # + 1 is for the 2 Yule
	if ($self->{month} > 6) {
	    $yday += 3;
	    ++$yday if $self->{leapyear};
	}
    } else {
	if ($self->{leapyear}) {
	    $yday = (0, 1, 182, 183, 184, 185, 366)[$self->{holiday}];
	} else {
	    $yday = (0, 1, 182, 183, 0, 184, 365)[$self->{holiday}];
	}
    }

    return $yday;
} # end sub day_of_year

sub doy { return $_[0]->day_of_year };

sub week { return ($_[0]->week_year, $_[0]->week_number); }

sub week_year { return $_[0]->year; } # the shire calendar is nice this way

sub week_number {
    my $self = shift;
    my $yday = $self->day_of_year;

    183 == $yday
	and return 0;	# Midyear's day has no week number
    184 == $yday
	and $self->is_leap_year
	and return 0;	# The Overlithe has no week number either.

    --$yday if $yday > 182; # don't count Midyear's day
    --$yday if $yday > 182 and $self->is_leap_year; # don't count the Overlithe

    return int(($yday - 1) / 7) + 1;
} # end sub week_number

sub epoch { return $_[0]->{dt}->epoch; }
sub hires_epoch { return $_[0]->{dt}->hires_epoch; }
sub utc_rd_values { return $_[0]->{dt}->utc_rd_values; }
sub utc_rd_as_seconds { return $_[0]->{dt}->utc_rd_as_seconds; }

# Set methods

sub set {
    my ($self, %args) = @_;
    my (%dt_args, $arg, $itr);
    my @months = ('', 'Afteryule', 'Solmath', 'Rethe', 'Astron', 'Thrimidge', 'Forelithe', 'Afterlithe', 
		  'Wedmath', 'Halimath', 'Winterfilth', 'Blotmath', 'Foreyule');
    my @holidays = ('', '2 Yule', '1 Lithe', "Midyear's day", 'Overlithe', '2 Lithe', '1 Yule');
    $self->_recalc_Shire if $self->{recalc};

    if ($args{month}) {
	foreach $itr (1..12) {
	    $args{month} = $itr if $args{month} eq $months[$itr];
	}
    }
    if ($args{holiday}) {
	foreach $itr (1..6) {
	    $args{holiday} = $itr if $args{holiday} eq $holidays[$itr];
	}
    }

    $self->{year} = $args{year} || $self->{year};
    $self->{leapyear} = 0;
    $self->{leapyear} = 1 if $self->{year} % 4 == 0 and $self->{year} % 100 != 0;
    $self->{leapyear} = 1 if $self->{year} % 400 == 0;
    if ($self->{holiday}) {
	if ($args{holiday}) {
	    $self->{holiday} = $args{holiday};
	} else {
	    $self->{holiday} = 0;
	    $self->{month} = $args{month} || 1;
	    $self->{day} = $args{day} || 1;
	}
    } else {
	if ($args{holiday}) {
	    $self->{holiday} = $args{holiday};
	    $self->{month} = 0;
	    $self->{day} = 0;

tools/lib/DateTime/Fiction/JRRTolkien/Shire.pm  view on Meta::CPAN


=head1 SYNOPSIS

    use DateTime::Fiction::JRRTolkien::Shire;

    # Constructors
    my $shire = DateTime::Fiction::JRRTolkien::Shire->new(year => 1419,
                                                          month => 'Rethe',
                                                          day => 25);
    my $shire = DateTime::Fiction::JRRTolkien::Shire->new(year => 1419,
                                                          month => 3,
                                                          day => 25);
    my $shire = DateTime::Fiction::JRRTolkien::Shire->new(year => 1419,
                                                          holiday => '2 Lithe');

    my $shire = DateTime::Fiction::JRRTolkien::Shire->from_epoch(epoch = $time);
    my $shire = DateTime::Fiction::JRRTolkien::Shire->today; # same as from_epoch(epoch = time());

    my $shire = DateTime::Fiction::JRRTolkien::Shire->from_object(object => $some_other_DateTime_object);
    my $shire = DateTime::Fiction::JRRTolkien::Shire->from_day_of_year(year => 1420,
                                                                       day_of_year => 182);
    my $shire2 = $shire->clone;

    # Accessors
    $year = $shire->year;
    $month = $shire->month;            # 1 - 12, or 0 on a holiday
    $month_name = $shire->month_name;
    $day = $shire->day;                # 1 - 30, or 0 on a holiday

    $dow = $shire->day_of_week;        # 1 - 7, or 0 on certain holidays
    $day_name = $shire->day_name;

    $holiday = $shire->holiday;
    $holiday_name = $shire->holiday_name;

    $leap = $shire->is_leap_year;

    $time = $shire->epoch;
    @rd = $shire->utc_rd_values;

    # Set Methods
    $shire->set(year => 7463,
                month => 5,
                day => 3);
    $shire->set(year => 7463,
                holiday => 6);
    $shire->truncate(to => 'month');

    # Comparisons
    $shire < $shire2;
    $shire == $shire2;

    # Strings
    print "$shire1\n"; # Prints Sunday 25 Rethe 1419

    # On this date in history
    print $shire->on_date;

=head1 DESCRIPTION
 
Implementation of the calendar used by the hobbits in J.R.R. Tolkien's exceptional
novel The Lord of The Rings, as described in Appendix D of that book 
(except where noted).  The calendar has 12 months, each with 30 days, and 5
holidays that are not part of any month.  A sixth holiday, Overlithe, is added on 
leap years.  The holiday Midyear's Day (and the Overlithe on a leap year) is
not part of any week, which means that the year always starts on Sterday.

This module is a follow on to the Date::Tolkien::Shire module, and is rewritten
to support Dave Rolsky and company's DateTime module.  The DateTime module must
be installed for this module to work.  Unlike the DateTime module, which includes
time support, this calendar does not have any mechanisms for giving a shire 
time (mostly because I've never quite figured out what it should look like).
Time is maintained, however, so that objects can be converted from other
calendars to the shire calendar and then converted back without their time components
being lost.  The same is true of time zones.

=head1 METHODS

Most of these methods mimic their corresponding DateTime methods in functionality.
For additional information on these methods, see the DateTime documentation.

=over 4

=head2 Constructors

=item * new( ... )

This method takes a year, month, and day parameter, or a year and holiday parameter.
The year can be any value.  The month can be specified with a string giving the name
of the month (the same string that would be returned by month_name, with the first letter 
capitalized and the rest in lower case) or by giving the numerical value for the month,
between 1 and 12.  The day should always be between 1 and 30.  If a holiday is given 
instead of a day and month, it should be the name of the holiday as returned by 
holiday_name (with the first letter of each word capitalized) or a value between 1 
and 6.  The 1 through 6 numbers map to holidays as follows:
    1 => 2 Yule
    2 => 1 Lithe
    3 => Midyear's Day
    4 => Overlithe      # Leap years only
    5 => 2 Lithe
    6 => 1 Yule

The new method will also take parameters for hour, minute, second, nanosecond, time_zone
and locale.  If given, these parameters will be stored in case the object is converted to
another class that supports times.

If a day is not given, it will default to 1.  If neither a day or month is given,
the date will default to 2 Yule, the first day of the year.

=item * from_epoch( epoch => $epoch, ... )

Same as in DateTime.

=item * now( ... )

Same as in DateTime.  Note that this is equivalent to 
    from_epoch( epoch => time() );

=item * today( ... )

Same as in DateTime.  

=item * from_object( object => $object, ... )

Same as in DateTime.  Takes any other DateTime calendar object and converts it to
a DateTime::Fiction::JRRTolkien::Shire object.

=item * last_day_of_month( ... )

Same as in DateTime.  Like the new constructor, but it does not take a day parameter.  
Instead, the day is set to 30, which is the last day of any month in the shire 
calendar.  A holiday parameter should not be used with this method.  Use new instead.

=item * from_day_of_year( year => $year, day_of_year => $yday)

Same as in DateTime.  Gets the date from the given year and day of year, both
of which must be given.  Hour, minute, second, time_zone, etc. parameters
may also be given, and will be passed to the underlying DateTime object, just
like in new.

=item * clone

Creates a new Shire object that is the same date (and underlying time)
as the calling object.

=head2 Get Methods

=item * year

returns the year.

=item * month

Returns the month number, from 1 to 12.  If the date is a holiday, 
a 0 is returned for the month.

=item * month_name

Returns the name of the month.  If the date is a holiday, an empty
string is returned.

=item * day_of_month, day, mday

Returns the day of the current month, from 1 to 30.  If the date is a
holiday, 0 is returned.

=item * day_of_week, wday, dow

Returns the day of the week from 1 to 7.  If the day is not part of
any week (Midyear's Day or the Overlithe), 0 is returned.

=item * day_name

Returns the name of the day of the week, or an empty string if the
day is not part of any week.

=item * day_name_trad

Like day_name, but returns the more traditional name of the days
of the week, as defined in Appendix D.

=item * day_of year, doy

Returns the day of the year, from 1 to 366

=item * holiday

Returns the holiday number (given in the description of the new constructor).
If the day is not a holiday, 0 is returned.

=item * holiday_name

Returns the name of the holiday.  If the day is not a holiday, an empty string
is returned.

=item * is_leap_year

Returns 1 if the year is a leap year, and 0 otherwise.  

Leap years are given
the same rule as the Gregorian calendar.  Every four years is a leap year,
except the first year of the century, which is not a leap year.  However,
every fourth century (400 years), the first year of the century is a leap 
year (every 4, except every 100, except every 400).  This is a slight
change from the calendar descibed in Appendix D, which uses the rule of 
once every 4 years, except every 100 years (the same as in the Julian 
calendar).  Given some uncertainty about how many years have passed
since the time in Lord of the Rings (see note below), and the expectations
of most people that the years match up with what they're used to, I have
changed this rule for this implementation.  However, this does mean that 
this calendar implementation is not strictly that described in Appendix D.

=item * week

A two element array, where the first is the week_year and the latter is the week_number.

=item * week_year

This is always the same as the year in the shire calendar, but is present for
compatability with other DateTime objects.

=item * week_number

Returns the week of the year.

=item * epoch

Returns the epoch of the given object, just like in DateTime.

=item * hires_epoch

Returns the epoch as a floating point number, with the fractional portion
for fractional seconds.  Functions the same as in DateTime.

=item * utc_rd_values

Returns the UTC rata die days, seconds, and nanoseconds. Ignores fractional
seconds.  This is the standard method used by other methods to convert 
the shire calendar to other calendars.  See the DateTime documentation for
more information.

=item * utc_rd_as_seconds

Returns the UTC rata die days entirely as seconds.  

=item * on_date

Prints out the current day.  If the day has some events that transpired on it
(as defined in Appendix B of the Lord of the Rings), those events are also printed.
This can be fun to put in a .bashrc or .cshrc.  Try

    perl -MDateTime::Fiction::JRRTolkien::Shire 
      -le 'print DateTime::Fiction::JRRTolkien::Shire->now->on_date;'

=head2 Set Methods

=item * Set( ... )

Allows the day, month, and year to be changed.  It takes any parameters allowed
by new constructor, including all those supported by DateTime and the holiday parameter,
except for time_zone.  This is used in much the same way as new, with the 
exception that any parameters not given will be left as is.

All parameters are optional, with the current values inserted if the values are not
supplied.  However, with holidays not falling in any month, it is recommended
that a day and month always be given together.  Otherwise, unanticipated
results may occur.

As in the new constructor, time parameters have no effect on the shire dates 
returned.  However, they are maintained in case the object is converted to another
calendar which supports time.

=item * Truncate( ... )

Same as in DateTime.  If the date is a holiday, a truncation to either
'month' or 'day' is equivalent.  Otherwise, this functions as specified in the
DateTime object.

=item * set_time_zone( $tz )

Just like in DateTime.  This method has no effect on the shire calendar, but be
stored with the date if it is ever converted to another calendar with time support.

=head2 Comparisons and Stringification

All comparison operators should work, just as in DateTime.  In addition,
all DateTime::Fiction::JRRTolkien::Shire objects will interpolate into
a string representing the date when used in a double-quoted string.  

=back

=head1 DURATIONS AND DATE MATH

Durations and date math (other than comparisons) are not supported at present
on this module (patches are always welcome).  If this is needed, there are a couple
of options.  If workig with dates within epoch time, the dates can be converted
to epoch time, the math done, and then converted back.  Regardless of the dates,
the shire objects can also be converted to DateTime objects, the math done with
the DateTime class, and then the DateTime object converted back to a Shire object.

=head1 NOTE: YEAR CALCULATION

http://www.glyhweb.com/arda/f/fourthage.html references a letter sent by
Tolkien in 1958 in which he estimates approxiimately 6000 years have passed
since the War of the Ring and the end of the Third Age.  (Thanks to Danny
O'Brien from sending me this link).  I took this approximate as an exact amount
and calculated back 6000 years from 1958.  This I set as the start of the 
4th age (1422 S.R.).  Thus the fourth age begins in our B.C 4042.

According to Appendix D of the Lord of the Rings, leap years in hobbit
calendar are every 4 years unless its the turn of the century, in which
case it's not a leap year.  Our calendar (Gregorian) uses every 4 years unless it's 
100 years unless its 400 years.  So, if no changes have been made to 
the hobbit's calendar since the end of the third age, their calendar would
be about 15 days further behind ours now then when the War of the Ring took
place.  Implementing this seemed to me to go against Tolkien's general habit
of converting dates in the novel to our equivalents to give us a better
sense of time.  My thoughts, at least right now, is that it is truer to the
spirit of things for years to line up, and for Midyear's day to still be 
approximately on the summer solstice.  So instead, I have modified Tolkien's 
description of the hobbit 
calendar so that leap years occur once every 4 years unless it's 100
years unless it's 400 years, so as it matches the Gregorian calendar in that
regard.  These 100 and 400 year intervals occur at different times in
the two calendars, so there is not a one to one correspondence
of days regardless of years.  However, the variations follow a 400 year cycle.

=head1 AUTHOR

Tom Braun <tbraun@pobox.com>

=head1 LICENSE AND COPYRIGHT

Copyright (c) 2003 Tom Braun.  All rights reserved.  This program is
free software; you can redistribute it and/or modify it under the same
terms as Perl itself.
                                                                                
The calendar implemented on this module was created by J.R.R. Tolkien,
and the copyright is still held by his estate.  The license and 
copyright given herein applies only to this code and not to the 
calendar itself.
                                                                   
The full text of the license can be found in the LICENSE file included
with this module.

=head1 SUPPORT

Support is by the author. Please file bug reports at
L<https://github.com/trwyant/perl-DateTime-Fiction-JRRTolkien-Shire/issues> or in
electronic mail to the author.

=head1 BIBLIOGRAPHY

Tolkien, J. R. R. <i>Return of the King<i>.  New York: Houghton Mifflin Press,
1955.

http://www.glyphweb.com/arda/f/fourthage.html

=head1 SEE ALSO

The DateTime project documentation (perldoc DateTime, datetime@perl.org mailing list,
or http://datetime.perl.org/).

=cut

1;



( run in 0.787 second using v1.01-cache-2.11-cpan-364913b4093 )