1, 'resolved_at' => 2, 'first_reply_at' => 4 ); const VERSION = 7; public static function utc( $timestamp = null ) { return gmdate( 'Y-m-d H:i:s', null === $timestamp ? time() : (int) $timestamp ); } /** Strict parsing: never normalize an invalid stored date into another date. */ public static function parse( $value ) { if ( ! is_string( $value ) || ! preg_match( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/D', $value ) ) { return null; } $date = \DateTimeImmutable::createFromFormat( '!Y-m-d H:i:s', $value, new \DateTimeZone( 'UTC' ) ); return $date && $date->format( 'Y-m-d H:i:s' ) === $value ? $date->getTimestamp() : null; } public static function known( array $entry, $column = 'created_at' ) { return isset( self::FIELDS[ $column ] ) && ( (int) ( $entry['time_version'] ?? 0 ) & self::FIELDS[ $column ] ) !== 0; } public static function timestamp( array $entry, $column = 'created_at' ) { return self::known( $entry, $column ) ? self::parse( $entry[ $column ] ?? null ) : null; } public static function display( array $entry, $column = 'created_at', $format = '' ) { $value = (string) ( $entry[ $column ] ?? '' ); if ( ! $value ) { return ''; } $timestamp = self::timestamp( $entry, $column ); if ( null === $timestamp ) { return $value . ' (' . __( 'time zone unconfirmed', 'accessible-forms-by-accessible-org' ) . ')'; } return wp_date( $format ?: get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $timestamp ); } /** Exports and integrations have an explicit UTC offset, or an explicit uncertainty label. */ public static function export( array $entry, $column = 'created_at' ) { $timestamp = self::timestamp( $entry, $column ); return null === $timestamp ? self::display( $entry, $column ) : gmdate( 'Y-m-d\TH:i:s\Z', $timestamp ); } /** Inclusive start, exclusive next-day start; calendar days can have 23 or 25 hours. */ public static function day_bounds( $value ) { $date = is_string( $value ) ? \DateTimeImmutable::createFromFormat( '!Y-m-d', $value, wp_timezone() ) : false; if ( ! $date || $date->format( 'Y-m-d' ) !== $value ) { return null; } return array( self::utc( $date->getTimestamp() ), self::utc( $date->modify( '+1 day' )->getTimestamp() ) ); } /** * Enumerate real instants for a historical wall-clock value. Repeated DST hours * require an explicit earlier/later policy; nonexistent times are never guessed. */ public static function historical( $value, \DateTimeZone $zone, $policy = 'preserve' ) { $naive = self::parse( $value ); if ( null === $naive ) { return null; } $offsets = array( $zone->getOffset( new \DateTimeImmutable( '@' . $naive ) ) ); foreach ( $zone->getTransitions( $naive - 2 * DAY_IN_SECONDS, $naive + 2 * DAY_IN_SECONDS ) ?: array() as $change ) { $offsets[] = $change['offset']; } $candidates = array(); foreach ( array_unique( $offsets ) as $offset ) { $instant = $naive - $offset; if ( ( new \DateTimeImmutable( '@' . $instant ) )->setTimezone( $zone )->format( 'Y-m-d H:i:s' ) === $value ) { $candidates[] = $instant; } } sort( $candidates ); if ( 1 === count( $candidates ) ) { return self::utc( $candidates[0] ); } if ( count( $candidates ) > 1 && in_array( $policy, array( 'earlier', 'later' ), true ) ) { return self::utc( 'earlier' === $policy ? reset( $candidates ) : end( $candidates ) ); } return null; } }