Title: get_calendar
Published: April 25, 2014
Last modified: February 24, 2026

---

# get_calendar( array $args = array() ): void|string

## In this article

 * [Description](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#description)
 * [Parameters](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#parameters)
 * [Return](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#return)
 * [Source](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#source)
 * [Hooks](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#hooks)
 * [Related](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#related)
 * [Changelog](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#changelog)

[ Back to top](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#wp--skip-link--target)

Displays calendar with days that have posts as links.

## 󠀁[Description](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#description)󠁿

The calendar is cached, which will be retrieved, if it exists. If there are no posts
for the month, then it will not be displayed.

## 󠀁[Parameters](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#parameters)󠁿

 `$args`arrayoptional

Arguments for the `get_calendar` function.

 * `initial` bool
 * Whether to use initial calendar names. Default true.
 * `display` bool
 * Whether to display the calendar output. Default true.
 * `post_type` string
 * Optional. Post type. Default `'post'`.

Default:`array()`

## 󠀁[Return](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#return)󠁿

 void|string Void if `$display` argument is true, calendar HTML if `$display` is
false.

## 󠀁[Source](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#source)󠁿

    ```php
    function get_calendar( $args = array() ) {
    	global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;

    	$defaults = array(
    		'initial'   => true,
    		'display'   => true,
    		'post_type' => 'post',
    	);

    	$original_args = func_get_args();
    	$args          = array();

    	if ( ! empty( $original_args ) ) {
    		if ( ! is_array( $original_args[0] ) ) {
    			if ( isset( $original_args[0] ) && is_bool( $original_args[0] ) ) {
    				$defaults['initial'] = $original_args[0];
    			}
    			if ( isset( $original_args[1] ) && is_bool( $original_args[1] ) ) {
    				$defaults['display'] = $original_args[1];
    			}
    		} else {
    			$args = $original_args[0];
    		}
    	}

    	/**
    	 * Filter the `get_calendar` function arguments before they are used.
    	 *
    	 * @since 6.8.0
    	 *
    	 * @param array $args {
    	 *     Optional. Arguments for the `get_calendar` function.
    	 *
    	 *     @type bool   $initial   Whether to use initial calendar names. Default true.
    	 *     @type bool   $display   Whether to display the calendar output. Default true.
    	 *     @type string $post_type Optional. Post type. Default 'post'.
    	 * }
    	 */
    	$args = apply_filters( 'get_calendar_args', wp_parse_args( $args, $defaults ) );

    	if ( ! post_type_exists( $args['post_type'] ) ) {
    		$args['post_type'] = 'post';
    	}

    	$w = 0;
    	if ( isset( $_GET['w'] ) ) {
    		$w = (int) $_GET['w'];
    	}

    	/*
    	 * Normalize the cache key.
    	 *
    	 * The following ensures the same cache key is used for the same parameter
    	 * and parameter equivalents. This prevents `post_type > post, initial > true`
    	 * from generating a different key from the same values in the reverse order.
    	 *
    	 * `display` is excluded from the cache key as the cache contains the same
    	 * HTML regardless of this function's need to echo or return the output.
    	 *
    	 * The global values contain data generated by the URL query string variables.
    	 */
    	$cache_args = $args;
    	unset( $cache_args['display'] );

    	$cache_args['globals'] = array(
    		'm'        => $m,
    		'monthnum' => $monthnum,
    		'year'     => $year,
    		'week'     => $w,
    	);

    	wp_recursive_ksort( $cache_args );
    	$key   = md5( serialize( $cache_args ) );
    	$cache = wp_cache_get( 'get_calendar', 'calendar' );

    	if ( $cache && is_array( $cache ) && isset( $cache[ $key ] ) ) {
    		/** This filter is documented in wp-includes/general-template.php */
    		$output = apply_filters( 'get_calendar', $cache[ $key ], $args );

    		if ( $args['display'] ) {
    			echo $output;
    			return;
    		}

    		return $output;
    	}

    	if ( ! is_array( $cache ) ) {
    		$cache = array();
    	}

    	$post_type = $args['post_type'];

    	// Quick check. If we have no posts at all, abort!
    	if ( ! $posts ) {
    		$gotsome = $wpdb->get_var(
    			$wpdb->prepare(
    				"SELECT 1 as test
    				FROM $wpdb->posts
    				WHERE post_type = %s
    				AND post_status = 'publish'
    				LIMIT 1",
    				$post_type
    			)
    		);

    		if ( ! $gotsome ) {
    			$cache[ $key ] = '';
    			wp_cache_set( 'get_calendar', $cache, 'calendar' );
    			return;
    		}
    	}

    	// week_begins = 0 stands for Sunday.
    	$week_begins = (int) get_option( 'start_of_week' );

    	// Let's figure out when we are.
    	if ( ! empty( $monthnum ) && ! empty( $year ) ) {
    		$thismonth = (int) $monthnum;
    		$thisyear  = (int) $year;
    	} elseif ( ! empty( $w ) ) {
    		// We need to get the month from MySQL.
    		$thisyear = (int) substr( $m, 0, 4 );
    		// It seems MySQL's weeks disagree with PHP's.
    		$d         = ( ( $w - 1 ) * 7 ) + 6;
    		$thismonth = (int) $wpdb->get_var(
    			$wpdb->prepare(
    				"SELECT DATE_FORMAT((DATE_ADD('%d0101', INTERVAL %d DAY) ), '%%m')",
    				$thisyear,
    				$d
    			)
    		);
    	} elseif ( ! empty( $m ) ) {
    		$thisyear = (int) substr( $m, 0, 4 );
    		if ( strlen( $m ) < 6 ) {
    			$thismonth = 1;
    		} else {
    			$thismonth = (int) substr( $m, 4, 2 );
    		}
    	} else {
    		$thisyear  = (int) current_time( 'Y' );
    		$thismonth = (int) current_time( 'm' );
    	}

    	$unixmonth = mktime( 0, 0, 0, $thismonth, 1, $thisyear );
    	$last_day  = gmdate( 't', $unixmonth );

    	// Get the next and previous month and year with at least one post.
    	$previous = $wpdb->get_row(
    		$wpdb->prepare(
    			"SELECT MONTH(post_date) AS month, YEAR(post_date) AS year
    			FROM $wpdb->posts
    			WHERE post_date < '%d-%d-01'
    			AND post_type = %s AND post_status = 'publish'
    			ORDER BY post_date DESC
    			LIMIT 1",
    			$thisyear,
    			zeroise( $thismonth, 2 ),
    			$post_type
    		)
    	);

    	$next = $wpdb->get_row(
    		$wpdb->prepare(
    			"SELECT MONTH(post_date) AS month, YEAR(post_date) AS year
    			FROM $wpdb->posts
    			WHERE post_date > '%d-%d-%d 23:59:59'
    			AND post_type = %s AND post_status = 'publish'
    			ORDER BY post_date ASC
    			LIMIT 1",
    			$thisyear,
    			zeroise( $thismonth, 2 ),
    			$last_day,
    			$post_type
    		)
    	);

    	/* translators: Calendar caption: 1: Month name, 2: 4-digit year. */
    	$calendar_caption = _x( '%1$s %2$s', 'calendar caption' );
    	$calendar_output  = '<table id="wp-calendar" class="wp-calendar-table">
    	<caption>' . sprintf(
    		$calendar_caption,
    		$wp_locale->get_month( $thismonth ),
    		gmdate( 'Y', $unixmonth )
    	) . '</caption>
    	<thead>
    	<tr>';

    	$myweek = array();

    	for ( $wdcount = 0; $wdcount <= 6; $wdcount++ ) {
    		$myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 );
    	}

    	foreach ( $myweek as $wd ) {
    		$day_name         = $args['initial'] ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd );
    		$wd               = esc_attr( $wd );
    		$calendar_output .= "\n\t\t<th scope=\"col\" aria-label=\"$wd\">$day_name</th>";
    	}

    	$calendar_output .= '
    	</tr>
    	</thead>
    	<tbody>
    	<tr>';

    	$daywithpost = array();

    	// Get days with posts.
    	$dayswithposts = $wpdb->get_results(
    		$wpdb->prepare(
    			"SELECT DISTINCT DAYOFMONTH(post_date)
    			FROM $wpdb->posts WHERE post_date >= '%d-%d-01 00:00:00'
    			AND post_type = %s AND post_status = 'publish'
    			AND post_date <= '%d-%d-%d 23:59:59'",
    			$thisyear,
    			zeroise( $thismonth, 2 ),
    			$post_type,
    			$thisyear,
    			zeroise( $thismonth, 2 ),
    			$last_day
    		),
    		ARRAY_N
    	);

    	if ( $dayswithposts ) {
    		foreach ( (array) $dayswithposts as $daywith ) {
    			$daywithpost[] = (int) $daywith[0];
    		}
    	}

    	// See how much we should pad in the beginning.
    	$pad = calendar_week_mod( (int) gmdate( 'w', $unixmonth ) - $week_begins );
    	if ( $pad > 0 ) {
    		$calendar_output .= "\n\t\t" . '<td colspan="' . esc_attr( $pad ) . '" class="pad">&nbsp;</td>';
    	}

    	$newrow      = false;
    	$daysinmonth = (int) gmdate( 't', $unixmonth );

    	for ( $day = 1; $day <= $daysinmonth; ++$day ) {
    		if ( $newrow ) {
    			$calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t";
    		}

    		$newrow = false;

    		if ( (int) current_time( 'j' ) === $day
    			&& (int) current_time( 'm' ) === $thismonth
    			&& (int) current_time( 'Y' ) === $thisyear
    		) {
    			$calendar_output .= '<td id="today">';
    		} else {
    			$calendar_output .= '<td>';
    		}

    		if ( in_array( $day, $daywithpost, true ) ) {
    			// Any posts today?
    			$date_format = gmdate( _x( 'F j, Y', 'daily archives date format' ), strtotime( "{$thisyear}-{$thismonth}-{$day}" ) );
    			/* translators: Post calendar label. %s: Date. */
    			$label            = sprintf( __( 'Posts published on %s' ), $date_format );
    			$calendar_output .= sprintf(
    				'<a href="%s" aria-label="%s">%s</a>',
    				get_day_link( $thisyear, $thismonth, $day ),
    				esc_attr( $label ),
    				$day
    			);
    		} else {
    			$calendar_output .= $day;
    		}

    		$calendar_output .= '</td>';

    		if ( 6 === (int) calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins ) ) {
    			$newrow = true;
    		}
    	}

    	$pad = 7 - calendar_week_mod( (int) gmdate( 'w', mktime( 0, 0, 0, $thismonth, $day, $thisyear ) ) - $week_begins );
    	if ( 0 < $pad && $pad < 7 ) {
    		$calendar_output .= "\n\t\t" . '<td class="pad" colspan="' . esc_attr( $pad ) . '">&nbsp;</td>';
    	}

    	$calendar_output .= "\n\t</tr>\n\t</tbody>";

    	$calendar_output .= "\n\t</table>";

    	$calendar_output .= '<nav aria-label="' . __( 'Previous and next months' ) . '" class="wp-calendar-nav">';

    	if ( $previous ) {
    		$calendar_output .= "\n\t\t" . sprintf(
    			'<span class="wp-calendar-nav-prev"><a href="%1$s">&laquo; %2$s</a></span>',
    			get_month_link( $previous->year, $previous->month ),
    			$wp_locale->get_month_abbrev( $wp_locale->get_month( $previous->month ) )
    		);
    	} else {
    		$calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-prev">&nbsp;</span>';
    	}

    	$calendar_output .= "\n\t\t" . '<span class="pad">&nbsp;</span>';

    	if ( $next ) {
    		$calendar_output .= "\n\t\t" . sprintf(
    			'<span class="wp-calendar-nav-next"><a href="%1$s">%2$s &raquo;</a></span>',
    			get_month_link( $next->year, $next->month ),
    			$wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) )
    		);
    	} else {
    		$calendar_output .= "\n\t\t" . '<span class="wp-calendar-nav-next">&nbsp;</span>';
    	}

    	$calendar_output .= '
    	</nav>';

    	$cache[ $key ] = $calendar_output;
    	wp_cache_set( 'get_calendar', $cache, 'calendar' );

    	/**
    	 * Filters the HTML calendar output.
    	 *
    	 * @since 3.0.0
    	 * @since 6.8.0 Added the `$args` parameter.
    	 *
    	 * @param string $calendar_output HTML output of the calendar.
    	 * @param array  $args {
    	 *     Optional. Array of display arguments.
    	 *
    	 *     @type bool   $initial   Whether to use initial calendar names. Default true.
    	 *     @type bool   $display   Whether to display the calendar output. Default true.
    	 *     @type string $post_type Optional. Post type. Default 'post'.
    	 * }
    	 */
    	$calendar_output = apply_filters( 'get_calendar', $calendar_output, $args );

    	if ( $args['display'] ) {
    		echo $calendar_output;
    		return;
    	}

    	return $calendar_output;
    }
    ```

[View all references](https://developer.wordpress.org/reference/files/wp-includes/general-template.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/6.9.4/src/wp-includes/general-template.php#L2261)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/6.9.4/src/wp-includes/general-template.php#L2261-L2601)

## 󠀁[Hooks](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#hooks)󠁿

 [apply_filters( ‘get_calendar’, string $calendar_output, array $args )](https://developer.wordpress.org/reference/hooks/get_calendar/)

Filters the HTML calendar output.

 [apply_filters( ‘get_calendar_args’, array $args )](https://developer.wordpress.org/reference/hooks/get_calendar_args/)

Filter the `get_calendar` function arguments before they are used.

## 󠀁[Related](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#related)󠁿

| Uses | Description | 
| [wp_recursive_ksort()](https://developer.wordpress.org/reference/functions/wp_recursive_ksort/)`wp-includes/functions.php` |

Sorts the keys of an array alphabetically.

  | 
| [wp_cache_set()](https://developer.wordpress.org/reference/functions/wp_cache_set/)`wp-includes/cache.php` |

Saves the data to the cache.

  | 
| [zeroise()](https://developer.wordpress.org/reference/functions/zeroise/)`wp-includes/formatting.php` |

Add leading zeros when necessary.

  | 
| [calendar_week_mod()](https://developer.wordpress.org/reference/functions/calendar_week_mod/)`wp-includes/general-template.php` |

Gets number of days since the start of the week.

  | 
| [current_time()](https://developer.wordpress.org/reference/functions/current_time/)`wp-includes/functions.php` |

Retrieves the current time based on specified type.

  | 
| [WP_Locale::get_month()](https://developer.wordpress.org/reference/classes/wp_locale/get_month/)`wp-includes/class-wp-locale.php` |

Retrieves the full translated month by month number.

  | 
| [WP_Locale::get_month_abbrev()](https://developer.wordpress.org/reference/classes/wp_locale/get_month_abbrev/)`wp-includes/class-wp-locale.php` |

Retrieves translated version of month abbreviation string.

  | 
| [WP_Locale::get_weekday()](https://developer.wordpress.org/reference/classes/wp_locale/get_weekday/)`wp-includes/class-wp-locale.php` |

Retrieves the full translated weekday word.

  | 
| [WP_Locale::get_weekday_initial()](https://developer.wordpress.org/reference/classes/wp_locale/get_weekday_initial/)`wp-includes/class-wp-locale.php` |

Retrieves the translated weekday initial.

  | 
| [WP_Locale::get_weekday_abbrev()](https://developer.wordpress.org/reference/classes/wp_locale/get_weekday_abbrev/)`wp-includes/class-wp-locale.php` |

Retrieves the translated weekday abbreviation.

  | 
| [get_day_link()](https://developer.wordpress.org/reference/functions/get_day_link/)`wp-includes/link-template.php` |

Retrieves the permalink for the day archives with year and month.

  | 
| [get_month_link()](https://developer.wordpress.org/reference/functions/get_month_link/)`wp-includes/link-template.php` |

Retrieves the permalink for the month archives with year.

  | 
| [post_type_exists()](https://developer.wordpress.org/reference/functions/post_type_exists/)`wp-includes/post.php` |

Determines whether a post type is registered.

  | 
| [wpdb::get_row()](https://developer.wordpress.org/reference/classes/wpdb/get_row/)`wp-includes/class-wpdb.php` |

Retrieves one row from the database.

  | 
| [wp_cache_get()](https://developer.wordpress.org/reference/functions/wp_cache_get/)`wp-includes/cache.php` |

Retrieves the cache contents from the cache by key and group.

  | 
| [_x()](https://developer.wordpress.org/reference/functions/_x/)`wp-includes/l10n.php` |

Retrieves translated string with gettext context.

  | 
| [__()](https://developer.wordpress.org/reference/functions/__/)`wp-includes/l10n.php` |

Retrieves the translation of $text.

  | 
| [esc_attr()](https://developer.wordpress.org/reference/functions/esc_attr/)`wp-includes/formatting.php` |

Escaping for HTML attributes.

  | 
| [wp_parse_args()](https://developer.wordpress.org/reference/functions/wp_parse_args/)`wp-includes/functions.php` |

Merges user defined arguments into defaults array.

  | 
| [apply_filters()](https://developer.wordpress.org/reference/functions/apply_filters/)`wp-includes/plugin.php` |

Calls the callback functions that have been added to a filter hook.

  | 
| [get_option()](https://developer.wordpress.org/reference/functions/get_option/)`wp-includes/option.php` |

Retrieves an option value based on an option name.

  | 
| [wpdb::get_var()](https://developer.wordpress.org/reference/classes/wpdb/get_var/)`wp-includes/class-wpdb.php` |

Retrieves one value from the database.

  | 
| [wpdb::get_results()](https://developer.wordpress.org/reference/classes/wpdb/get_results/)`wp-includes/class-wpdb.php` |

Retrieves an entire SQL result set from the database (i.e., many rows).

  | 
| [wpdb::prepare()](https://developer.wordpress.org/reference/classes/wpdb/prepare/)`wp-includes/class-wpdb.php` |

Prepares a SQL query for safe execution.

  |

[Show 19 more](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#)
[Show less](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#)

| Used by | Description | 
| [WP_Widget_Calendar::widget()](https://developer.wordpress.org/reference/classes/wp_widget_calendar/widget/)`wp-includes/widgets/class-wp-widget-calendar.php` |

Outputs the content for the current Calendar widget instance.

  |

## 󠀁[Changelog](https://developer.wordpress.org/reference/functions/get_calendar/?output_format=md#changelog)󠁿

| Version | Description | 
| [6.8.0](https://developer.wordpress.org/reference/since/6.8.0/) | Added the `$args` parameter, with backward compatibility for the replaced `$initial` and `$display` parameters. | 
| [1.0.0](https://developer.wordpress.org/reference/since/1.0.0/) | Introduced. |

## User Contributed Notes

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fget_calendar%2F)
before being able to contribute a note or feedback.