This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE 

Replace localtime() and gmtime() with date()

=head1 VERSION

   Maintainer: Nathan Wiger <[EMAIL PROTECTED]>
   Date: 05 Aug 2000
   Version: 1
   Status: developing
   Mailing List: [EMAIL PROTECTED]
   Number: 48

=head1 ABSTRACT

Currently, Perl uses the C library C<localtime()> and C<gmtime()>
functions for date access. However, because of many problems, these
should be replaced with a single new functions, C<date()>, that will
be called as follows:

   $scalar  =  date;     # scalar ctime date, same as current
   @array   =  date;     # array of date/time values (new order)
   %hash    =  date;     # hash of date/time values (new)
   $object  =  date;     # object with accessor functions (new)

The new C<date()> function will return time adjusted for the local
timezone. To get UTC (or GMT, for nostalgic types) time, you simply pass
an argument to C<date()>.

=head1 DESCRIPTION

=head2 Overview

In the past, Perl has provided access to date and time information
through the C library C<localtime()> and C<gmtime()> functions.
Unfortunately, these functions have several "really bad things" about
them:

   1. some values are 0-indexed while others are 1-indexed
   2. they return massive lists as their only interface
   3. minute and second values aren't 0-padded
   4. the year has to have 1900 added to it to be correct.

While some of these are perhaps understandable, that last one just plain
doesn't make I<any> sense.

With Perl 6 we have the chance to fix this. While some have suggested
merely changing what C<localtime()> and C<gmtime()> return, this is a
bad idea because:

   1. Many Perl scripts already use it
   2. Many C programmers are familiar with its stupidity

As such, we should replace these functions with versions that work the
way that we want them to. The new names will indicate that these are
"not the same old time functions", and that you'd better read the docs.

=head2 Proposed Syntax

The new function would have several different, highly-flexible calling
forms. In addition, it allows full-featured date arithmetic (currently
this requires Time::Object, Date::Calc, or some other module).

The new syntax of C<date()> is proposed as:

   $|@|%return  =  date [ $time ], [ $format ], [ $timezone ];

If C<$time> is not specified, then the current return value from
C<time()> is used (just like C<localtime()>).

The optional C<$format> affects the format of the date returned.
Actually, what it does is call the object function C<format()> with the
format, which in turn determines what calls to the C<date()> member
function look like. See below.

The C<$format> specifier is the same one used in C<POSIX strftime()>,
allowing complete date formatting with having to import the C<POSIX>
module.

The C<$time> specifier can be followed by a C<$timezone> argument,
which returns the date relative to that timezone. By default, the time
is returned relative to the local timezone. You can get UTC, for
example, by specifying C<UTC> or C<GMT> as the timezone.

Depending on the context within which C<date()> is called, many
different things are returned:

   # Get some various local date stuff
   $scalar  =  date;            # scalar ctime date, same as now
   $scalar  =  date time, '%m/%d/%Y';  # scalar date per strftime
   @array   =  date;            # array of date/time values
   %hash    =  date;            # hash of values (%Y is ignored!)
   $object  =  date $time;      # object with accessor functions,
                                # relative to time $time

   # Access UTC information
   $scalar  =  date time, '%H:%M', 'UTC';  # return time in UTC
   $object  =  date time, undef, 'GMT';    # return object in UTC

   # Explicity get ctime date for Eastern US time
   # If $time is undef, time() is assumed
   $scalar  =  date undef, undef, 'EST';

=head2 Alternate Syntax

The initial syntax I was going to propose would involve two functions:

        date [ $time ], [ $format ]
     utcdate [ $time ], [ $format ]

This is similar to above, only there are two possible timezones and
therefore no timezone argument. Everything else remains the same as the
specification above. I chose to use 'utc' instead of 'gmt' because it's
time to catch up with standards (and "UTC" is the "official" name of
univeral time). 

I abandoned this in favor of the more flexible single C<date()> function
(which gives you access to any timezone). However, it may be more
appropriate to use these two separate functions.

=head2 Return Values

The return values are dependent on the context within which C<date()> is
called. For all contexts, the following are the values and their ranges:

   $hour  =  0 .. 24  
   $min   =  00 .. 59   # 0-padded
   $sec   =  00 .. 59   # 0-padded
   $msec  =  0 .. 999  
   $nsec  =  0 .. 10**9 # nanoseconds, unless Gisle needs more! :-)

   $mon   =  1 .. 12    # hooray!
   $mday  =  1 .. 31 
   $year  =  1 .. 9999  # 4-digit! 

   $wday  =  1 .. 7     # 1 == Sunday, 7 == Saturday
   $yday  =  1 .. 366

   $isdst =  1 or undef;  # daylight savings?
   $isutc =  1 or undef;  # is UTC? (see below...)
   $tz    =  PST|UTC|...; # timezone

I'm still a little uneasy about C<$wday> starting with 1 == Sunday, just
because Monday seems like the first day of the week to me. But I'm not
the one writing the calendars, and it seems silly to only have one value
that's 0-indexed. Oh, well. :-)

=head3 SCALAR Context

