squirrelmail/functions/date.php
2025-01-03 11:47:03 -08:00

479 lines
12 KiB
PHP

<?php
/**
* date.php
*
* Takes a date and parses it into a usable format. The form that a
* date SHOULD arrive in is:
* <Tue,> 29 Jun 1999 09:52:11 -0500 (EDT)
* (as specified in RFC 822) -- 'Tue' is optional
*
* @copyright 1999-2025 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id$
* @package squirrelmail
* @subpackage date
*/
/**
* dependency information
* - none
*/
/**
* Corrects a time stamp to be the local time.
*
* @param int stamp the timestamp to adjust
* @param string tzc the timezone correction
* @return int the corrected timestamp
*/
function getGMTSeconds($stamp, $tzc) {
/* date couldn't be parsed */
if ($stamp == -1) {
return -1;
}
/* timezone correction, expressed as `shhmm' */
switch($tzc)
{
case 'Pacific':
case 'PST':
$tzc = '-0800';
break;
case 'Mountain':
case 'MST':
case 'PDT':
$tzc = '-0700';
break;
case 'Central':
case 'CST':
case 'MDT':
$tzc = '-0600';
break;
case 'Eastern':
case 'EST':
case 'CDT':
$tzc = '-0500';
break;
case 'EDT':
$tzc = '-0400';
break;
case 'GMT':
case 'UTC':
$tzc = '+0000';
break;
case 'BST':
case 'MET':
case 'CET':
$tzc = '+0100';
break;
case 'EET':
case 'IST':
case 'MET DST':
case 'METDST':
case 'MEST':
case 'CEST':
$tzc = '+0200';
break;
case 'HKT':
$tzc = '+0800';
break;
case 'JST':
case 'KST':
$tzc = '+0900';
break;
}
$neg = false;
if (preg_match('/^([+-]?)(\d\d)(\d\d)$/', $tzc, $matches)) {
if ($matches[1] === '-')
$neg = true;
$hh = $matches[2];
$mm = $matches[3];
} else {
// anything not listed above and not in the form +0400
// defaults to UTC
$hh = $mm = 0;
}
$iTzc = ($hh * 60 + $mm) * 60;
if ($neg) $iTzc = -1 * (int) $iTzc;
/* stamp in gmt */
$stamp -= $iTzc;
/* now find what the server is at */
$current = date('Z', time());
/* stamp in local timezone */
$stamp += $current;
return $stamp;
}
/**
* Returns the (localized) string for a given day number.
* Switch system has been intentionaly chosen for the
* internationalization of month and day names. The reason
* is to make sure that _("") strings will go into the
* main po.
*
* @param int day_number the day number
* @return string the day in human readable form
*/
function getDayName( $day_number ) {
switch( $day_number ) {
case 0:
$ret = _("Sunday");
break;
case 1:
$ret = _("Monday");
break;
case 2:
$ret = _("Tuesday");
break;
case 3:
$ret = _("Wednesday");
break;
case 4:
$ret = _("Thursday");
break;
case 5:
$ret = _("Friday");
break;
case 6:
$ret = _("Saturday");
break;
default:
$ret = '';
}
return( $ret );
}
/**
* Like getDayName, but returns the short form
* @param int day_number the day number
* @return string the day in short human readable form
*/
function getDayAbrv( $day_number ) {
switch( $day_number ) {
case 0:
$ret = _("Sun");
break;
case 1:
$ret = _("Mon");
break;
case 2:
$ret = _("Tue");
break;
case 3:
$ret = _("Wed");
break;
case 4:
$ret = _("Thu");
break;
case 5:
$ret = _("Fri");
break;
case 6:
$ret = _("Sat");
break;
default:
$ret = '';
}
return( $ret );
}
/**
* Returns the (localized) string for a given month number.
*
* @param string month_number the month number (01..12)
* @return string the month name in human readable form
*/
function getMonthName( $month_number ) {
switch( $month_number ) {
case '01':
$ret = _("January");
break;
case '02':
$ret = _("February");
break;
case '03':
$ret = _("March");
break;
case '04':
$ret = _("April");
break;
case '05':
$ret = _("May");
break;
case '06':
$ret = _("June");
break;
case '07':
$ret = _("July");
break;
case '08':
$ret = _("August");
break;
case '09':
$ret = _("September");
break;
case '10':
$ret = _("October");
break;
case '11':
$ret = _("November");
break;
case '12':
$ret = _("December");
break;
default:
$ret = '';
}
return( $ret );
}
/**
* Returns the (localized) string for a given month number,
* short representation.
*
* @param string month_number the month number (01..12)
* @return string the shortened month in human readable form
*/
function getMonthAbrv( $month_number ) {
switch( $month_number ) {
case '01':
$ret = _("Jan");
break;
case '02':
$ret = _("Feb");
break;
case '03':
$ret = _("Mar");
break;
case '04':
$ret = _("Apr");
break;
case '05':
$ret = _("Ma&#121;");
break;
case '06':
$ret = _("Jun");
break;
case '07':
$ret = _("Jul");
break;
case '08':
$ret = _("Aug");
break;
case '09':
$ret = _("Sep");
break;
case '10':
$ret = _("Oct");
break;
case '11':
$ret = _("Nov");
break;
case '12':
$ret = _("Dec");
break;
default:
$ret = '';
}
return( $ret );
}
/**
* Returns the localized representation of the date/time.
*
* @param string date_format The format for the date, like the input for the PHP date() function.
* @param int stamp the timestamp to convert
* @return string a full date representation
*/
function date_intl( $date_format, $stamp ) {
$ret = str_replace( array('D','F','l','M'), array('$1','$2','$3','$4'), $date_format );
// to reduce the date calls we retrieve m and w in the same call
$ret = date('w#m#'. $ret, $stamp );
// extract day and month in order to replace later by intl day and month
$aParts = explode('#',$ret);
$ret = str_replace(array('$1','$4','$2','$3',),
array(getDayAbrv($aParts[0]),
getMonthAbrv($aParts[1]),
getMonthName($aParts[1]),
getDayName($aParts[0])),
$aParts[2]);
return( $ret );
}
/**
* This returns a date of the format "Wed, Oct 29, 2003 9:52 am",
* or the same in 24H format (depending on the user's settings),
* and taking localization into accout.
*
* @param int stamp the timestamp
* @param string fallback string to use when stamp not valid
* @return string the long date string
*/
function getLongDateString( $stamp, $fallback = '' ) {
global $hour_format;
if ($stamp == -1) {
return $fallback;
}
if ( $hour_format == SMPREF_TIME_12HR ) {
$date_format = _("D, F j, Y g:i a");
} else {
$date_format = _("D, F j, Y H:i");
}
return( date_intl( $date_format, $stamp ) );
}
/**
* Returns a short representation of the date,
* taking timezones and localization into account.
* Depending on user's settings, this string can be
* of the form: "14:23" or "Jun 14, 2003" depending
* on whether the stamp is "today" or not.
*
* @param int $stamp The timestamp
* @param boolean $return_full_date_and_time When TRUE,
* ignore all
* user settings
* and use full
* date and time
* (OPTIONAL;
* default FALSE)
* @return string the date string
*/
function getDateString( $stamp, $return_full_date_and_time=FALSE ) {
global $invert_time, $hour_format, $show_full_date, $custom_date_format;
if ( $stamp == -1 ) {
return '';
}
$now = time();
$dateZ = date('Z', $now );
// FIXME: isn't this obsolete and introduced as a terrible workaround
// for bugs at other places which are fixed a long time ago?
if ($invert_time) {
$dateZ = - $dateZ;
}
// calculate when it was midnight and when it will be,
// in order to display dates differently if they're 'today'
$midnight = $now - ($now % 86400) - $dateZ;
// this is to correct if after calculations midnight is more than
// one whole day away.
if ($now - $midnight > 86400) {
$midnight += 86400;
}
$nextmid = $midnight + 86400;
$custom_date_format = trim($custom_date_format);
if ($return_full_date_and_time) {
if ( $hour_format == SMPREF_TIME_12HR ) {
$date_format = _("D, F j, Y g:i a");
} else {
$date_format = _("D, F j, Y H:i");
}
} else if (!empty($custom_date_format)) {
$date_format = $custom_date_format;
} else if ($show_full_date == 1 || $nextmid < $stamp) {
$date_format = _("M j, Y");
} else if ($midnight < $stamp) {
/* Today */
if ( $hour_format == SMPREF_TIME_12HR ) {
$date_format = _("g:i a");
} else {
$date_format = _("H:i");
}
} else if ($midnight - 518400 < $stamp) {
/* This week */
if ( $hour_format == SMPREF_TIME_12HR ) {
$date_format = _("D, g:i a");
} else {
$date_format = _("D, H:i");
}
} else {
/* before this week */
$date_format = _("M j, Y");
}
return( date_intl( $date_format, $stamp ) );
}
/**
* Decodes a RFC 822 Date-header into a timestamp
*
* @param array dateParts the Date-header split by whitespace
* @return int the timestamp calculated from the header
*/
function getTimeStamp($dateParts) {
/* $dateParts[0] == <day of week> Mon, Tue, Wed
* $dateParts[1] == <day of month> 23
* $dateParts[2] == <month> Jan, Feb, Mar
* $dateParts[3] == <year> 1999
* $dateParts[4] == <time> 18:54:23 (HH:MM:SS)
* $dateParts[5] == <from GMT> +0100
* $dateParts[6] == <zone> (EDT)
*
* NOTE: In RFC 822, it states that <day of week> is optional.
* In that case, dateParts[0] would be the <day of month>
* and everything would be bumped up one.
*/
if (count($dateParts) <2) {
return -1;
} else if (count($dateParts) ==3) {
if (substr_count($dateParts[0],'-') == 2 &&
substr_count($dateParts[1],':') == 2) {
// dd-Month-yyyy 23:19:05 +0200
// redefine the date
$aDate = explode('-',$dateParts[0]);
$newDate = array($aDate[0],$aDate[1],$aDate[2],$dateParts[1],$dateParts[2]);
$dateParts = $newDate;
}
}
/*
* Simply check to see if the first element in the dateParts
* array is an integer or not.
* Since the day of week is optional, this check is needed.
*/
if (!is_numeric(trim($dateParts[0]))) {
/* cope with broken mailers that send "Tue,23" without space */
if ( preg_match ('/^\w+,(\d{1,2})$/', $dateParts[0], $match) ) {
/* replace Tue,23 with 23 */
$dateParts[0] = $match[1];
} else {
/* just drop the day of week */
array_shift($dateParts);
}
}
/* calculate timestamp separated from the zone and obs-zone */
$stamp = strtotime(implode (' ', array_splice ($dateParts,0,4)));
if (!isset($dateParts[0])) {
$dateParts[0] = '+0000';
}
if (!preg_match('/^[+-]{1}[0-9]{4}$/',$dateParts[0])) {
/* zone in obs-zone format */
if (preg_match('/\((.+)\)/',$dateParts[0],$regs)) {
$obs_zone = $regs[1];
} else {
$obs_zone = $dateParts[0];
}
return getGMTSeconds($stamp, $obs_zone);
} else {
return getGMTSeconds($stamp, $dateParts[0]);
}
}