Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I am unable to set the year below 1899 for a post. If i set year below 1899 it is set to the current year automatically.

screen shot

I have purchased the Timeline theme and asked in their support forum. They replied:

This sounds like a limitation created by your hosting provider. Nothing in the theme is preventing the date you assign – as you can see, the demo has posts using dates in the 1400s. Try contacting your hosting provider and see if they have any insight on how to address that.

share|improve this question
2  
And your question is what exactly? There's thousands of theme's out there - it's a long shot to assume anybody would know the theme you bought. You'll have to at least provide relevant code. –  Johannes Pille Sep 19 '13 at 18:33
1  
I doubt it is the hoster's fault. This is about a value saved to the database, I assume. Turn on WP_DEBUG and edit an error message into your question. I might have made it seem like so, but a link to a working version of the theme in question really isn't much help either. –  Johannes Pille Sep 19 '13 at 19:06
1  
This is a legit question. It is not related to the theme. I can reproduce this problem. See this animated screen shot. –  toscho Sep 25 '13 at 6:57
1  
Same problem with 1900 and 1901 but 1902 works ;-) –  birgire Sep 25 '13 at 10:26
1  
I can reproduce this, by the way there are issues above Unix timestamp range (2038) as well. PHP 5.4 on Win7x64 –  Rarst Sep 25 '13 at 12:09

2 Answers 2

This is not really an answer, just an attempt to find the specific context for this problem. Please install the following plugin on your site, try to set the three dates and add your result to the second <pre> in the table below.

/* Plugin Name: WPSE Sysinfo */
add_action( 'admin_footer', 'wpse_sysinfo' );
function wpse_sysinfo() {

    $bit         = 4 === PHP_INT_SIZE ? 32 : 64; // PHP version, not OS!
    $php_version = PHP_VERSION;
    $db_version  = $GLOBALS['wpdb']->db_version();

    print "<pre>$bit | $php_version | $db_version</pre>";
}

Gist of the Plugin can be checked out here.

OS           | OS bit | PHP         | PHP Bit | MySQL  | 999  | 1899 | 2020 | 2039 | user
WIN7         |     64 | 5.4.4       | ??      | 5.5.25 |   ✘  |   ✘  |  ✔   |   ✘  | toscho
Linux        |     ?? | 5.3.18-nmm1 | ??      | 5.1.70 |   ✔  |   ✔  |  ✔   |   ✔  | toscho
CentOS 6     |     64 | 5.5.4       | ??      | 5.0.95 |   ✔  |   ✔  |  ✔   |   ✔  | toscho
WIN7         |     64 | 5.4.15      | 32      | 5.5.31 |   ✘  |   ✘  |  ✔   |   ✘  | rarst
Ubuntu 12.04 |     64 | 5.3.10-1    | 64      | 5.5.32 |   ✔  |   ✔  |  ✔   |   ✔  | Pille
CloudLinux   |     64 | 5.2.17      | 64      | 5.0.96 |   ✔  |   ✔  |  ✔   |   ✔  | Pille
Ubuntu 12.10 |     64 | 5.4.6       | 64      | 5.5.32 |   ✔  |   ✔  |  ✔   |   ✔  | Michael Ecklund
CENTOS 5.9   |     32 | 5.3.27      | 32      | 5.5.32 |   ✘  |   ✘  |  ✔   |   ✘  | Michael Ecklund
WIN7         |     64 | 5.4.7       | 64      | 5.5.27 |   ✘  |   ✘  |  ✔   |   ✘  | kaiser
OSX 10.7.5   |     64 | 5.3.6       | 64      | 5.5.9  |   ✔  |   ✔  |  ✔   |   ✔  | GhostToast
Centos 6.4   |     64 | 5.4.17      | 32      | 5.1.59 |   ✘  |   ✘  |  ✔   |   ✘  | birgire
Debian 6     |     64 | 5.4.19      | 64      | 5.1.66 |   ✘  |   ✘  |  ✔   |   ✘  | birgire
WIN7         |     64 | 5.5.0       | 64      | 5.5.22 |   ✘  |   ✘  |  ✔   |   ✘  | G.M.
OSX 10.7.4   |     64 | 5.3.6       | 64      | 5.5.9  |   ✔  |   ✔  |  ✔   |   ✔  | brasofilo
CentOS 5     |     64 | 5.3.22      | 64      | 5.1.68 |   ✔  |   ✔  |  ✔   |   ✔  | brasofilo
Mac 10.8.5   |     64 | 5.3.26      | 64      | 5.5.25 |   ✔  |   ✔  |  ✔   |   ✔  | flentini
WIN7         |     64 | 5.3.27      | 64      | 5.5.31 |   ✔  |   ✔  |  ✔   |   ✔  | Sascha Krause
Win7SP1      |     64 | 5.3.8       | 64      | 5.5.28 |   ✔  |   ✔  |  ✔   |   ✔  | Manuel Sychold
  1. Create a new post. Save it.
  2. Set the date to Jan. 1st, 0999, click Update. Is it saved or changed to the current date?
  3. Repeat for the date settings for 1899, 2020 and 2039.
  4. Take the information from the plugin output in your admin footer, and update the table.