A single ctime-formatted date string is returned, consistent with the
current usage of C<localtime()>. Unless the optional C<$format> is
included, in which case the format of this string is changed
accordingly.

=head3 LIST Context

A list of date/time values is returned. The ordering and format of these
values has been radically changed to reflect what most of us probably
view as "ordinary":

   ($mday, $mon, $year, $hour, $min, $sec, $msec, $nsec,
    $wday, $yday, $isdst, $isutc, $tz) = date;

This ordering is very easy to remember, in my opinion, since it reflects
usual time conventions (except in the US, but we're pretty backwards
with Month/Day/Year, actually). An easy memory aid is thinking of a time
stamp:

    28/11/2000 12:03:09

Since the first 6 fields are probably mostly what you'd pull out, this
works pretty well.

This ordering can probably be debated ad nauseum, and maybe it should.
In particular, I suspect there will be a big todo over C<$mday> before
C<$mon>, which it may deserve. 

=head3 HASH Context

A hash of all the above values is returned. These are named the same as
the variables above. So,

   %date = date;

Would return a hash with these values (assuming the same date shown
above):

   $date{mday} = 28;
   $date{mon}  = 11;
   $date{year} = 2000;
   $date{hour} = 12;
   $date{min}  = 03;    
   $date{sec}  = 09;
   $date{msec} = 143; 
   $date{nsec} = [insert ridiculous value here];    # :-)
   $date{wday} = 2; 
   $date{yday} = 331;
   $date{isdst}= undef;
   $date{isutc}= undef;
   $date{tz}   = 'PST';

I'd love to rename 'mday' to just 'day', but this will cause too much
confusion. Especially when you consider...

=head3 OBJECT Context

First, as an aside, we need to find a way to differentiate between
B<general> C<SCALAR> and C<OBJECT> contexts. Damian's excellent RFC on a
new C<want()> goes a long way, but it doesn't address everything.
However, I'll assume this is going to be worked out, because I'm sure it
will be.

In an C<OBJECT> context, the following is returned. This is shamelessly
adapted from C<Time::Object> and Larry's own comments:

   my object $t = date;  # create date object, hypothetical syntax

   $t->sec               # also available as $t->second
   $t->min               # also available as $t->minute
   $t->hour
   $t->mday              # also available as $t->day_of_month
   $t->mon               # based at 1
   $t->_mon              # based at 0
   $t->monname           # February
   $t->month             # same as $t->monname
   $t->year              # based at 0 (year 0 AD is, of course 1 BC).
   $t->_year             # year minus 1900
   $t->yr                # 2 digit year
   $t->wday              # based at 1 = Sunday
   $t->_wday             # based at 0 = Sunday
   $t->day_of_week       # based at 0 = Sunday
   $t->wdayname          # Tuesday
   $t->day               # same as wdayname
   $t->yday              # also available as $t->day_of_year
   $t->isdst             # also available as $t->daylight_savings
   $t->hms               # 01:23:45
   $t->ymd               # 2000/02/29
   $t->mdy               # 02/29/2000
   $t->dmy               # 29/02/2000
   $t->date              # Tue Feb 29 01:23:45 2000 (but depends on 
                         # the format set by $t->format)
   "$t"                  # same as $t->date
   $t->epoch             # seconds since the epoch
   $t->tzoffset          # timezone offset in a Time::Seconds object

The C<$t->strftime(FORMAT)> function has instead been replaced with:

   $t->format(FORMAT)    # set the format which determines what calls
                         # to $t->date (stringified $t) look like

In addition, we should add:

   $t->isutc             # in UTC time?
   $t->tz                # timezone (also $t->timezone)

With the flexibility of C<date()> being able to return any timezone,
these last two give us the ability to tell where we are.

=head2 Date Arithmetic

Date arithmetic can only be done on date objects. C<SCALAR> date
arithmetic must be performed via C<Date::Calc> or some other module.

For date arithmetic, we use C<Time::Object>'s method of overloading -.
This is the simplest thing to do, and easy to understand. However,
rather than changing types, all of these operations should return the
same C<date()> object:

   $t1 - $t2;      # returns date() object
   $t1 - 42;       # returns date() object
   $t1 + 533;      # returns date() object
   $t1 + $t2;      # ??????

I'm not sure why you would do the last one. With the current
C<Time::Object> it causes a runtime error, though, so if we want to
support it we'll have to see how feasible it is.

=head1 IMPLEMENTATION

Slash and burn C<localtime()> and C<gmtime()>. Actually, we should
probably move them to C<Time::Local> or some other place and keep them
available, since some people like pain. :-)

The C<$tz> variable might seem hard at first. It's not. There are only 2
possible values:

   1. UTC or other timezone (user specified)
   2. Local timezone (none specified)

So, all that would have to happen is C<date()> would have to store the
correct value in the return object, in the precendence above.

Extensive date calculations, including reverse date calculations, should
be left to an external module.

=head1 REFERENCES

Matt Sergeant's great Time::Object CPAN module
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2000-01/msg00241.html
Jonathan Scott Duff, Tim Jenness, and perl6-language for their input

Reply via email to