Repository: PostHog/posthog
Pull request #30171: refactor(experiments): edit experiment end date.
Author: login: rodrigoi
Labels: name: team/experiments
Created: 2025-03-19T17:51:29Z | Merged: 2025-03-28T13:15:14Z

## Problem

Modifying the dates of an experiment is a common practice, and our current interface only allows modifying the _start date_. This PR adds support for changing the _end date_ as well.

<!-- Who are we building for, what are their needs, why is this important? -->

<!-- Does this fix an issue? Uncomment the line below with the issue ID to automatically close it when merged -->
<!-- Closes #ISSUE_ID -->

## Changes

We update the _Experiment_ logic and scenes to show an edit option when the experiment has an end date. We don't do validation other than that on the front end because only `completed` experiments have _end date_.

| Before | After |
| - | - |
| <img width="330" alt="image" src="https://github.com/user-attachments/assets/3e679d7e-5e96-4868-b57b-24c2548010d7" /> | <img width="365" alt="image" src="https://github.com/user-attachments/assets/b5a6f1f6-d24c-4e35-b490-5736f360071c" /> |

Also, thanks to changes introduced in #30259, the _end date_ cannot be set before the start date.

| <img width="585" alt="image" src="https://github.com/user-attachments/assets/4277c006-db77-4486-9660-acbcc822bd85" /> | <img width="589" alt="image" src="https://github.com/user-attachments/assets/b172e53e-6cc6-4f50-87dd-c2cbe376949c" /> |
| - | - |
| Valid Date Selection | Selecting a Date before the _Start Date_ |

We also take the opportunity to refactor some of the scene code by creating a new component to handle the inline form edition.

<!-- If there are frontend changes, please include screenshots. -->
<!-- If a reference design was involved, include a link to the relevant Figma frame! -->

> [!IMPORTANT]
> 👉 _Stay up-to-date with [PostHog coding conventions](https://posthog.com/docs/contribute/coding-conventions) for a smoother review._

> [!NOTE]
> Depends on #30259 which introduces support for date limits on the `LemonCalendarSelect` component.
> Depends on #30491 for backend validations.

## Does this work well for both Cloud and self-hosted?

<!-- Yes / no / it doesn't have an impact. -->

## How did you test this code?

<!-- Briefly describe the steps you took. -->
<!-- Include automated tests if possible, otherwise describe the manual testing routine. -->



Diff:
diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts
index ec489530ef88..4cad0643c80f 100644
--- a/frontend/src/lib/utils/eventUsageLogic.ts
+++ b/frontend/src/lib/utils/eventUsageLogic.ts
@@ -406,6 +406,10 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
             experiment,
             newStartDate,
         }),
+        reportExperimentEndDateChange: (experiment: Experiment, newEndDate: string) => ({
+            experiment,
+            newEndDate,
+        }),
         reportExperimentCompleted: (
             experiment: Experiment,
             endDate: Dayjs,
@@ -979,6 +983,13 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
                 new_start_date: newStartDate,
             })
         },
