Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: time

  • Random Post of the Day

    Random Post of the Day

    I wanted to make a random post of the day. In this case, I wanted it to be a random post of one of two custom post types, and I wanted to output it as JSON for a variety of reasons, including future plans. Like tweeting that post.

    I’ll get to that later.

    Overview

    To do this, I have the following moving parts:

    1. The RESTful routes
    2. The random post
    3. The expiration (i.e. each post lasts a day)

    I’m going to skip over how to make a REST API route. I talked about that earlier in 2017 when I explained how I made the Bury Your Queers plugin.

    What’s important here is actually the random post and spitting out the right content.

    Getting a Random Post

    This is cool. WordPress can do this out of the box:

    $args = array( 
    	'post_type'      => 'post_type_characters',
    	'orderby'        => 'rand', 
    	'posts_per_page' =>'1',
    );
    $post = new WP_Query( $args );
    
    while ( $post->have_posts() ) {
    	$post->the_post(); 
    	$id = get_the_ID();
    }
    wp_reset_postdata();
    
    $of_the_day_array = array(
    	'name'   => get_the_title( $id ),
    	'url'    => get_permalink( $id ),
    );
    

    And at that point all you need to do is have the API return the array, and your final output is like this:

    {"name":"Van","url":"http:\/\/lezwatchtv.com\/character\/van\/"}
    

    This is a simplified version of my code, since in actuality I’m juggling a couple post types (shows or characters), and outputting more data (like if the character is dead or alive). It’s sufficient to prove this point.

    Expirations

    Okay. Now here’s the fun part. If you go to your JSON page now, it’ll show you a new character on every page reload, which is absolutely not what we want. We want this to only update once a day, so we can do this via Transients like this:

    if ( false === ( $id = get_transient( 'lwtv_otd_character' ) ) ) {
    	// Grab a random post
    	$args = array( 
    		'post_type'      => 'post_type_characters',
    		'orderby'        => 'rand', 
    		'posts_per_page' =>'1',
    	);
    	$post = new WP_Query( $args );
    	// Do the needful
    	while ( $post->have_posts() ) {
    		$post->the_post();
    		$id = get_the_ID();
    	}
    	wp_reset_postdata();
    	set_transient( 'lwtv_otd_character', $id, DAY_IN_SECONDS );
    }
    

    But.

    Transients kinda suck.

    Expirations 2.0

    Alright. Let’s do this differently. The server this is on has object caching, and it gets flushed every now and then. While it doesn’t matter if the post is re-randomizes in this case, it’s still not a great practice. So let’s use options!

    // Grab the options
    $default = array (
    	'character' => array( 
    		'time'  => strtotime( 'midnight tomorrow' ),
    		'post'  => 'none',
    	),
    	'show'      =>  array( 
    		'time'  => strtotime( 'midnight tomorrow' ),
    		'post'  => 'none',
    	),
    );
    $options = get_option( 'lwtv_otd', $default );
    
    // If there's no ID or the timestamp has past, we need a new ID
    if ( $options[ $type ][ 'post' ] == 'none' || time() >= $options[ $type ][ 'time' ] ) {
    	// Grab a random post
    	$args = array( 
    		'post_type'      => 'post_type_characters',
    		'orderby'        => 'rand', 
    		'posts_per_page' =>'1',
    	);
    	$post = new WP_Query( $args );
    	// Do the needful
    	while ( $post->have_posts() ) {
    		$post->the_post();
    		$id = get_the_ID();
    	}
    	wp_reset_postdata();
    
    	// Update the options
    	$options[ $type ][ 'post' ] = $id;
    	$options[ $type ][ 'time' ] = strtotime( 'midnight tomorrow' );
    	update_option( 'lwtv_otd', $options );
    }
    

    And now you see my $type variable and why it matters. There’s more magic involved in the real world, but it’s not relevant.

  • Date, Time, and PHP

    Date, Time, and PHP

    I had a case where I needed to convert time from the format of the US centric dd/MM/YYYY to something a little more sane.

    A Very Bad (and Stupid) Way

    Since I was starting from a set position of 18/07/2017 (for example), the first idea I had was to use explode to do this:

    $date = "18/07/2017";
    $datearray = explode("/", $date);
    $newdate = $date[1].'-'.$date[0].'-'.$date[2]
    

    I did say this was stupid, right? The reason I’d want to do that, though, is that gets it into a DD-MM-YYYY format, which can be parsed a little more easily. Unless you’re an American. Which means this idea is useless.

    A Less Bad, But Still Bad, Way

    What about making it unix timestamp?

    $date = "18/07/2017";
    $date_parse = date_parse_from_format( 'm/d/Y' , $date);
    $date_echo = mktime( $date_parse['hour'], $date_parse['minute'], $date_parse['second'], $date_parse['month'], $date_parse['day'], $date_parse['year'] );
    

    Now I have a unix timestamp, which I can output any which way I want! At this point, if I’m not sure what version of PHP people are using, as long as it’s above 5.2, my code will work. Awesome.

    But we can do better.

    A Clever Way

    Instead of all that, I could use date_parse_from_format:

    $date = "18/07/2017";
    $date_parse = date_create_from_format('m/j/Y', $date );
    $date_echo[] = date_format($date_parse, 'F d, Y');
    

    And this will convert “18/07/2017” into “18 July, 2017”

    Which is far, far more understandable.

    So Which Is Best?

    If it’s your own code on your own server, I’d use date_create_from_format to control the output as you want.

    If this is code you want to run on any server, like in a WordPress plugin, use the unix timestamp, and then use date_i18n to localize:

    date_i18n( get_option( 'date_format' ), $date_echo );