Timezones (#2684)
* Implemented support for timezones * Removed OFFSET const from config * Localized yet sortable date in admin shorturl list * Unit tests * Code cleanup Co-authored-by: Jérémy K <JeremyKeusters@users.noreply.github.com>
This commit is contained in:
parent
4a78d72936
commit
6dc5423f5e
@ -132,6 +132,9 @@ td.url small a{
|
||||
body.desktop td.actions input,body.desktop td.actions a {
|
||||
visibility:hidden;
|
||||
}
|
||||
td.timestamp span.timestamp {
|
||||
display:none;
|
||||
}
|
||||
td.actions input.disabled, td.actions input.loading {
|
||||
visibility:visible;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class Init {
|
||||
$this->include_core_functions();
|
||||
}
|
||||
|
||||
// Enforce UTC timezone to suppress PHP warnings -- correct date/time will be managed using the config time offset
|
||||
// Enforce UTC timezone. Date/time can be adjusted with a plugin.
|
||||
if ($actions->default_timezone === true) {
|
||||
date_default_timezone_set( 'UTC' );
|
||||
}
|
||||
|
@ -722,3 +722,62 @@ function yourls_make_bookmarklet( $code ) {
|
||||
$book = new \Ozh\Bookmarkletgen\Bookmarkletgen;
|
||||
return $book->crunch( $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp, plus or minus the time offset if defined
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string|int $timestamp a timestamp
|
||||
* @return int a timestamp, plus or minus offset if defined
|
||||
*/
|
||||
function yourls_get_timestamp( $timestamp ) {
|
||||
$offset = yourls_get_time_offset();
|
||||
$timestamp_offset = $timestamp + ($offset * 3600);
|
||||
|
||||
return yourls_apply_filter( 'get_timestamp', $timestamp_offset, $timestamp, $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time offset, as defined in config, filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @return int Time offset
|
||||
*/
|
||||
function yourls_get_time_offset() {
|
||||
$offset = defined('YOURLS_HOURS_OFFSET') ? (int)YOURLS_HOURS_OFFSET : 0;
|
||||
return yourls_apply_filter( 'get_time_offset', $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for a full date + time, filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_datetime_format( $format ) {
|
||||
return yourls_apply_filter( 'get_datetime_format', (string)$format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for date (no time), filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_date_format( $format ) {
|
||||
return yourls_apply_filter( 'get_date_format', (string)$format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a date() format for a time (no date), filtered
|
||||
*
|
||||
* @since 1.7.10
|
||||
* @param string $format Date format string
|
||||
* @return string Date format string
|
||||
*/
|
||||
function yourls_get_time_format( $format ) {
|
||||
return yourls_apply_filter( 'get_time_format', (string)$format );
|
||||
}
|
||||
|
||||
|
@ -599,8 +599,9 @@ function yourls_table_add_row( $keyword, $url, $title = '', $ip, $clicks, $times
|
||||
'warning' => $protocol_warning,
|
||||
),
|
||||
'timestamp' => array(
|
||||
'template' => '%date%',
|
||||
'date' => date( 'M d, Y H:i', $timestamp +( YOURLS_HOURS_OFFSET * 3600 ) ),
|
||||
'template' => '<span class="timestamp" aria-hidden="true">%timestamp%</span> %date%',
|
||||
'timestamp' => $timestamp,
|
||||
'date' => yourls_date_i18n( yourls_get_datetime_format('M d, Y H:i'), yourls_get_timestamp( $timestamp )),
|
||||
),
|
||||
'ip' => array(
|
||||
'template' => '%ip%',
|
||||
@ -1001,4 +1002,3 @@ function yourls_get_html_context() {
|
||||
global $ydb;
|
||||
$ydb->get_html_context();
|
||||
}
|
||||
|
||||
|
@ -121,27 +121,27 @@ function yourls__( $text, $domain = 'default' ) {
|
||||
* @return string Translated text
|
||||
*/
|
||||
function yourls_s( $pattern ) {
|
||||
// Get pattern and pattern arguments
|
||||
// Get pattern and pattern arguments
|
||||
$args = func_get_args();
|
||||
// If yourls_s() called by yourls_se(), all arguments are wrapped in the same array key
|
||||
if( count( $args ) == 1 && is_array( $args[0] ) ) {
|
||||
$args = $args[0];
|
||||
}
|
||||
$pattern = $args[0];
|
||||
|
||||
|
||||
// get list of sprintf tokens (%s and such)
|
||||
$num_of_tokens = substr_count( $pattern, '%' ) - 2 * substr_count( $pattern, '%%' );
|
||||
|
||||
|
||||
$domain = 'default';
|
||||
// More arguments passed than needed for the sprintf? The last one will be the domain
|
||||
if( $num_of_tokens < ( count( $args ) - 1 ) ) {
|
||||
$domain = array_pop( $args );
|
||||
}
|
||||
|
||||
|
||||
// Translate text
|
||||
$args[0] = yourls__( $pattern, $domain );
|
||||
|
||||
return call_user_func_array( 'sprintf', $args );
|
||||
|
||||
return call_user_func_array( 'sprintf', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,7 +378,7 @@ function yourls_nx($single, $plural, $number, $context, $domain = 'default') {
|
||||
function yourls_n_noop( $singular, $plural, $domain = null ) {
|
||||
return array(
|
||||
0 => $singular,
|
||||
1 => $plural,
|
||||
1 => $plural,
|
||||
'singular' => $singular,
|
||||
'plural' => $plural,
|
||||
'context' => null,
|
||||
@ -507,7 +507,7 @@ function yourls_unload_textdomain( $domain ) {
|
||||
*/
|
||||
function yourls_load_default_textdomain() {
|
||||
$yourls_locale = yourls_get_locale();
|
||||
|
||||
|
||||
if( !empty( $yourls_locale ) )
|
||||
return yourls_load_textdomain( 'default', YOURLS_LANG_DIR . "/$yourls_locale.mo" );
|
||||
}
|
||||
@ -563,13 +563,13 @@ function yourls_translate_user_role( $name ) {
|
||||
*/
|
||||
function yourls_get_available_languages( $dir = null ) {
|
||||
$languages = array();
|
||||
|
||||
|
||||
$dir = is_null( $dir) ? YOURLS_LANG_DIR : $dir;
|
||||
|
||||
|
||||
foreach( (array) glob( $dir . '/*.mo' ) as $lang_file ) {
|
||||
$languages[] = basename( $lang_file, '.mo' );
|
||||
}
|
||||
|
||||
|
||||
return yourls_apply_filter( 'get_available_languages', $languages );
|
||||
}
|
||||
|
||||
@ -586,7 +586,7 @@ function yourls_number_format_i18n( $number, $decimals = 0 ) {
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
$formatted = number_format( $number, abs( intval( $decimals ) ), $yourls_locale_formats->number_format['decimal_point'], $yourls_locale_formats->number_format['thousands_sep'] );
|
||||
return yourls_apply_filter( 'number_format_i18n', $formatted );
|
||||
}
|
||||
@ -635,7 +635,7 @@ function yourls_date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = fal
|
||||
$dateweekday_abbrev = $yourls_locale_formats->get_weekday_abbrev( $dateweekday );
|
||||
$datemeridiem = $yourls_locale_formats->get_meridiem( $datefunc( 'a', $i ) );
|
||||
$datemeridiem_capital = $yourls_locale_formats->get_meridiem( $datefunc( 'A', $i ) );
|
||||
|
||||
|
||||
$dateformatstring = ' '.$dateformatstring;
|
||||
$dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . yourls_backslashit( $dateweekday_abbrev ), $dateformatstring );
|
||||
$dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . yourls_backslashit( $datemonth ), $dateformatstring );
|
||||
@ -649,7 +649,7 @@ function yourls_date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = fal
|
||||
$timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
|
||||
$timezone_formats_re = implode( '|', $timezone_formats );
|
||||
if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
|
||||
|
||||
|
||||
// TODO: implement a timezone option
|
||||
$timezone_string = yourls_get_option( 'timezone_string' );
|
||||
if ( $timezone_string ) {
|
||||
@ -689,15 +689,13 @@ function yourls_date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = fal
|
||||
function yourls_current_time( $type, $gmt = 0 ) {
|
||||
switch ( $type ) {
|
||||
case 'mysql':
|
||||
return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', time() + YOURLS_HOURS_OFFSET * 3600 );
|
||||
return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', yourls_get_timestamp( time() ));
|
||||
break;
|
||||
case 'timestamp':
|
||||
return ( $gmt ) ? time() : time() + YOURLS_HOURS_OFFSET * 3600;
|
||||
break;
|
||||
return ( $gmt ) ? time() : yourls_get_timestamp( time() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class that loads the calendar locale.
|
||||
*
|
||||
@ -955,7 +953,7 @@ class YOURLS_Locale_Formats {
|
||||
* @return string Translated full month name
|
||||
*/
|
||||
function get_month( $month_number ) {
|
||||
return $this->month[ sprintf( '%02s', $month_number ) ];
|
||||
return $this->month[ sprintf( '%02s', $month_number ) ];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1060,7 +1058,7 @@ function yourls_is_rtl() {
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
return $yourls_locale_formats->is_rtl();
|
||||
}
|
||||
|
||||
@ -1078,10 +1076,10 @@ function yourls_l10n_weekday_abbrev( $weekday = '' ){
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
if( $weekday === '' )
|
||||
return $yourls_locale_formats->weekday_abbrev;
|
||||
|
||||
|
||||
if( is_int( $weekday ) ) {
|
||||
$day = $yourls_locale_formats->weekday[ $weekday ];
|
||||
return $yourls_locale_formats->weekday_abbrev[ $day ];
|
||||
@ -1104,10 +1102,10 @@ function yourls_l10n_weekday_initial( $weekday = '' ){
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
if( $weekday === '' )
|
||||
return $yourls_locale_formats->weekday_initial;
|
||||
|
||||
|
||||
if( is_int( $weekday ) ) {
|
||||
$weekday = $yourls_locale_formats->weekday[ $weekday ];
|
||||
return $yourls_locale_formats->weekday_initial[ $weekday ];
|
||||
@ -1130,10 +1128,10 @@ function yourls_l10n_month_abbrev( $month = '' ){
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
if( $month === '' )
|
||||
return $yourls_locale_formats->month_abbrev;
|
||||
|
||||
|
||||
if( intval( $month ) > 0 ) {
|
||||
$month = sprintf('%02d', intval( $month ) );
|
||||
$month = $yourls_locale_formats->month[ $month ];
|
||||
@ -1153,6 +1151,6 @@ function yourls_l10n_months(){
|
||||
global $yourls_locale_formats;
|
||||
if( !isset( $yourls_locale_formats ) )
|
||||
$yourls_locale_formats = new YOURLS_Locale_Formats();
|
||||
|
||||
|
||||
return $yourls_locale_formats->month;
|
||||
}
|
||||
|
72
tests/tests/format/timedates.php
Normal file
72
tests/tests/format/timedates.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Formatting functions for time & dates
|
||||
*
|
||||
* @group formatting
|
||||
* @group timedate
|
||||
* @since 0.1
|
||||
*/
|
||||
class Format_Test_Dates extends PHPUnit_Framework_TestCase {
|
||||
|
||||
protected function tearDown() {
|
||||
yourls_remove_all_filters( 'get_time_offset' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_timestamp returns an int
|
||||
*/
|
||||
function test_get_time_offset() {
|
||||
$this->assertInternalType( "int", yourls_get_time_offset() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_datetime_format returns a string
|
||||
*/
|
||||
function test_get_datetime_format() {
|
||||
$this->assertInternalType( "string", yourls_get_datetime_format('M d, Y H:i') );
|
||||
$this->assertInternalType( "string", yourls_get_datetime_format( 10 ) );
|
||||
$this->assertInternalType( "string", yourls_get_datetime_format(false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_date_format returns a string
|
||||
*/
|
||||
function test_get_date_format() {
|
||||
$this->assertInternalType( "string", yourls_get_date_format('M d, Y') );
|
||||
$this->assertInternalType( "string", yourls_get_date_format( 10 ) );
|
||||
$this->assertInternalType( "string", yourls_get_date_format(false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_time_format returns a string
|
||||
*/
|
||||
function test_get_time_format() {
|
||||
$this->assertInternalType( "string", yourls_get_time_format('H:i') );
|
||||
$this->assertInternalType( "string", yourls_get_time_format( 10 ) );
|
||||
$this->assertInternalType( "string", yourls_get_time_format(false) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_timestamp returns unmodified timestamp if no offset
|
||||
*/
|
||||
function test_get_time_offset_zero() {
|
||||
$now = time();
|
||||
|
||||
yourls_add_filter('get_time_offset', 'yourls_return_zero' );
|
||||
$this->assertEquals( $now, yourls_get_timestamp( $now ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test yourls_get_timestamp returns a timestamp with offset
|
||||
*/
|
||||
function test_get_time_offset_non_zero() {
|
||||
$now = time();
|
||||
|
||||
$offset = mt_rand(-12,12);
|
||||
|
||||
yourls_add_filter('get_time_offset', function() use($offset) {return $offset;} );
|
||||
$this->assertEquals( $now + ($offset * 3600), yourls_get_timestamp( $now ) );
|
||||
}
|
||||
|
||||
}
|
@ -34,14 +34,11 @@ define( 'YOURLS_DB_PREFIX', 'yourls_' );
|
||||
** If you define it to "http://sho.rt", don't use "http://www.sho.rt" in your browser (and vice-versa) */
|
||||
define( 'YOURLS_SITE', 'http://your-own-domain-here.com' );
|
||||
|
||||
/** Server timezone GMT offset */
|
||||
define( 'YOURLS_HOURS_OFFSET', 0 );
|
||||
|
||||
/** YOURLS language
|
||||
** Change this setting to use a translation file for your language, instead of the default English.
|
||||
** That translation file (a .mo file) must be installed in the user/language directory.
|
||||
** See http://yourls.org/translations for more information */
|
||||
define( 'YOURLS_LANG', '' );
|
||||
define( 'YOURLS_LANG', '' );
|
||||
|
||||
/** Allow multiple short URLs for a same long URL
|
||||
** Set to true to have only one pair of shortURL/longURL (default YOURLS behavior)
|
||||
@ -68,7 +65,7 @@ $yourls_user_passwords = array(
|
||||
/** Debug mode to output some internal information
|
||||
** Default is false for live site. Enable when coding or before submitting a new issue */
|
||||
define( 'YOURLS_DEBUG', false );
|
||||
|
||||
|
||||
/*
|
||||
** URL Shortening settings
|
||||
*/
|
||||
@ -81,7 +78,7 @@ define( 'YOURLS_URL_CONVERT', 36 );
|
||||
* Stick to one setting. It's best not to change after you've started creating links.
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* Reserved keywords (so that generated URLs won't match them)
|
||||
* Define here negative, unwanted or potentially misleading keywords.
|
||||
*/
|
||||
@ -92,4 +89,3 @@ $yourls_reserved_URL = array(
|
||||
/*
|
||||
** Personal settings would go after here.
|
||||
*/
|
||||
|
||||
|
@ -25,14 +25,14 @@ function ozh_yourls_samplepage_do_page() {
|
||||
if( isset( $_POST['test_option'] ) ) {
|
||||
// Check nonce
|
||||
yourls_verify_nonce( 'sample_page' );
|
||||
|
||||
|
||||
// Process form
|
||||
ozh_yourls_samplepage_update_option();
|
||||
}
|
||||
|
||||
// Get value from database
|
||||
$test_option = yourls_get_option( 'test_option' );
|
||||
|
||||
|
||||
// Create nonce
|
||||
$nonce = yourls_create_nonce( 'sample_page' );
|
||||
|
||||
@ -51,13 +51,13 @@ HTML;
|
||||
// Update option in database
|
||||
function ozh_yourls_samplepage_update_option() {
|
||||
$in = $_POST['test_option'];
|
||||
|
||||
|
||||
if( $in ) {
|
||||
// Validate test_option. ALWAYS validate and sanitize user input.
|
||||
// Here, we want an integer
|
||||
$in = intval( $in);
|
||||
|
||||
|
||||
// Update value in database
|
||||
yourls_update_option( 'test_option', $in );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,12 +149,14 @@ if( yourls_do_log_redirect() ) {
|
||||
unset($_lists);
|
||||
}
|
||||
|
||||
$offset = yourls_get_time_offset();
|
||||
|
||||
// *** Last 24 hours : array of $last_24h[ $hour ] = number of click ***
|
||||
$sql = "SELECT
|
||||
DATE_FORMAT(DATE_ADD(`click_time`, INTERVAL " . YOURLS_HOURS_OFFSET . " HOUR), '%H %p') AS `time`,
|
||||
DATE_FORMAT(DATE_ADD(`click_time`, INTERVAL " . $offset . " HOUR), '%H %p') AS `time`,
|
||||
COUNT(*) AS `count`
|
||||
FROM `$table`
|
||||
WHERE `shorturl` $keyword_range AND DATE_ADD(`click_time`, INTERVAL " . YOURLS_HOURS_OFFSET . " HOUR) > (DATE_ADD(CURRENT_TIMESTAMP, INTERVAL " . YOURLS_HOURS_OFFSET . " HOUR) - INTERVAL 1 DAY)
|
||||
WHERE `shorturl` $keyword_range AND DATE_ADD(`click_time`, INTERVAL " . $offset . " HOUR) > (DATE_ADD(CURRENT_TIMESTAMP, INTERVAL " . $offset . " HOUR) - INTERVAL 1 DAY)
|
||||
GROUP BY `time`;";
|
||||
$sql = yourls_apply_filter('stat_query_last24h', $sql);
|
||||
$rows = $ydb->fetchObjects($sql, $keyword_binds);
|
||||
@ -167,7 +169,7 @@ if( yourls_do_log_redirect() ) {
|
||||
|
||||
$now = intval( date('U') );
|
||||
for ($i = 23; $i >= 0; $i--) {
|
||||
$h = date('H A', ($now - ($i * 60 * 60) + (YOURLS_HOURS_OFFSET * 60 * 60)) );
|
||||
$h = date('H A', ($now - ($i * 60 * 60) + ($offset * 60 * 60)) );
|
||||
// If the $last_24h doesn't have all the hours, insert missing hours with value 0
|
||||
$last_24h[ $h ] = array_key_exists( $h, $_last_24h ) ? $_last_24h[ $h ] : 0 ;
|
||||
}
|
||||
@ -335,14 +337,15 @@ yourls_html_menu();
|
||||
<td valign="top">
|
||||
<h3><?php yourls_e( 'Historical click count' ); ?></h3>
|
||||
<?php
|
||||
$ago = round( (date('U') - strtotime($timestamp)) / (24* 60 * 60 ) );
|
||||
$timestamp = strtotime( $timestamp );
|
||||
$ago = round( (date('U') - $timestamp) / (24* 60 * 60 ) );
|
||||
if( $ago <= 1 ) {
|
||||
$daysago = '';
|
||||
} else {
|
||||
$daysago = ' (' . sprintf( yourls_n( 'about 1 day ago', 'about %s days ago', $ago ), $ago ) . ')';
|
||||
}
|
||||
?>
|
||||
<p><?php echo /* //translators: eg Short URL created on March 23rd 1972 */ yourls_s( 'Short URL created on %s', yourls_date_i18n( "F j, Y @ g:i a", ( strtotime( $timestamp ) + YOURLS_HOURS_OFFSET * 3600 ) ) ) . $daysago; ?></p>
|
||||
<p><?php echo /* //translators: eg Short URL created on March 23rd 1972 */ yourls_s( 'Short URL created on %s', yourls_date_i18n( yourls_get_datetime_format("F j, Y @ g:i a"), yourls_get_timestamp( $timestamp ) ) ) . $daysago; ?></p>
|
||||
<div class="wrap_unfloat">
|
||||
<ul class="no_bullet toggle_display stat_line" id="historical_clicks">
|
||||
<?php
|
||||
@ -381,7 +384,7 @@ yourls_html_menu();
|
||||
$best_time['month'] = date( "m", strtotime( $best['day'] ) );
|
||||
$best_time['year'] = date( "Y", strtotime( $best['day'] ) );
|
||||
?>
|
||||
<p><?php echo sprintf( /* //translators: eg. 43 hits on January 1, 1970 */ yourls_n( '<strong>%1$s</strong> hit on %2$s', '<strong>%1$s</strong> hits on %2$s', $best['max'] ), $best['max'], yourls_date_i18n( "F j, Y", strtotime( $best['day'] ) ) ); ?>.
|
||||
<p><?php echo sprintf( /* //translators: eg. 43 hits on January 1, 1970 */ yourls_n( '<strong>%1$s</strong> hit on %2$s', '<strong>%1$s</strong> hits on %2$s', $best['max'] ), $best['max'], yourls_date_i18n( yourls_get_date_format("F j, Y"), strtotime( $best['day'] ) ) ); ?>.
|
||||
<a href="" class='details hide-if-no-js' id="more_clicks"><?php yourls_e( 'Click for more details' ); ?></a></p>
|
||||
<ul id="details_clicks" style="display:none">
|
||||
<?php
|
||||
|
Loading…
x
Reference in New Issue
Block a user