+        reportExperimentEndDateChange: ({ experiment, newEndDate }) => {
+            posthog.capture('experiment end date changed', {
+                ...getEventPropertiesForExperiment(experiment),
+                old_end_date: experiment.end_date,
+                new_end_date: newEndDate,
+            })
+        },
         reportExperimentCompleted: ({ experiment, endDate, duration, significant }) => {
             posthog.capture('experiment completed', {
                 ...getEventPropertiesForExperiment(experiment),
diff --git a/frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx b/frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
new file mode 100644
index 000000000000..f8da128cf4bc
--- /dev/null
+++ b/frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
@@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr={dataAttr}>
+            <div className={clsx('text-xs font-semibold uppercase tracking-wide', isDatePickerOpen && 'text-center')}>
+                {label}
+            </div>
+            <div className="flex">
+                {isDatePickerOpen ? (
+                    <LemonCalendarSelectInput
+                        granularity="minute"
+                        visible
+                        value={dayjs(date)}
+                        onChange={(newDate) => {
+                            if (newDate && onChange) {
+                                onChange(newDate.toISOString())
+                            }
+                        }}
+                        onClose={() => setIsDatePickerOpen(false)}
+                        onClickOutside={() => setIsDatePickerOpen(false)}
+                        clearable={false}
+                        selectionPeriod="past"
+                        buttonProps={{ size: 'xsmall', 'data-attr': `${dataAttr}-picker` }}
+                        selectionPeriodLimit={selectionLimitDate ? dayjs(selectionLimitDate) : undefined}
+                    />
+                ) : (
+                    <>
+                        <TZLabel time={date} />
+                        <LemonButton
+                            title={`Move ${label}`}
+                            data-attr={`move-${dataAttr}`}
+                            icon={<IconPencil />}
+                            size="small"
+                            onClick={() => setIsDatePickerOpen(true)}
+                            noPadding
+                            className="ml-2"
+                        />
+                    </>
+                )}
+            </div>
+        </div>
+    )
+}
+
+export default ExperimentDate
diff --git a/frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx b/frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx
index f3f0f39e1440..b56f92ca80f5 100644
--- a/frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx
@@ -1,23 +1,22 @@
-import { IconPencil } from '@posthog/icons'
-import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
-import clsx from 'clsx'
 import { useActions, useValues } from 'kea'
 import { TZLabel } from 'lib/components/TZLabel'
-import { dayjs } from 'lib/dayjs'
-import { useState } from 'react'
 
 import { experimentLogic } from '../experimentLogic'
+import ExperimentDate from './ExperimentDate'
 
-export function ExperimentDates(): JSX.Element {
-    const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false)
+export function ExperimentDates(): JSX.Element | null {
     const { experiment } = useValues(experimentLogic)
-    const { changeExperimentStartDate } = useActions(experimentLogic)
+    const { changeExperimentStartDate, changeExperimentEndDate } = useActions(experimentLogic)
     const { created_at, start_date, end_date } = experiment
 
-    if (!start_date) {
-        if (!created_at) {
-            return <></>
-        }
+    // If the experiment has no start date and no creation date, don't show anything
+    if (!start_date && !created_at) {
+        return null
+    }
+
+    // If the experiment has no start date but has a creation date, show the creation date.
+    // This also narrows the type of created_at to be non-null
+    if (!start_date && created_at) {
         return (
             <div className="block" data-attr="experiment-creation-date">
                 <div className="text-xs font-semibold uppercase tracking-wide">Creation date</div>
@@ -25,59 +24,23 @@ export function ExperimentDates(): JSX.Element {
             </div>
         )
     }
+
+    // If the experiment has a start date, show the start date and end date
     return (
         <>
-            <div className="block" data-attr="experiment-start-date">
-                <div
-                    className={clsx(
-                        'text-xs font-semibold uppercase tracking-wide',
-                        isStartDatePickerOpen && 'text-center'
-                    )}
-                >
-                    Start date
-                </div>
-                <div className="flex">
-                    {isStartDatePickerOpen ? (
-                        <LemonCalendarSelectInput
-                            granularity="minute"
-                            visible
-                            value={dayjs(start_date)}
-                            onChange={(newStartDate) => {
-                                if (newStartDate) {
-                                    changeExperimentStartDate(newStartDate.toISOString())
-                                }
-                            }}
-                            onClose={() => setIsStartDatePickerOpen(false)}
-                            onClickOutside={() => setIsStartDatePickerOpen(false)}
-                            clearable={false}
-                            selectionPeriod="past"
-                            buttonProps={{ size: 'xsmall', 'data-attr': 'experiment-start-date-picker' }}
-                        />
-                    ) : (
-                        <>
-                            <TZLabel time={start_date} />
-                            <LemonButton
-                                title="Move start date"
-                                data-attr="move-experiment-start-date"
-                                icon={<IconPencil />}
-                                size="small"
-                                onClick={() => setIsStartDatePickerOpen(true)}
-                                noPadding
-                                className="ml-2"
-                            />
-                        </>
-                    )}
-                </div>
-            </div>
-            {end_date && (
-                <div className="block" data-attr="experiment-end-date">
-                    <div className="text-xs font-semibold uppercase tracking-wide">End date</div>
-                    {/* Flex class here is for the end date to have same appearance as the start date. */}
-                    <div className="flex">
-                        <TZLabel time={end_date} />
-                    </div>
-                </div>
-            )}
+            <ExperimentDate
+                label="Start Date"
+                date={start_date}
+                data-attr="experiment-start-date"
+                onChange={changeExperimentStartDate}
+            />
+            <ExperimentDate
+                label="End Date"
+                date={end_date}
+                data-attr="experiment-end-date"
+                selectionLimitDate={start_date}
+                onChange={changeExperimentEndDate}
+            />
         </>
     )
 }
diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx
index 23058acf3036..a8dae15ba4bf 100644
--- a/frontend/src/scenes/experiments/experimentLogic.tsx
+++ b/frontend/src/scenes/experiments/experimentLogic.tsx
@@ -12,11 +12,11 @@ import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
 import { ProductIntentContext } from 'lib/utils/product-intents'
 import { addProjectIdIfMissing } from 'lib/utils/router-utils'
 import {
+    featureFlagLogic as sceneFeatureFlagLogic,
     indexToVariantKeyFeatureFlagPayloads,
     validateFeatureFlagKey,
     variantKeyToIndexFeatureFlagPayloads,
 } from 'scenes/feature-flags/featureFlagLogic'
-import { featureFlagLogic as sceneFeatureFlagLogic } from 'scenes/feature-flags/featureFlagLogic'
 import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic'
 import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic'
 import { insightDataLogic } from 'scenes/insights/insightDataLogic'
@@ -253,6 +253,7 @@ export const experimentLogic = kea<experimentLogicType>([
         updateExperimentCollectionGoal: true,
         updateExposureCriteria: true,
         changeExperimentStartDate: (startDate: string) => ({ startDate }),
+        changeExperimentEndDate: (endDate: string) => ({ endDate }),
         setExperimentStatsVersion: (version: number) => ({ version }),
         launchExperiment: true,
         endExperiment: true,
@@ -914,6 +915,10 @@ export const experimentLogic = kea<experimentLogicType>([
             actions.updateExperiment({ start_date: startDate })
             values.experiment && eventUsageLogic.actions.reportExperimentStartDateChange(values.experiment, startDate)
         },
+        changeExperimentEndDate: async ({ endDate }) => {
+            actions.updateExperiment({ end_date: endDate })
+            values.experiment && eventUsageLogic.actions.reportExperimentEndDateChange(values.experiment, endDate)
+        },
         setExperimentStatsVersion: async ({ version }, breakpoint) => {
             actions.updateExperiment({ stats_config: { version } })
             await breakpoint(100)


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

Here's a concise summary of the key changes in this pull request:

Added functionality to edit experiment end dates with validation and UI improvements:

- Created new `ExperimentDate` component in `/frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx` for consistent date editing UI
- Modified `ExperimentDates` component to support both start and end date editing with proper validation
- Added end date change tracking via `reportExperimentEndDateChange` in `eventUsageLogic.ts`
- Implemented date validation to prevent end dates from being set before start dates
- Added `changeExperimentEndDate` action in `experimentLogic.tsx` to handle end date updates

The changes improve experiment management by allowing users to modify both start and end dates through a consistent interface with proper validation and tracking.

<sub>4 file(s) reviewed, 4 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=posthog_30171)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:01:56Z
user: login: rodrigoi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T02:53:43Z
user: login: rodrigoi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T02:56:57Z
user: login: rodrigoi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T03:00:08Z
user: login: rodrigoi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T03:05:22Z
user: login: andehen
body: Good job! 👍 

Well structured in a new component, usage reporting place and well explained.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T11:19:43Z

Review comments:
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: greptile-apps[bot]
body: logic: data-attr is hardcoded as 'experiment-start-date' but this component is used for both start and end dates

```suggestion
        <div className="block" data-attr={dataAttr}>
```
created_at: 2025-03-27T15:01:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
+            <div className={clsx('text-xs font-semibold uppercase tracking-wide', isDatePickerOpen && 'text-center')}>
+                {label}
+            </div>
+            <div className="flex">
+                {isDatePickerOpen ? (
+                    <LemonCalendarSelectInput
+                        granularity="minute"
+                        visible
+                        value={dayjs(date)}
+                        onChange={(newDate) => {
+                            if (newDate && onChange) {
+                                onChange(newDate.toISOString())
+                            }
+                        }}
+                        onClose={() => setIsDatePickerOpen(false)}
+                        onClickOutside={() => setIsDatePickerOpen(false)}
+                        clearable={false}
+                        selectionPeriod="past"
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: greptile-apps[bot]
body: logic: selectionPeriod is hardcoded as 'past' but for end dates we may want to allow future dates
created_at: 2025-03-27T15:01:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
+            <div className={clsx('text-xs font-semibold uppercase tracking-wide', isDatePickerOpen && 'text-center')}>
+                {label}
+            </div>
+            <div className="flex">
+                {isDatePickerOpen ? (
+                    <LemonCalendarSelectInput
+                        granularity="minute"
+                        visible
+                        value={dayjs(date)}
+                        onChange={(newDate) => {
+                            if (newDate && onChange) {
+                                onChange(newDate.toISOString())
+                            }
+                        }}
+                        onClose={() => setIsDatePickerOpen(false)}
+                        onClickOutside={() => setIsDatePickerOpen(false)}
+                        clearable={false}
+                        selectionPeriod="past"
+                        buttonProps={{ size: 'xsmall', 'data-attr': `${dataAttr}-picker` }}
+                        selectionPeriodLimit={selectionLimitDate ? dayjs(selectionLimitDate) : undefined}
+                    />
+                ) : (
+                    <>
+                        <TZLabel time={date} />
+                        <LemonButton
+                            title="Move start date"
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: greptile-apps[bot]
body: logic: title is hardcoded as 'Move start date' but this component is used for both start and end dates

```suggestion
                            title={`Move ${label.toLowerCase()}`}
```
created_at: 2025-03-27T15:01:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,83 +1,46 @@
-import { IconPencil } from '@posthog/icons'
-import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
-import clsx from 'clsx'
 import { useActions, useValues } from 'kea'
 import { TZLabel } from 'lib/components/TZLabel'
-import { dayjs } from 'lib/dayjs'
-import { useState } from 'react'
 
 import { experimentLogic } from '../experimentLogic'
+import ExperimentDate from './ExperimentDate'
 
 export function ExperimentDates(): JSX.Element {
-    const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false)
     const { experiment } = useValues(experimentLogic)
-    const { changeExperimentStartDate } = useActions(experimentLogic)
+    const { changeExperimentStartDate, changeExperimentEndDate } = useActions(experimentLogic)
     const { created_at, start_date, end_date } = experiment
 
-    if (!start_date) {
-        if (!created_at) {
-            return <></>
-        }
+    // If the experiment has no start date and no creation date, don't show anything
+    if (!start_date && !created_at) {
+        return <></>
+    }
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx
user: login: greptile-apps[bot]
body: style: Consider adding a warning or placeholder instead of returning empty fragment when no dates exist
created_at: 2025-03-27T15:01:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
+            <div className={clsx('text-xs font-semibold uppercase tracking-wide', isDatePickerOpen && 'text-center')}>
+                {label}
+            </div>
+            <div className="flex">
+                {isDatePickerOpen ? (
+                    <LemonCalendarSelectInput
+                        granularity="minute"
+                        visible
+                        value={dayjs(date)}
+                        onChange={(newDate) => {
+                            if (newDate && onChange) {
+                                onChange(newDate.toISOString())
+                            }
+                        }}
+                        onClose={() => setIsDatePickerOpen(false)}
+                        onClickOutside={() => setIsDatePickerOpen(false)}
+                        clearable={false}
+                        selectionPeriod="past"
+                        buttonProps={{ size: 'xsmall', 'data-attr': `${dataAttr}-picker` }}
+                        selectionPeriodLimit={selectionLimitDate ? dayjs(selectionLimitDate) : undefined}
+                    />
+                ) : (
+                    <>
+                        <TZLabel time={date} />
+                        <LemonButton
+                            title="Move start date"
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: rodrigoi
body: fixed on cd4661fa2c12b543bf2aa574df8a468d3a572cf0 using just the provided `label`.
created_at: 2025-03-28T02:53:43Z
author_association: MEMBER
diff_hunk: @@ -1,83 +1,46 @@
-import { IconPencil } from '@posthog/icons'
-import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
-import clsx from 'clsx'
 import { useActions, useValues } from 'kea'
 import { TZLabel } from 'lib/components/TZLabel'
-import { dayjs } from 'lib/dayjs'
-import { useState } from 'react'
 
 import { experimentLogic } from '../experimentLogic'
+import ExperimentDate from './ExperimentDate'
 
 export function ExperimentDates(): JSX.Element {
-    const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false)
     const { experiment } = useValues(experimentLogic)
-    const { changeExperimentStartDate } = useActions(experimentLogic)
+    const { changeExperimentStartDate, changeExperimentEndDate } = useActions(experimentLogic)
     const { created_at, start_date, end_date } = experiment
 
-    if (!start_date) {
-        if (!created_at) {
-            return <></>
-        }
+    // If the experiment has no start date and no creation date, don't show anything
+    if (!start_date && !created_at) {
+        return <></>
+    }
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDates.tsx
user: login: rodrigoi
body: we probably never hit this code path, but returning `null` instead of an empty fragment. Updated here: b77916e8382714af1983f1e31095f80395f465c7.
created_at: 2025-03-28T02:56:57Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
+            <div className={clsx('text-xs font-semibold uppercase tracking-wide', isDatePickerOpen && 'text-center')}>
+                {label}
+            </div>
+            <div className="flex">
+                {isDatePickerOpen ? (
+                    <LemonCalendarSelectInput
+                        granularity="minute"
+                        visible
+                        value={dayjs(date)}
+                        onChange={(newDate) => {
+                            if (newDate && onChange) {
+                                onChange(newDate.toISOString())
+                            }
+                        }}
+                        onClose={() => setIsDatePickerOpen(false)}
+                        onClickOutside={() => setIsDatePickerOpen(false)}
+                        clearable={false}
+                        selectionPeriod="past"
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: rodrigoi
body: Keeping all calendar selects set as `past.` We may want to set future dates, and it's an easy change, but let's wait for feedback.k.
created_at: 2025-03-28T03:00:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,69 @@
+import { IconPencil } from '@posthog/icons'
+import { LemonButton, LemonCalendarSelectInput } from '@posthog/lemon-ui'
+import clsx from 'clsx'
+import { TZLabel } from 'lib/components/TZLabel'
+import { dayjs } from 'lib/dayjs'
+import { useState } from 'react'
+
+const ExperimentDate = ({
+    label,
+    date,
+    onChange,
+    selectionLimitDate,
+    'data-attr': dataAttr,
+}: {
+    label: string
+    date?: string | null
+    selectionLimitDate?: string | null
+    onChange?: (date: string) => void
+    'data-attr'?: string
+}): JSX.Element | null => {
+    const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
+
+    if (!date) {
+        return null
+    }
+
+    return (
+        <div className="block" data-attr="experiment-start-date">
path: frontend/src/scenes/experiments/ExperimentView/ExperimentDate.tsx
user: login: rodrigoi
body: missed this one. Updated on 7434303dba53fc19fba680abc62364e4c2319264
created_at: 2025-03-28T03:05:21Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-19T18:02:12Z
body: **Size Change:** +242 B (0%) 

**Total Size:** 9.96 MB



<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size | Change |
| :--- | :---: | :---: |
| `frontend/dist/toolbar.js` | 9.96 MB | +242 B (0%) |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-03-19T18:02:12Z
body: ## 📸 UI snapshots have been updated

**6** snapshot changes in total. **0** added, **6** modified, **0** deleted:

- **`chromium`**: **0** added, **6** modified, **0** deleted ([diff for shard 7](https://github.com/PostHog/posthog/pull/30171/commits/4158fece5ed6bffdeeba0ba77d1e682db8ff08c6))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/30171/commits/850a17a7dbcf1be839c58b50c877c2b20e59dbcc).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/30171/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: posthog-bot
created_at: 2025-03-20T19:15:22Z
body: ## 📸 UI snapshots have been updated

**6** snapshot changes in total. **0** added, **6** modified, **0** deleted:

- **`chromium`**: **0** added, **6** modified, **0** deleted ([diff for shard 7](https://github.com/PostHog/posthog/pull/30171/commits/81b521d9d8fabeb366a9fe9c6d896b1fcb6b25db))
- `webkit`: **0** added, **0** modified, **0** deleted

Triggered by [this commit](https://github.com/PostHog/posthog/pull/30171/commits/252f6607b1d33d29d1d623fc66d234aaf2a99c5c).

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/30171/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR