#-*- perl -*-
#
# Copyright (C) 2000 Ken'ichi Fukamachi
#
# $Id$
# $FML$
#

package FML::Date;

=head1 NAME

FML::Date - utilities for date and time 

=head1 SYNOPSIS


   use FML::Date;
   $date = new FML::Date time;

   # get the date in $style format
   $date->{ log_file_style }
   $date->log_file_style

=head1 DESCRIPTION

The style you use follows:

    style                       example
    ----------------------------------------------
    log_file_style              01/01/07 21:06:19
    mail_header_style           Sun, 7 Jan 2001 21:06:19 +0900  
    YYYYMMDD                    20010107
    current_time                200101072106
    precise_current_time        20010107210619


=head1 METHOD

You can also method like $date->$style() style.

=item    log_file_style()

=item    mail_header_style()

=item    YYYYMMDD()

=item    current_time()

=item    precise_current_time()

=item    stardate()

return STAR TREK stardate.

=cut

require Exporter;
use vars qw($TimeZone);
@ISA = qw(Exporter);

use strict;
use Carp;


sub new
{
    my ($class, $time) = @_;
    my $type = _date($time);
    return bless $type, $class;
}


sub _date
{
    my ($time) = @_;
    my ($date) = {};

    # use the current UTC if $time is not given.
    $time ||= time;
	
    my @WDay  = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
    my @Month = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
		 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

    $TimeZone ||= '+0900';
    my ($sec,$min,$hour,$mday,$mon,$year,$wday) = (localtime($time))[0..6];

    $date->{'log_file_style'} = 
	sprintf("%02d/%02d/%02d %02d:%02d:%02d", 
		($year % 100), $mon + 1, $mday, $hour, $min, $sec);

    $date->{'mail_header_style'} = 
	sprintf("%s, %d %s %d %02d:%02d:%02d %s", 
		$WDay[$wday], $mday, $Month[$mon], 
		1900 + $year, $hour, $min, $sec, $TimeZone);

    $date->{'YYYY'} = sprintf("%04d", 1900 + $year);
    $date->{'MM'}   = sprintf("%02d", $mon + 1);
    $date->{'DD'}   = sprintf("%02d", $mday);

    $date->{'YYYYMMDD'} = 
	sprintf("%04d%02d%02d", 1900 + $year, $mon + 1, $mday);

    $date->{'current_time'} = 
	sprintf("%04d%02d%02d%02d%02d", 
		1900 + $year, $mon + 1, $mday, $hour, $min);

    $date->{'precise_current_time'} = 
	sprintf("%04d%02d%02d%02d%02d%02d", 
		1900 + $year, $mon + 1, $mday, $hour, $min, $sec);

    return $date;
}


sub log_file_style
{
    my ($self, $time) = @_;
    my $p = _date($time || time);
    $p->{'log_file_style'};
}


sub mail_header_style
{
    my ($self, $time) = @_;
    my $p = _date($time || time);
    $p->{'mail_header_style'};
}


sub YYYYMMDD
{
    my ($self, $time) = @_;
    my $p = _date($time || time);
    $p->{'YYYYMMDD'};
}


sub YYYYxMMxDD
{
    my ($self, $time, $sep) = @_;
    my $date = _date($time || time);

    $sep ||= '/'; # 1999/09/13 by default
    $date->{ YYYY }. $sep . $date->{ MM }. $sep. $date->{ DD };
}


sub current_time
{
    my ($self, $time) = @_;
    my $p = _date($time || time);
    $p->{'current_time'};
}


sub precise_current_time
{
    my ($self, $time) = @_;
    my $p = _date($time || time);
    $p->{'precise_current_time'};
}

# Descriptions: return Star Trek stardate()
#                  stardate(tm, issue, integer, fraction)
#                           unsigned long tm;
#                           long *issue, *integer, *fraction;
#    Arguments: $self $args
# Side Effects: 
# Return Value: stardate
sub stardate
{
    my ($self, $args) = @_;
    my ($issue, $integer, $fraction);

    # It would be convenient to calculate the fractional part with
    # *fraction = ( (tm%17280) *1000000) / 17280;
    # but the long int type may not be long enough for this (it requires 36
    # bits).  Cancelling the 1000000 with the 17280 gives an expression that
    # takes only 27 bits.

    $fraction = int (    (((time % 17280) * 3125) / 54)   );

    # Get integer part.
    $integer = time / 17280 + 9350;

    # At this stage, *integer contains the issue number in the obvious place,
    # biased to always be non-negative.  The issue number can be extracted by
    # simply dividing *integer by 10000 and offsetting it appropriately:

    $issue = int($integer / 10000) - 36;
    
    # Remove the issue number from *integer.

    $integer = $integer % 10000;

    sprintf("[%d]%04d.%02.2s", $issue, $integer, $fraction);
}


1;