share|improve this answer

Question and expectations

While the literal form of this question is practical in context (year 1899) it is a little vague in theoretical sense. How old is old? How far into the past we might want to go? What about the future?

Since WordPress had started out as blogging engine, in that contextual sense it evolved to handle following span of time:

  • dates WP existed (obviously to be able to use it)
  • range of possible historical posts (implicitly as far back as Internet existed)
  • as far into future as possible without special effort (work until it breaks)

As WordPress usage evolved into non-blogging applications such projects (commonly history and art as I have seen from reports) started to hit assorted issues with dates outside of this span.

For the purpose of my research I had formulated following questions:

  1. What are the two earliest and latest full calendar years, which can be used with WordPress post dates natively and reliably?
  2. What are low hanging fruits (if any) to extend available span beyond native range?

Platform limitations

Since WordPress is PHP application and uses MySQL for data storage it's subject to their limitations.

MySQL

WordPress stores post dates in post_date column of DATETIME type in MySQL.

According to documentation this type supports years 1000 to 9999:

The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD HH:MM:SS' format. The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.

However it also says that earlier values might work, no mention of later values:

For the DATE and DATETIME range descriptions, “supported” means that although earlier values might work, there is no guarantee.

While empirically I have observed values out of range working, this is anecdotal and falls out of our reliability condition.

PHP

In PHP programming Unix timestamp representation of date is widely used. According to documentation for our purposes (PHP 5.2+ and generic 32 bit environment) it supports years (in full) 1902 to 2037:

The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 UTC to Tue, 19 Jan 2038 03:14:07 UTC. (These are the dates that correspond to the minimum and maximum values for a 32-bit signed integer.) Additionally, not all platforms support negative timestamps, therefore your date range may be limited to no earlier than the Unix epoch. This means that e.g. dates prior to Jan 1, 1970 will not work on Windows, some Linux distributions, and a few other operating systems. PHP 5.1.0 and newer versions overcome this limitation though.

Other than that newer Date/Time based handling is 64 bit and has range roughly -292 billion to 292 billion years, which probably exceeds humanity needs at this time.

WordPress limitations

WordPress introduces and inherits some additional limitation in its code base.

Data flow

From basic user workflow point of view there are two processed related to date:

  • date input in post edit form must be correctly processed and saved in database
  • date saved in database must be correctly read and shown in interface

Note that these are technically completely different and independent processes. As further explained their ranges do not overlap and saving correct date does not equal ability to correctly read it in WordPress environment.

Explicit limits

  • WordPress post editor in admin allows range of years, that can be submitted as post date, 100 to 9999
  • _wp_translate_postdata() processes year (submitted as distinct number from form) and:
    • sanitizes it to non-negative >0
    • validates it using wp_checkdate(), which calls PHP native checkdate(), which imposes limit of 1 to 32767

