Commit c812ac0090 for wordpress.org

commit c812ac00908a0dbdcf0bb275fe281392f014dce0
Author: westonruter <westonruter@git.wordpress.org>
Date:   Fri Nov 13 23:32:54 2026 +0000

    Date/Time: Pass only valid weekday numbers to `WP_Locale`.

    The PHP warning that `WP_Locale::get_weekday()` raises for an out-of-range weekday is already useful feedback, so no runtime validation is added. Instead, its parameter type is narrowed for static analysis, along with those of `get_month_genitive()` and `get_meridiem()`. The core callers that the narrower types exposed are fixed. The parameter of `get_month()` is left as is, since in r59870 it was made to tolerate unvalidated input.

    The fixes change behavior in a few edge cases. A post whose date cannot be parsed is no longer labeled as Sunday by `the_weekday()` and `the_weekday_date()`; both filters still run. A numeric timestamp that passes the `is_numeric()` check but cannot be parsed now makes `wp_date()` return false instead of causing a fatal error. The calendar keeps wrapping an out-of-range `start_of_week` value modulo 7, so a value of 7 still means Sunday, consistent with the weekly archive query.

    To let the weekday type flow through, the return type of `get_post_time()` is narrowed per date format, and the `get_post_time` filter now documents that its value must match what the format produces.

    Developed in https://github.com/WordPress/wordpress-develop/pull/14827.
    Follow-up to r36292, r45379, r45901, r59870, r63941.

    Props raulsalvat, westonruter, shishir1, swissspidy.
    See #34688.
    Fixes #64522.

    Built from https://develop.svn.wordpress.org/trunk@65525


    git-svn-id: http://core.svn.wordpress.org/trunk@64598 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/class-wp-locale.php b/wp-includes/class-wp-locale.php
