Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-782] fix: Date timezone changes error #3992

Merged
merged 6 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AlertLabel } from "src/ui/components/alert-label";
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu";
import { SummaryPopover } from "src/ui/components/summary-popover";
import { InfoPopover } from "src/ui/components/info-popover";
import { getDate } from "src/utils/date-utils";

interface IEditorHeader {
editor: Editor;
Expand Down Expand Up @@ -72,7 +73,7 @@ export const EditorHeader = (props: IEditorHeader) => {
Icon={Archive}
backgroundColor="bg-blue-500/20"
textColor="text-blue-500"
label={`Archived at ${archivedAt.toLocaleString()}`}
label={`Archived at ${getDate(archivedAt)?.toLocaleString()}`}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { usePopper } from "react-popper";
import { Calendar, History, Info } from "lucide-react";
// types
import { DocumentDetails } from "src/types/editor-types";
//utils
import { getDate } from "src/utils/date-utils";

type Props = {
documentDetails: DocumentDetails;
};

// function to render a Date in the format- 25 May 2023 at 2:53PM
const renderDate = (date: Date): string => {
const renderDate = (date: Date | undefined): string => {
const options: Intl.DateTimeFormatOptions = {
day: "numeric",
month: "long",
Expand Down Expand Up @@ -52,14 +54,14 @@ export const InfoPopover: React.FC<Props> = (props) => {
<h6 className="text-xs text-custom-text-400">Last updated on</h6>
<h5 className="flex items-center gap-1 text-sm">
<History className="h-3 w-3" />
{renderDate(documentDetails.last_updated_at)}
{renderDate(getDate(documentDetails?.last_updated_at))}
</h5>
</div>
<div className="space-y-1.5">
<h6 className="text-xs text-custom-text-400">Created on</h6>
<h5 className="flex items-center gap-1 text-sm">
<Calendar className="h-3 w-3" />
{renderDate(documentDetails.created_on)}
{renderDate(getDate(documentDetails?.created_on))}
</h5>
</div>
</div>
Expand Down
26 changes: 26 additions & 0 deletions packages/editor/document-editor/src/utils/date-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
function isNumber(value: any) {
return typeof value === "number";
}

/**
* This method returns a date from string of type yyyy-mm-dd
* This method is recommended to use instead of new Date() as this does not introduce any timezone offsets
* @param date
* @returns date or undefined
*/
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
try {
if (!date || date === "") return;

if (typeof date !== "string" && !(date instanceof String)) return date;
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
const year = parseInt(yearString);
const month = parseInt(monthString);
const day = parseInt(dayString);
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;

return new Date(year, month - 1, day);
} catch (e) {
return undefined;
}
};
4 changes: 2 additions & 2 deletions packages/types/src/pages.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IPage {
archived_at: string | null;
blocks: IPageBlock[];
color: string;
created_at: Date;
created_at: string | null;
created_by: string;
description: string;
description_html: string;
Expand All @@ -25,7 +25,7 @@ export interface IPage {
owned_by: string;
project: string;
project_detail: IProjectLite;
updated_at: Date;
updated_at: string | null;
updated_by: string;
workspace: string;
workspace_detail: IWorkspaceLite;
Expand Down
2 changes: 1 addition & 1 deletion web/components/api-token/modal/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
// if never expires is toggled on, set expired_at to null
if (neverExpires) payload.expired_at = null;
// if never expires is toggled off, and the user has selected a custom date, set expired_at to the custom date
else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate ?? new Date());
else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate);
// if never expires is toggled off, and the user has selected a predefined date, set expired_at to the predefined date
else {
const expiryDate = getExpiryDate(data.expired_at ?? "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export const CreatedUpcomingIssueListItem: React.FC<IssueListItemProps> = observ
if (!issue) return null;

const projectDetails = getProjectById(issue.project_id);
const targetDate = getDate(issue.target_date);

return (
<ControlLink
Expand All @@ -170,11 +171,7 @@ export const CreatedUpcomingIssueListItem: React.FC<IssueListItemProps> = observ
<h6 className="text-sm flex-grow truncate">{issue.name}</h6>
</div>
<div className="text-xs text-center">
{issue.target_date
? isToday(new Date(issue.target_date))
? "Today"
: renderFormattedDate(issue.target_date)
: "-"}
{targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"}
</div>
<div className="text-xs flex justify-center">
{issue.assignee_ids && issue.assignee_ids?.length > 0 ? (
Expand Down Expand Up @@ -210,7 +207,7 @@ export const CreatedOverdueIssueListItem: React.FC<IssueListItemProps> = observe

const projectDetails = getProjectById(issue.project_id);

const dueBy = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0;
const dueBy: number = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0;

return (
<ControlLink
Expand Down
10 changes: 5 additions & 5 deletions web/components/gantt-chart/blocks/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { observer } from "mobx-react";
// components
// helpers
import { cn } from "@/helpers/common.helper";
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { useIssueDetail } from "@/hooks/store";
// types
// constants
Expand Down Expand Up @@ -45,12 +45,12 @@ export const GanttChartBlock: React.FC<Props> = observer((props) => {
totalBlockShifts: number,
dragDirection: "left" | "right" | "move"
) => {
if (!block.start_date || !block.target_date) return;
const originalStartDate = getDate(block.start_date);
const originalTargetDate = getDate(block.target_date);

const originalStartDate = new Date(block.start_date);
const updatedStartDate = new Date(originalStartDate);
if (!originalStartDate || !originalTargetDate) return;

const originalTargetDate = new Date(block.target_date);
const updatedStartDate = new Date(originalStartDate);
const updatedTargetDate = new Date(originalTargetDate);

// update the start date on left resize
Expand Down
16 changes: 9 additions & 7 deletions web/components/notifications/notification-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { ArchiveRestore, Clock, MessageSquare, MoreVertical, User2 } from "lucide-react";
import { Menu } from "@headlessui/react";
// icons
// type
import type { IUserNotification, NotificationType } from "@plane/types";
// ui
import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
import { ArchiveIcon, CustomMenu, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
// constants
import {
ISSUE_OPENED,
Expand All @@ -16,13 +16,13 @@ import {
NOTIFICATION_SNOOZED,
} from "@/constants/event-tracker";
import { snoozeOptions } from "@/constants/notification";
// helper
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "@/helpers/date-time.helper";
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "@/helpers/string.helper";
// hooks
import { useEventTracker } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
// type
// helper
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate, getDate } from "helpers/date-time.helper";
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper";


type NotificationCardProps = {
selectedTab: NotificationType;
Expand Down Expand Up @@ -122,7 +122,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
const notificationField = notification.data.issue_activity.field;
const notificationTriggeredBy = notification.triggered_by_details;

if (isSnoozedTabOpen && notification.snoozed_till! < new Date()) return null;
const snoozedTillDate = getDate(notification?.snoozed_till);

if (snoozedTillDate && isSnoozedTabOpen && snoozedTillDate < new Date()) return null;

return (
<Link
Expand Down
10 changes: 6 additions & 4 deletions web/components/notifications/select-snooze-till-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { DateDropdown } from "@/components/dropdowns";
import { allTimeIn30MinutesInterval12HoursFormat } from "@/constants/notification";
// ui
// types
// helpers
import { getDate } from "helpers/date-time.helper";

type SnoozeModalProps = {
isOpen: boolean;
Expand Down Expand Up @@ -56,7 +58,7 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {

if (!formDataDate) return timeStamps;

const isToday = today.toDateString() === formDataDate.toDateString();
const isToday = today.toDateString() === getDate(formDataDate)?.toDateString();

if (!isToday) return timeStamps;

Expand Down Expand Up @@ -89,9 +91,9 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
);
const minutes = parseInt(time[1]);

const dateTime = formData.date;
dateTime.setHours(hours);
dateTime.setMinutes(minutes);
const dateTime: Date | undefined = getDate(formData?.date);
dateTime?.setHours(hours);
dateTime?.setMinutes(minutes);

await handleSubmitSnooze(notification.id, dateTime).then(() => {
handleClose();
Expand Down
17 changes: 9 additions & 8 deletions web/components/pages/pages-list/list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,15 @@ export const PagesListItem: FC<IPagesListItem> = observer(({ pageId, projectId }
<p className="text-sm text-custom-text-200">{renderFormattedTime(archived_at)}</p>
</Tooltip>
) : (
<Tooltip
isMobile={isMobile}
tooltipContent={`Last updated at ${renderFormattedTime(updated_at)} on ${renderFormattedDate(
updated_at
)}`}
>
<p className="text-sm text-custom-text-200">{renderFormattedTime(updated_at)}</p>
</Tooltip>
updated_at && (
<Tooltip
tooltipContent={`Last updated at ${renderFormattedTime(updated_at)} on ${renderFormattedDate(
updated_at
)}`}
>
<p className="text-sm text-custom-text-200">{renderFormattedTime(updated_at)}</p>
</Tooltip>
)
)}
{isEditingAllowed && (
<Tooltip
Expand Down
9 changes: 5 additions & 4 deletions web/helpers/cycle.helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sortBy from "lodash/sortBy";
// helpers
import { satisfiesDateFilter } from "@/helpers/filter.helper";
import { getDate } from "@/helpers/date-time.helper";
// types
import { ICycle, TCycleFilters } from "@plane/types";

Expand Down Expand Up @@ -42,15 +43,15 @@ export const shouldFilterCycle = (cycle: ICycle, filter: TCycleFilters): boolean
if (filterKey === "status" && filter.status && filter.status.length > 0)
fallsInFilters = fallsInFilters && filter.status.includes(cycle.status.toLowerCase());
if (filterKey === "start_date" && filter.start_date && filter.start_date.length > 0) {
const startDate = getDate(cycle.start_date);
filter.start_date.forEach((dateFilter) => {
fallsInFilters =
fallsInFilters && !!cycle.start_date && satisfiesDateFilter(new Date(cycle.start_date), dateFilter);
fallsInFilters = fallsInFilters && !!startDate && satisfiesDateFilter(startDate, dateFilter);
});
}
if (filterKey === "end_date" && filter.end_date && filter.end_date.length > 0) {
const endDate = getDate(cycle.end_date);
filter.end_date.forEach((dateFilter) => {
fallsInFilters =
fallsInFilters && !!cycle.end_date && satisfiesDateFilter(new Date(cycle.end_date), dateFilter);
fallsInFilters = fallsInFilters && !!endDate && satisfiesDateFilter(endDate, dateFilter);
});
}
});
Expand Down
27 changes: 16 additions & 11 deletions web/helpers/date-time.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import isNumber from "lodash/isNumber";
* @param {Date | string} date
* @example renderFormattedDate("2024-01-01") // Jan 01, 2024
*/
export const renderFormattedDate = (date: string | Date | undefined): string | null => {
export const renderFormattedDate = (date: string | Date | undefined | null): string | null => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
Expand Down Expand Up @@ -44,7 +44,7 @@ export const renderFormattedDateWithoutYear = (date: string | Date): string => {
* @param {Date | string} date
* @example renderFormattedPayloadDate("Jan 01, 20224") // "2024-01-01"
*/
export const renderFormattedPayloadDate = (date: Date | string): string | null => {
export const renderFormattedPayloadDate = (date: Date | string | undefined | null): string | null => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
Expand All @@ -67,7 +67,7 @@ export const renderFormattedPayloadDate = (date: Date | string): string | null =
*/
export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" | "24-hour" = "24-hour"): string => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
const parsedDate = new Date(date);
// return if undefined
if (!parsedDate) return "";
// Check if the parsed date is valid
Expand Down Expand Up @@ -208,14 +208,19 @@ export const checkIfDatesAreEqual = (
* @returns date or undefined
*/
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
if (!date || date === "") return;
try {
if (!date || date === "") return;

if (typeof date !== "string" && !(date instanceof String)) return date;

if (typeof date !== "string" && !(date instanceof String)) return date;
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
const year = parseInt(yearString);
const month = parseInt(monthString);
const day = parseInt(dayString);
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
const year = parseInt(yearString);
const month = parseInt(monthString);
const day = parseInt(dayString);
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;

return new Date(year, month - 1, day);
return new Date(year, month - 1, day);
} catch (e) {
return undefined;
}
};
9 changes: 5 additions & 4 deletions web/helpers/module.helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sortBy from "lodash/sortBy";
// helpers
import { satisfiesDateFilter } from "@/helpers/filter.helper";
import { getDate } from "@/helpers/date-time.helper";
// types
import { IModule, TModuleDisplayFilters, TModuleFilters, TModuleOrderByOptions } from "@plane/types";

Expand Down Expand Up @@ -62,15 +63,15 @@ export const shouldFilterModule = (
fallsInFilters = fallsInFilters && filters.members.some((memberId) => memberIds.includes(memberId));
}
if (filterKey === "start_date" && filters.start_date && filters.start_date.length > 0) {
const startDate = getDate(module.start_date);
filters.start_date.forEach((dateFilter) => {
fallsInFilters =
fallsInFilters && !!module.start_date && satisfiesDateFilter(new Date(module.start_date), dateFilter);
fallsInFilters = fallsInFilters && !!startDate && satisfiesDateFilter(startDate, dateFilter);
});
}
if (filterKey === "target_date" && filters.target_date && filters.target_date.length > 0) {
const endDate = getDate(module.target_date);
filters.target_date.forEach((dateFilter) => {
fallsInFilters =
fallsInFilters && !!module.target_date && satisfiesDateFilter(new Date(module.target_date), dateFilter);
fallsInFilters = fallsInFilters && !!endDate && satisfiesDateFilter(endDate, dateFilter);
});
}
});
Expand Down
4 changes: 3 additions & 1 deletion web/helpers/project.helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sortBy from "lodash/sortBy";
// helpers
import { satisfiesDateFilter } from "@/helpers/filter.helper";
import { getDate } from "@/helpers/date-time.helper";
// types
import { IProject, TProjectDisplayFilters, TProjectFilters, TProjectOrderByOptions } from "@plane/types";
// constants
Expand Down Expand Up @@ -85,8 +86,9 @@ export const shouldFilterProject = (
fallsInFilters = fallsInFilters && filters.members.some((memberId) => memberIds.includes(memberId));
}
if (filterKey === "created_at" && filters.created_at && filters.created_at.length > 0) {
const createdDate = getDate(project.created_at);
filters.created_at.forEach((dateFilter) => {
fallsInFilters = fallsInFilters && satisfiesDateFilter(new Date(project.created_at), dateFilter);
fallsInFilters = fallsInFilters && !!createdDate && satisfiesDateFilter(createdDate, dateFilter);
});
}
});
Expand Down
Loading
Loading