Implicit limits

  • strtotime() PHP function is used multiple times and is subject to above mentioned Unix timestamp, at the lowest level in mysql2date() that affects all reads of dates from database, 1902 to 2037 range inherited
  • WordPress falls back to regular expression for date parsing in get_gmt_from_date(), which expects year to be ([0-9]{1,4}), limiting it 1 to 9999, strong possibility of similar processing in other functions that will require more thorough code audit to be enumerated

Possibility of workarounds

  • wp_checkdate() has wp_checkdate filter, which allows to override this validation check
  • output aimed at end-user goes through date_i18n() which has date_i18n filter, theoretically allows to completely intercept and re-process output of dates to interface however challenging if function is passed already out of range (false) timestamp input

Conclusions

For the practical purposes and portability of data WordPress post date range seems to be equal to that of 32 bit Unix timestamp and consist of years 1902 to 2037 inclusively.

For any post date operation out of this range environment must be audited (64 bit range of Unix timestamps, de-facto functioning MySQL or alternate database storage for the values). For farther ranges (below 1000, above 9999) considerable amounts of custom code are likely to be required.

For any implementation of arbitrary dates it makes sense to:

  • store them in MySQL in format not subject to database limitations
  • process in PHP using completely custom Date/Time-based code and/or WordPress functions audited not to be affected by Unix timestamp limits

Code test bed

The following code and hand picked set of years have been used for research above and testing of conclusions:

require ABSPATH . '/wp-admin/includes/post.php';

$timestamp_size_info = array(
    'PHP_INT_SIZE'   => PHP_INT_SIZE,
    'PHP_INT_MAX'    => number_format( PHP_INT_MAX ),
    'min timestamp'  => date( DATE_ISO8601, - PHP_INT_MAX ),
    'zero timestamp' => date( DATE_ISO8601, 0 ),
    'max timestamp'  => date( DATE_ISO8601, PHP_INT_MAX ),
);

r( $timestamp_size_info );

// hand picked set of years to test for assorted limits
$years = array(
    'negative'           => - 1,
    'zero'               => 0,
    'one'                => 1,
    'wp min'             => 100,
    'mysql first'        => 1000,
    'before unix'        => 1899,
    'unix first'         => 1902,
    'current'            => 2013,
    'unix last'          => 2037,
    'after unix'         => 2039,
    'mysql last, wp max' => 9999,
    'after checkdate'    => 33000,
);

// simulates form submission data
$post = array(
    'post_type' => 'post', // shut notice
    'edit_date' => 1,
    'aa'        => 1,
    'mm'        => '01',
    'jj'        => '01',
    'hh'        => '00',
    'mn'        => '00',
    'ss'        => '00',
);

// add_filter( 'wp_checkdate', '__return_true' );

foreach ( $years as $name => $year ) {

    $post['aa'] = $year;
    $translated = _wp_translate_postdata( false, $post );

    if ( is_wp_error( $translated ) ) { // wp_checkdate() failed
        r( array( 'year' => $year . " ({$name})", 'translated valid' => false ) );
    }
    else {

        $post_date        = $translated['post_date'];
        $post_date_gmt    = $translated['post_date_gmt'];
        $translated_valid = (string) $year == substr( $post_date, 0, strpos( $post_date, '-' ) );
        $mysql2date       = mysql2date( DATE_ISO8601, $post_date );
        $mysql2date_valid = (string) $year == substr( $mysql2date, 0, strpos( $mysql2date, '-' ) );

        r( array(
            'year'             => $year . " ({$name})",
            'post_date'        => $post_date,
            'translated valid' => $translated_valid,
            'post_date_gmt'    => $post_date_gmt,
            'mysql2date'       => $mysql2date,
            'from sql valid'   => $mysql2date_valid,
        ) );
    }
}
share|improve this answer
    
+1 Only question remaining: What is r()? –  kaiser Sep 26 '13 at 13:13
1  
@kaiser php-ref, replace with dump function of choice :) –  Rarst Sep 26 '13 at 13:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.