index 3671c1b279..cd022fdaf9 100644
--- a/wp-includes/class-wp-locale.php
+++ b/wp-includes/class-wp-locale.php
@@ -264,6 +264,8 @@ class WP_Locale {
 	 *
 	 * @param int $weekday_number 0 for Sunday through 6 Saturday.
 	 * @return string Full translated weekday.
+	 *
+	 * @phpstan-param int<0, 6> $weekday_number
 	 */
 	public function get_weekday( $weekday_number ) {
 		return $this->weekday[ $weekday_number ];
@@ -355,6 +357,8 @@ class WP_Locale {
 	 *
 	 * @param string|int $month_number '01' through '12'.
 	 * @return string Translated genitive month name.
+	 *
+	 * @phpstan-param int<1, 12>|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'10'|'11'|'12'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09' $month_number
 	 */
 	public function get_month_genitive( $month_number ) {
 		return $this->month_genitive[ zeroise( $month_number, 2 ) ];
@@ -369,6 +373,8 @@ class WP_Locale {
 	 *
 	 * @param string $meridiem Either 'am', 'pm', 'AM', or 'PM'. Not translated version.
 	 * @return string Translated version
+	 *
+	 * @phpstan-param 'am'|'pm'|'AM'|'PM' $meridiem
 	 */
 	public function get_meridiem( $meridiem ) {
 		return $this->meridiem[ $meridiem ];
diff --git a/wp-includes/functions.php b/wp-includes/functions.php
index 10449deeba..d9927baffb 100644
--- a/wp-includes/functions.php
+++ b/wp-includes/functions.php
@@ -258,6 +258,11 @@ function wp_date( $format, $timestamp = null, $timezone = null ) {
 	}

 	$datetime = date_create( '@' . $timestamp );
+
+	if ( false === $datetime ) {
+		return false;
+	}
+
 	$datetime->setTimezone( $timezone );

 	if ( empty( $wp_locale->month ) || empty( $wp_locale->weekday ) ) {
@@ -269,7 +274,7 @@ function wp_date( $format, $timestamp = null, $timezone = null ) {
 		$new_format    = '';
 		$format_length = strlen( $format );
 		$month         = $wp_locale->get_month( $datetime->format( 'm' ) );
-		$weekday       = $wp_locale->get_weekday( $datetime->format( 'w' ) );
+		$weekday       = $wp_locale->get_weekday( (int) $datetime->format( 'w' ) );

 		for ( $i = 0; $i < $format_length; $i++ ) {
 			switch ( $format[ $i ] ) {
diff --git a/wp-includes/general-template.php b/wp-includes/general-template.php
index 4c6945a626..bfb400cbda 100644
--- a/wp-includes/general-template.php
+++ b/wp-includes/general-template.php
@@ -2666,7 +2666,7 @@ function get_calendar( $args = array() ) {
 	}

 	// week_begins = 0 stands for Sunday.
-	$week_begins = (int) get_option( 'start_of_week' );
+	$week_begins = absint( get_option( 'start_of_week' ) ) % 7;

 	// Read the current date.
 	list( $current_year, $current_month, $current_day ) = array_map( 'intval', explode( '-', current_time( 'Y-m-j' ) ) );
@@ -3181,7 +3181,21 @@ function get_the_time( $format = '', $post = null ) {
  * @return string|int|false Formatted date string or Unix timestamp if `$format` is 'U' or 'G'.
  *                          False on failure.
  *
- * @phpstan-return ( $format is 'U'|'G' ? int|false : string|false )
+ * @phpstan-return (
+ *     $format is 'U'|'G' ? int|false : (
+ *     $format is 'w' ? '0'|'1'|'2'|'3'|'4'|'5'|'6'|false : (
+ *     $format is 'N' ? '1'|'2'|'3'|'4'|'5'|'6'|'7'|false : (
+ *     $format is 'L'|'I' ? '0'|'1'|false : (
+ *     $format is 't' ? '28'|'29'|'30'|'31'|false : (
+ *     $format is 'm'|'h' ? '01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09'|'10'|'11'|'12'|false : (
+ *     $format is 'n'|'g' ? '1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'10'|'11'|'12'|false : (
+ *     $format is 'H' ? '00'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09'|'10'|'11'|'12'|'13'|'14'|'15'|'16'|'17'|'18'|'19'|'20'|'21'|'22'|'23'|false : (
+ *     $format is 'd' ? '01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09'|'10'|'11'|'12'|'13'|'14'|'15'|'16'|'17'|'18'|'19'|'20'|'21'|'22'|'23'|'24'|'25'|'26'|'27'|'28'|'29'|'30'|'31'|false : (
+ *     $format is 'j' ? '1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'10'|'11'|'12'|'13'|'14'|'15'|'16'|'17'|'18'|'19'|'20'|'21'|'22'|'23'|'24'|'25'|'26'|'27'|'28'|'29'|'30'|'31'|false : (
+ *     $format is 'z'|'W'|'o'|'Y'|'y'|'B'|'i'|'s'|'u'|'v'|'Z' ? numeric-string|false :
+ *     string|false
+ *     ) ) ) ) ) ) ) ) ) )
+ * )
  */
 function get_post_time( $format = 'U', $gmt = false, $post = null, $translate = false ) {
 	$post = get_post( $post );
@@ -3217,8 +3231,14 @@ function get_post_time( $format = 'U', $gmt = false, $post = null, $translate =
 	/**
 	 * Filters the localized time of the post.
 	 *
+	 * The filtered value should match the type and values that `$format` produces,
+	 * for example, an integer for 'U' or 'G', or a numeric string from '0' to '6' for 'w'.
+	 *
 	 * @since 2.6.0
 	 *
+	 * @see get_post_time()
+	 * @link https://www.php.net/manual/en/datetime.format.php
+	 *
 	 * @param string|int $time   Formatted date string or Unix timestamp if `$format` is 'U' or 'G'.
 	 * @param string     $format Format to use for retrieving the date of the post.
 	 *                           Accepts 'G', 'U', or PHP date format.
@@ -3424,6 +3444,7 @@ function get_post_modified_time( $format = 'U', $gmt = false, $post = null, $tra
  * Displays the localized weekday for the post.
  *
  * @since 0.71
+ * @since 7.2.0 Passes an empty string to the filter instead of 'Sunday' if the post date is invalid.
  *
  * @global WP_Locale $wp_locale WordPress date and time locale object.
  */
@@ -3436,7 +3457,12 @@ function the_weekday() {
 		return;
 	}

-	$the_weekday = $wp_locale->get_weekday( get_post_time( 'w', false, $post ) );
+	$the_weekday    = '';
+	$weekday_number = get_post_time( 'w', false, $post );
+
+	if ( false !== $weekday_number ) {
+		$the_weekday = $wp_locale->get_weekday( (int) $weekday_number );
+	}

 	/**
 	 * Filters the localized weekday of the post, for display.
@@ -3455,6 +3481,7 @@ function the_weekday() {
  * the previous one output.
  *
  * @since 0.71
+ * @since 7.2.0 Outputs nothing instead of 'Sunday' if the post date is invalid.
  *
  * @global WP_Locale $wp_locale       WordPress date and time locale object.
  * @global string    $currentday      The day of the current post in the loop.
@@ -3475,10 +3502,15 @@ function the_weekday_date( $before = '', $after = '' ) {
 	$the_weekday_date = '';

 	if ( $currentday !== $previousweekday ) {
-		$the_weekday_date .= $before;
-		$the_weekday_date .= $wp_locale->get_weekday( get_post_time( 'w', false, $post ) );
-		$the_weekday_date .= $after;
-		$previousweekday   = $currentday;
+		$weekday_number = get_post_time( 'w', false, $post );
+
+		if ( false !== $weekday_number ) {
+			$the_weekday_date .= $before;
+			$the_weekday_date .= $wp_locale->get_weekday( (int) $weekday_number );
+			$the_weekday_date .= $after;
+		}
+
+		$previousweekday = $currentday;
 	}

 	/**
diff --git a/wp-includes/version.php b/wp-includes/version.php
index e01110c13b..38d2c0162a 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta5-65524';
+$wp_version = '7.2-beta5-65525';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.