
=encoding utf8

=head1 TITLE

DRAFT: Synopsis 32: Setting Library - Temporal

=head1 AUTHORS

    The authors of the related Perl 5 docs
    Rod Adams <rod@rodadams.net>
    Larry Wall <larry@wall.org>
    Aaron Sherman <ajs@ajs.com>
    Mark Stosberg <mark@summersault.com>
    Carl Mäsak <cmasak@gmail.com>
    Moritz Lenz <moritz@faui2k3.org>
    Tim Nelson <wayland@wayland.id.au>
    Daniel Ruoso <daniel@ruoso.com>
    Dave Rolsky <autarch@urth.org>

=head1 VERSION

    Created: 19 Mar 2009 extracted from S29-functions.pod and S16-IO.pod
    Last Modified: 23 Feb 2009
    Version: 3

The document is a draft.

If you read the HTML version, it is generated from the pod in the pugs
repository under /docs/Perl6/Spec/S32-setting-library/Temporal.pod so edit it there in
the SVN repository if you would like to make changes.

=head2 Time

=over

=item gmtime

 our Temporal::DateTime multi gmtime ( Num $epoch )
 our Temporal::DateTime multi gmtime ( Rat $epoch = time() )

Identical to:

 Time::localtime(:$time,:tz<GMT>)

=item localtime

 our Temporal::DateTime multi localtime ( Num $epoch )
 our Temporal::DateTime multi localtime ( Rat $epoch = time() )

These functions take an epoch value and return a C<Temporal::DateTime>
object. For C<localtime> the time zone is taken from the local
system. For C<gmtime> the time zone is aways UTC.

If no time is provided, the current time is used.

=item time

 our Rat time()

Returns an epoch value for the current time.

=back

=head1 Roles

The intent behind these classes is to provide an absolutely minimal,
but still useful, set of core behavior. The assumption is that the
core will ship with a simple set of classes so that C<gmtime> and
C<localtime> have something to return.

=head2 Temporal::Date

You probably want to use the Temporal::DateTime object instead.

    role Temporal::Date {
        my subset Month of Int where { 1 <= $^a <= 12 };
        my subset Day of Int where { 1 <= $^a <= 31 };
        my subset DayOfWeek of Int where { 1 <= $^a <= 7 };

        has Int   $.year;
        has Month $.month = 1;
        has Day   $.day = 1;

        # This can be cached internally, but it's a calculated value,
        # not an attribute.
        our DayOfWeek method day-of-week ();

        # These always return the long English names
        our Str method month-name () ; # "January"
        our Str method day-name ();   # "Tuesday"

        # returns the date formatted in ISO8601 style - 2008-01-25
        our Str method iso8601 () {
            [ self.year, self.month, self.date ].join('-');
        }

        method Str { self.iso8601 };

        multi method infix:{'<=>'} (Temporal::Date $self, Temporal::Date $other) {
            $self.year <=> $other.year
            ||
            $self.month <=> $other.month
            ||
            $self.day <=> $other.day;
        }
    }

Example:

$date = Date.new( :year(2008), :month(1), :day(25) );
$date.month(); # 1

=head2 Temporal::Time

You probably want to use the Temporal::DateTime object instead.

    role Temporal::Time {
        my subset Hour   of Int where { 0 <= $^a <= 23 };
        my subset Minute of Int where { 0 <= $^a <= 59 };
        my subset Second of Num where { 0 <= $^a <= 60 };

        has Hour   $.hour = 0;
        has Minute $.minute = 0;
        has Second $.second = 0;

        our Str method iso8601 ()
            { [ self.hour, self.minute, self.second ].join(':') }

        method Str { self.iso8601() };

        multi method infix:{'<=>'} (Temporal::Time $self, Temporal::Time $other) {
            $self.hour <=> $other.hour
            ||
            $self.minute <=> $other.minute
            ||
            $self.second <=> $other.second
        }
    }

=head2 Temporal::TimeZone::Observance

role    Temporal::TimeZone::Observance {
        my subset Offset of Int where { -86400 < $^a < 86400 };

        has Offset $.offset;
        has Bool   $.isdst;
        has Str    $.abbreviation; # CST, AST

        # The ISO8601 standard does not allow for offsets with
        # sub-minute resolutions. In real-world practice, this is not
        # an issue.
        our Str method iso8601 {
            my $hours = self.offset.abs / 3600;
            my $minutes = self.offset.abs % 3600;

            return self.offset < 0 ?? '-' :: '+'
                   ~ $hours.fmt('%02d')
                   ~ $minutes.truncate.fmt('%02d');
        }

        method Str { self.iso8601 }
}

This is called an I<observance> because it represents the state of a
time zone for a given instant. A real Temporal::TimeZone role would
return an observance when given a particular datetime.

We don't specify a proper C<Temporal::TimeZone> role because time
zones are messy and complex. The system libraries are able to give us
sufficient information to create an observance for a time, but are not
able to give us proper time zone information.

=head2 Temporal::DateTime

    role Temporal::DateTime {
        has Temporal::Date $!date handles <year month day day-of-week>;
        has Temporal::Time $!time handles <hour minute second fractional-second>;
        has Temporal::TimeZone::Observance $!timezone handles <offset isdst>;

        our Str method iso8601 () {
            self.date.iso8601 ~ 'T' ~ self.time.iso8601 ~ self.timezone.iso8601;
        }

        method Str { self.iso8601 }

        # This involves a whole bunch of code - see Perl 5's
        # Time::Local
        our Num method epoch { ... }

        method Int { self.epoch.truncate }

        method Num { self.epoch }
    }

=head1 Additions

Please post errors and feedback to perl6-language.  If you are making
a general laundry list, please separate messages by topic.

