package LJ;
use strict;
#
# name: LJ::days_in_month
# class: time
# des: Figures out the number of days in a month.
# args: month, year?
# des-month: Month
# des-year: Year. Necessary for February. If undefined or zero, function
# will return 29.
# returns: Number of days in that month in that year.
#
sub days_in_month
{
my ($month, $year) = @_;
if ($month == 2)
{
return 29 unless $year; # assume largest
if ($year % 4 == 0)
{
# years divisible by 400 are leap years
return 29 if ($year % 400 == 0);
# if they're divisible by 100, they aren't.
return 28 if ($year % 100 == 0);
# otherwise, if divisible by 4, they are.
return 29;
}
}
return ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$month-1]);
}
sub day_of_week
{
my ($year, $month, $day) = @_;
my $time = eval { Time::Local::timelocal(0,0,0,$day,$month-1,$year) };
return undef if $@;
return (localtime($time))[6];
}
#
# class: time
# name: LJ::http_to_time
# des: Converts HTTP date to Unix time.
# info: Wrapper around HTTP::Date::str2time.
# See also [func[LJ::time_to_http]].
# args: string
# des-string: HTTP Date. See RFC 2616 for format.
# returns: integer; Unix time.
#
sub http_to_time {
my $string = shift;
return HTTP::Date::str2time($string);
}
sub mysqldate_to_time {
my ($string, $gmt) = @_;
return undef unless $string =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d)(?::(\d\d))?)?$/;
my ($y, $mon, $d, $h, $min, $s) = ($1, $2, $3, $4, $5, $6);
my $calc = sub {
$gmt ?
Time::Local::timegm($s, $min, $h, $d, $mon-1, $y) :
Time::Local::timelocal($s, $min, $h, $d, $mon-1, $y);
};
# try to do it. it'll die if the day is bogus
my $ret = eval { $calc->(); };
return $ret unless $@;
# then fix the day up, if so.
my $max_day = LJ::days_in_month($mon, $y);
$d = $max_day if $d > $max_day;
return $calc->();
}
#
# class: time
# name: LJ::time_to_http
# des: Converts a Unix time to a HTTP date.
# info: Wrapper around HTTP::Date::time2str to make an
# HTTP date (RFC 1123 format) See also [func[LJ::http_to_time]].
# args: time
# des-time: Integer; Unix time.
# returns: String; RFC 1123 date.
#
sub time_to_http {
my $time = shift;
return HTTP::Date::time2str($time);
}
#
# name: LJ::time_to_cookie
# des: Converts Unix time to format expected in a Set-Cookie header.
# args: time
# des-time: unix time
# returns: string; Date/Time in format expected by cookie.
#
sub time_to_cookie {
my $time = shift;
$time = time() unless defined $time;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
$year+=1900;
my @day = qw{Sunday Monday Tuesday Wednesday Thursday Friday Saturday};
my @month = qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec};
return sprintf("$day[$wday], %02d-$month[$mon]-%04d %02d:%02d:%02d GMT",
$mday, $year, $hour, $min, $sec);
}
# http://www.w3.org/TR/NOTE-datetime
# http://www.w3.org/TR/xmlschema-2/#dateTime
sub time_to_w3c {
my ($time, $ofs) = @_;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
$mon++;
$year += 1900;
$ofs =~ s/([\-+]\d\d)(\d\d)/$1:$2/;
$ofs = 'Z' if $ofs =~ /0000$/;
return sprintf("%04d-%02d-%02dT%02d:%02d:%02d$ofs",
$year, $mon, $mday,
$hour, $min, $sec);
}
#
# name: LJ::mysql_time
# des:
# class: time
# info:
# args:
# des-:
# returns:
#
sub mysql_time
{
my ($time, $gmt) = @_;
$time ||= time();
my @ltime = $gmt ? gmtime($time) : localtime($time);
return sprintf("%04d-%02d-%02d %02d:%02d:%02d",
$ltime[5]+1900,
$ltime[4]+1,
$ltime[3],
$ltime[2],
$ltime[1],
$ltime[0]);
}
#
# name: LJ::alldatepart_s1
# des: Gets date in MySQL format, produces s1dateformat.
# class: time
# args:
# des-:
# info: s1 dateformat is: "%a %W %b %M %y %Y %c %m %e %d %D %p %i %l %h %k %H"
# Sample string: Tue Tuesday Sep September 03 2003 9 09 30 30 30th AM 22 9 09 9 09.
# Thu Thursday Oct October 03 2003 10 10 2 02 2nd AM 33 9 09 9 09
# returns:
#
sub alldatepart_s1
{
my $time = shift;
my ($sec,$min,$hour,$mday,$mon,$year,$wday) =
gmtime(LJ::mysqldate_to_time($time, 1));
my $ret = "";
$ret .= LJ::Lang::day_short($wday+1) . " " .
LJ::Lang::day_long($wday+1) . " " .
LJ::Lang::month_short($mon+1) . " " .
LJ::Lang::month_long($mon+1) . " " .
sprintf("%02d %04d %d %02d %d %02d %d%s ",
$year % 100, $year + 1900, $mon+1, $mon+1,
$mday, $mday, $mday, LJ::Lang::day_ord($mday));
$ret .= $hour < 12 ? "AM " : "PM ";
$ret .= sprintf("%02d %d %02d %d %02d", $min,
($hour+11)%12 + 1,
($hour+ 11)%12 +1,
$hour,
$hour);
return $ret;
}
#
# name: LJ::alldatepart_s2
# des: Gets date in MySQL format, produces s2dateformat.
# class: time
# args:
# des-:
# info: s2 dateformat is: yyyy mm dd hh mm ss day_of_week
# returns:
#
sub alldatepart_s2
{
my $time = shift;
my ($sec,$min,$hour,$mday,$mon,$year,$wday) =
gmtime(LJ::mysqldate_to_time($time, 1));
return
sprintf("%04d %02d %02d %02d %02d %02d %01d",
$year+1900,
$mon+1,
$mday,
$hour,
$min,
$sec,
$wday);
}
#
# name: LJ::statushistory_time
# des: Convert a time like "20070401120323" to "2007-04-01 12:03:23".
# class: time
# args:
# des-:
# info: Only [dbtable[statushistory]] currently formats dates like this.
# returns:
#
sub statushistory_time {
my $time = shift;
$time =~ s/(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/$1-$2-$3 $4:$5:$6/;
return $time;
}
#
# class: time
# name: LJ::ago_text
# des: Converts integer seconds to English time span
# info: Turns a number of seconds into the largest possible unit of
# time. "2 weeks", "4 days", or "20 hours".
# returns: A string with the number of largest units found
# args: secondsold
# des-secondsold: The number of seconds from now something was made.
#
sub ago_text
{
my $secondsold = shift;
return $BML::ML{'time.ago.never'} unless $secondsold > 0;
my $num;
my $unit;
if ($secondsold >= 60*60*24*7) {
$num = int($secondsold / (60*60*24*7));
return LJ::Lang::ml('time.ago.week', {'num' => $num});
} elsif ($secondsold >= 60*60*24) {
$num = int($secondsold / (60*60*24));
return LJ::Lang::ml('time.ago.day', {'num' => $num});
} elsif ($secondsold >= 60*60) {
$num = int($secondsold / (60*60));
return LJ::Lang::ml('time.ago.hour', {'num' => $num});
} elsif ($secondsold >= 60) {
$num = int($secondsold / (60));
return LJ::Lang::ml('time.ago.minute', {'num' => $num});
} else {
$num = $secondsold;
return LJ::Lang::ml('time.ago.second', {'num' => $num});
}
}
# Given a year, month, and day; calculate the age in years compared to now. May return a negative number or
# zero if called in such a way as would cause those.
sub calc_age {
my ($year, $mon, $day) = @_;
$year += 0; # Force all the numeric context, so 0s become false.
$mon += 0;
$day += 0;
my ($cday, $cmon, $cyear) = (gmtime)[3,4,5];
$cmon += 1; # Normalize the month to 1-12
$cyear += 1900; # Normalize the year
return unless $year;
my $age = $cyear - $year;
return $age unless $mon;
# Sometime this year they will be $age, subtract one if we haven't hit their birthdate yet.
$age -= 1 if $cmon < $mon;
return $age unless $day;
# Sometime this month they will be $age, subtract one if we haven't hit their birthdate yet.
$age -= 1 if ($cday < $day && $cmon == $mon);
return $age;
}
1;