From 4b1f317d1e7c530436d8277581bf00c64be443c7 Mon Sep 17 00:00:00 2001 From: Flint Date: Fri, 21 Aug 2026 16:03:00 -0400 Subject: [PATCH 1/2] Normalize date-type user field values to Y-m-d on import Date user fields render as , which requires the stored meta to be exactly YYYY-MM-DD. Values imported in other formats (e.g. 7/26/22) were stored raw, rendering blank on profile edit screens and risking silent data wipe on the next profile save. Parseable values are reformatted via strtotime(); unparseable values are left unchanged so they stay visible and correctable, matching the existing option-key mapping philosophy in pmproiucsv_normalize_user_field_meta_value(). Co-Authored-By: Claude Fable 5 --- includes/pmpro-membership-data.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/includes/pmpro-membership-data.php b/includes/pmpro-membership-data.php index 3d7ce6d..4d871bd 100644 --- a/includes/pmpro-membership-data.php +++ b/includes/pmpro-membership-data.php @@ -413,6 +413,15 @@ function pmproiucsv_normalize_user_field_meta_value( $metavalue, $metakey ) { return $metavalue; } + if ( 'date' === $field->type ) { + if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $metavalue ) ) { + return $metavalue; + } + + $timestamp = strtotime( $metavalue ); + return false !== $timestamp ? date( 'Y-m-d', $timestamp ) : $metavalue; + } + // Prefer core helper when present; keep a local type check for older PMPro. $is_multi = false; if ( method_exists( $field, 'stores_array_values' ) ) { From d0541859c3cff175ea7286f632dd8264dda95f17 Mon Sep 17 00:00:00 2001 From: Flint Date: Fri, 21 Aug 2026 16:19:03 -0400 Subject: [PATCH 2/2] Reject calendar-invalid dates instead of rolling them forward Council review caught that strtotime() silently normalizes invalid dates (2/30/2022 becomes 2022-03-02), corrupting typos into plausible wrong values. Switch to date_parse() + checkdate() so only calendar-valid absolute dates are reformatted; everything else is stored unchanged. Relative strings ("+1 week") are now also rejected. Docblock updated for the wider scope. Co-Authored-By: Claude Fable 5 --- includes/pmpro-membership-data.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/includes/pmpro-membership-data.php b/includes/pmpro-membership-data.php index 4d871bd..a8ae7e2 100644 --- a/includes/pmpro-membership-data.php +++ b/includes/pmpro-membership-data.php @@ -390,7 +390,7 @@ function pmproiucsv_required_pmpro_import_headers( $required_headers ) { add_filter( 'pmproiucsv_required_import_headers', 'pmproiucsv_required_pmpro_import_headers', 10, 1 ); /** - * Normalize multi-value User Field CSV cells to arrays. + * Normalize multi-value User Field CSV cells to arrays and date field values to Y-m-d. * * @since TBD * @@ -418,8 +418,17 @@ function pmproiucsv_normalize_user_field_meta_value( $metavalue, $metakey ) { return $metavalue; } - $timestamp = strtotime( $metavalue ); - return false !== $timestamp ? date( 'Y-m-d', $timestamp ) : $metavalue; + // date_parse() flags calendar-invalid dates (e.g. 2/30/2022) that strtotime() would silently roll forward. + $parsed = date_parse( $metavalue ); + if ( + empty( $parsed['error_count'] ) && empty( $parsed['warning_count'] ) + && false !== $parsed['year'] && false !== $parsed['month'] && false !== $parsed['day'] + && checkdate( $parsed['month'], $parsed['day'], $parsed['year'] ) + ) { + return sprintf( '%04d-%02d-%02d', $parsed['year'], $parsed['month'], $parsed['day'] ); + } + + return $metavalue; } // Prefer core helper when present; keep a local type check for older PMPro.