diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx index 3b3e637c5..c180ad917 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx @@ -1,8 +1,5 @@ import _, { noop } from "lodash"; import dayjs from "dayjs"; -import utc from 'dayjs/plugin/utc'; -import customParseFormat from 'dayjs/plugin/customParseFormat'; -import timezone from 'dayjs/plugin/timezone'; import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core"; import { BoolCodeControl, @@ -55,10 +52,6 @@ import { dropdownControl } from "comps/controls/dropdownControl"; import { timeZoneOptions } from "./timeZone"; -dayjs.extend(utc); -dayjs.extend(timezone); -dayjs.extend(customParseFormat); - const EventOptions = [changeEvent, focusEvent, blurEvent] as const; @@ -91,7 +84,7 @@ const commonChildren = { ...validationChildren, viewRef: RefControl, inputFieldStyle: styleControl(DateTimeStyle, 'inputFieldStyle'), - timeZone: dropdownControl(timeZoneOptions, "Etc/UTC"), + timeZone: dropdownControl(timeZoneOptions, Intl.DateTimeFormat().resolvedOptions().timeZone), }; type CommonChildrenType = RecordConstructorToComp; @@ -149,7 +142,7 @@ function validate( const childrenMap = { value: stringExposingStateControl("value"), - userTimeZone: stringExposingStateControl("userTimeZone", 'Etc/UTC'), + userTimeZone: stringExposingStateControl("userTimeZone", Intl.DateTimeFormat().resolvedOptions().timeZone), ...commonChildren, ...formDataChildren, }; @@ -310,7 +303,7 @@ export const dateRangeControl = (function () { const childrenMap = { start: stringExposingStateControl("start"), end: stringExposingStateControl("end"), - userRangeTimeZone: stringExposingStateControl("userRangeTimeZone" , 'Etc/UTC'), + userRangeTimeZone: stringExposingStateControl("userRangeTimeZone" , Intl.DateTimeFormat().resolvedOptions().timeZone), ...formDataChildren, ...commonChildren, }; @@ -469,61 +462,39 @@ export const dateRangeControl = (function () { .build(); })(); +const getTimeZoneInfo = (timeZone: any, othereTimeZone: any) => { + const tz = timeZone === 'UserChoice' ? othereTimeZone : timeZone ; + return { + TimeZone: tz, + Offset: dayjs().tz(tz).format('Z') // Get the UTC offset for the selected timezone + }; +}; + export const DatePickerComp = withExposingConfigs(datePickerControl, [ depsConfig({ name: "value", desc: trans("export.datePickerValueDesc"), - depKeys: ["value", "showTime", "timeZone", "userTimeZone"], + depKeys: ["value", "showTime"], func: (input) => { - - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.value, format).isValid()) { - mom = dayjs.utc(input.value, format); - break; - } - } - - if (!input.showTime && mom?.hour() === 0 && mom?.minute() === 0 && mom?.second() === 0) { - mom = mom?.hour(12); // Default to noon to avoid day shift - } - - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone || 'UTC'; - const formattedDate = mom.tz(tz).format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT); - return formattedDate; - } - - return null; + const mom = Boolean(input.value) ? dayjs(input.value, DateParser) : null; + return mom?.isValid() ? mom.format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT) : null; }, }), - depsConfig({ name: "formattedValue", desc: trans("export.datePickerFormattedValueDesc"), - depKeys: ["value", "format", "timeZone", "userTimeZone"], - + depKeys: ["value", "format", "timeZone", "userTimeZone"], func: (input) => { - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.value, format).isValid()) { - mom = dayjs.utc(input.value, format); - break; - } - } - if (!input.showTime && mom?.hour() === 0 && mom?.minute() === 0 && mom?.second() === 0) { - mom = mom?.hour(12); // Default to noon to avoid timezone-related day shifts - } - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone || 'UTC'; - const formattedTime = mom.tz(tz).format(input.format); - return formattedTime; - } - return ''; + const mom = Boolean(input.value) ? dayjs(input.value, DateParser) : null; + const tz = input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone; // Get the selected timezone + const timeInTz = mom?.clone().tz(tz, true); // Apply the selected timezone without altering the time itself (do not convert the time) + return mom?.isValid() + ? (!input.format || input.format.includes('Z') || input.format.includes('z')) // Check if format is not available or contains 'Z' + ? timeInTz?.format(input?.format) // Return formattedDateWithoffset if format includes 'Z' or is not available + : mom.format(input.format) // Otherwise, return mom.format(input.format) + : ""; }, }), - - depsConfig({ name: "timestamp", desc: trans("export.datePickerTimestampDesc"), @@ -547,11 +518,8 @@ export const DatePickerComp = withExposingConfigs(datePickerControl, [ name: "timeZone", desc: trans("export.timeZoneDesc"), depKeys: ["timeZone", "userTimeZone"], - func: (input) => { - console.log("input.timeZone", input.timeZone) - return input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone || 'UTC'; + func: (input: { timeZone: any; userTimeZone: any; }) => getTimeZoneInfo(input.timeZone, input.userTimeZone) - }, }), ...CommonNameConfig, ]); @@ -560,235 +528,111 @@ export let DateRangeComp = withExposingConfigs(dateRangeControl, [ depsConfig({ name: "start", desc: trans("export.dateRangeStartDesc"), - depKeys: ["start", "showTime", "timeZone", "userRangeTimeZone"], + depKeys: ["start", "showTime"], func: (input) => { - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.start, format).isValid()) { - mom = dayjs.utc(input.start, format); - break; - } - } - if (!input.showTime && mom?.hour() === 0 && mom?.minute() === 0 && mom?.second() === 0) { - mom = mom?.hour(12); - } - - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - const formattedStart = mom.tz(tz).format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT); - return formattedStart; - } - return null; + const mom = Boolean(input.start) ? dayjs(input.start, DateParser): null; + return mom?.isValid() ? mom.format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT) : null; }, }), - depsConfig({ name: "end", desc: trans("export.dateRangeEndDesc"), - depKeys: ["end", "showTime", "timeZone", "userRangeTimeZone"], - + depKeys: ["end", "showTime"], func: (input) => { - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.end, format).isValid()) { - mom = dayjs.utc(input.end, format); - break; - } - } - if (!input.showTime && mom?.hour() === 0 && mom?.minute() === 0 && mom?.second() === 0) { - mom = mom?.hour(12); - } - - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - const formattedEnd = mom.tz(tz).format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT); - return formattedEnd; - } - return null; + const mom = Boolean(input.end) ? dayjs(input.end, DateParser): null; + return mom?.isValid() ? mom.format(input.showTime ? DATE_TIME_FORMAT : DATE_FORMAT) : null; }, }), - depsConfig({ name: "startTimestamp", desc: trans("export.dateRangeStartTimestampDesc"), - depKeys: ["start", "timeZone", "userRangeTimeZone"], + depKeys: ["start"], func: (input) => { - - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.start, format).isValid()) { - mom = dayjs.utc(input.start, format); - break; - } - } - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return mom.tz(tz).unix(); - } - return ""; + const mom = Boolean(input.start) ? dayjs(input.start, DateParser) : null; + return mom?.isValid() ? mom.unix() : ""; }, }), - depsConfig({ name: "endTimestamp", desc: trans("export.dateRangeEndTimestampDesc"), - depKeys: ["end", "timeZone", "userRangeTimeZone"], + depKeys: ["end"], func: (input) => { - - let mom = null; - for (const format of DateParser) { - if (dayjs.utc(input.end, format).isValid()) { - mom = dayjs.utc(input.end, format); - break; - } - } - if (mom?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return mom.tz(tz).unix(); - } - return ""; + const mom = Boolean(input.end) ? dayjs(input.end, DateParser) : null; + return mom?.isValid() ? mom.unix() : ""; }, }), - depsConfig({ name: "formattedValue", desc: trans("export.dateRangeFormattedValueDesc"), - depKeys: ["start", "end", "format", "timeZone", "userRangeTimeZone"], + depKeys: ["start", "end", "format" , "timeZone", "userRangeTimeZone"], func: (input) => { - let start = null; - let end = null; - - for (const format of DateParser) { - if (dayjs.utc(input.start, format).isValid()) { - start = dayjs.utc(input.start, format); - break; - } - } - for (const format of DateParser) { - if (dayjs.utc(input.end, format).isValid()) { - end = dayjs.utc(input.end, format); - break; - } - } - - //When the time is 00:00:00 and you convert it to a timezone behind UTC (e.g., UTC-5), the date can shift to the previous day - if (!input.showTime && start?.hour() === 0 && start?.minute() === 0 && start?.second() === 0) { - start = start?.hour(12); - } - - if (!input.showTime && end?.hour() === 0 && end?.minute() === 0 && end?.second() === 0) { - end = end?.hour(12); - } - - if (start?.isValid() || end?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - - const formattedStart = start?.isValid() ? start.tz(tz).format(input.format) : ''; - const formattedEnd = end?.isValid() ? end.tz(tz).format(input.format) : ''; - const formattedValue = [formattedStart, formattedEnd].filter(Boolean).join(" - "); - return formattedValue; - } - return ''; + const start = Boolean(input.start) ? dayjs(input.start, DateParser): null; + const end = Boolean(input.end) ? dayjs(input.end, DateParser): null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; // Get the selected timezone + const startTimeInTz = start?.clone().tz(tz, true); // Apply the selected timezone without altering the time itself (do not convert the time) + const endTimeInTz = end?.clone().tz(tz, true); // Apply the selected timezone without altering the time itself (do not convert the time) + + return [ + start?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) // Check if format is not available or contains 'Z' + ? startTimeInTz?.format(input?.format) // Return formattedDateWithoffset if format includes 'Z' or is not available + : start?.format(input.format), + end?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) // Check if format is not available or contains 'Z' + ? endTimeInTz?.format(input?.format) // Return formattedDateWithoffset if format includes 'Z' or is not available + : end?.format(input.format) , + ] + .filter((item) => item) + .join(" - "); }, }), - - depsConfig({ - name: "formattedStartValue", - desc: trans("export.dateRangeFormattedStartValueDesc"), - depKeys: ["start", "format", "timeZone", "userRangeTimeZone"], - func: (input) => { - let start = null; - // Loop through DateParser to find a valid format - for (const format of DateParser) { - if (dayjs.utc(input.start, format).isValid()) { - start = dayjs.utc(input.start, format); - break; - } - } - - if (!input.showTime && start?.hour() === 0 && start?.minute() === 0 && start?.second() === 0) { - start = start?.hour(12); - } - - if (start?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - const formattedStart = start.tz(tz).format(input.format); - return formattedStart; - } - return ''; - }, - }), - + name: "formattedStartValue", + desc: trans("export.dateRangeFormattedStartValueDesc"), + depKeys: ["start", "format", "timeZone", "userRangeTimeZone"], + func: (input) => { + const start = Boolean(input.start) ? dayjs(input.start, DateParser): null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; + const startTimeInTz = start?.clone().tz(tz, true); + return start?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) + ? startTimeInTz?.format(input?.format) + : start?.format(input.format); + }, +}), depsConfig({ name: "formattedEndValue", desc: trans("export.dateRangeFormattedEndValueDesc"), - depKeys: ["end", "format", "timeZone", "userRangeTimeZone"], + depKeys: ["end", "format" , "timeZone", "userRangeTimeZone"], func: (input) => { - let end = null; - // Loop through DateParser to find a valid format - for (const format of DateParser) { - if (dayjs.utc(input.end, format).isValid()) { - end = dayjs.utc(input.end, format); - break; - } - } - - if (!input.showTime && end?.hour() === 0 && end?.minute() === 0 && end?.second() === 0) { - end = end?.hour(12); - } - - if (end?.isValid()) { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - const formattedEnd = end.tz(tz).format(input.format); - return formattedEnd; - } - return ''; + const end = Boolean(input.end) ? dayjs(input.end, DateParser): null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; + const endTimeInTz = end?.clone().tz(tz, true); + return end?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) + ? endTimeInTz?.format(input?.format) + : end?.format(input.format); }, }), - - depsConfig({ - name: "invalid", - desc: trans("export.invalidDesc"), - depKeys: ["start", "end", "required", "minTime", "maxTime", "minDate", "maxDate", "customRule", "timeZone", "userRangeTimeZone"], - func: (input) => { - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - let startDate = null; - let endDate = null; - - for (const format of DateParser) { - if (dayjs.utc(input.start, format).isValid()) { - startDate = dayjs.utc(input.start, format).tz(tz); - break; - } - } - for (const format of DateParser) { - if (dayjs.utc(input.end, format).isValid()) { - endDate = dayjs.utc(input.end, format).tz(tz); - break; - } - } - - const startInvalid = startDate && (!startDate.isValid() || (input.minDate && startDate.isBefore(dayjs(input.minDate).tz(tz))) || (input.maxDate && startDate.isAfter(dayjs(input.maxDate).tz(tz)))); - const endInvalid = endDate && (!endDate.isValid() || (input.minDate && endDate.isBefore(dayjs(input.minDate).tz(tz))) || (input.maxDate && endDate.isAfter(dayjs(input.maxDate).tz(tz)))); - - return startInvalid || endInvalid; - }, + name: "timeZone", + desc: trans("export.timeZoneDesc"), + depKeys: ["timeZone", "userRangeTimeZone"], + func: (input:any) => getTimeZoneInfo(input.timeZone, input.userRangeTimeZone) }), - depsConfig({ - name: "timeZone", - desc: trans("export.timeZoneDesc"), - depKeys: ["timeZone", "userRangeTimeZone"], - func: (input) => { - return input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - }, + name: "invalid", + desc: trans("export.invalidDesc"), + depKeys: ["start", "end", "required", "minTime", "maxTime", "minDate", "maxDate", "customRule"], + func: (input) => + validate({ + ...input, + value: { value: input.start }, + }).validateStatus !== "success" || + validate({ + ...input, + value: { value: input.end }, + }).validateStatus !== "success", }), ...CommonNameConfig, ]); - DateRangeComp = withMethodExposing(DateRangeComp, [ ...dateRefMethods, { @@ -830,4 +674,4 @@ DateRangeComp = withMethodExposing(DateRangeComp, [ comp.children.end.getView().onChange(data.end); }, }, -]); +]); \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/dateRangeUIView.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/dateRangeUIView.tsx index 1c925fa95..678bbced5 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/dateRangeUIView.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/dateRangeUIView.tsx @@ -82,7 +82,7 @@ export const DateRangeUIView = (props: DateRangeUIViewProps) => { option.value !== 'UserChoice')} placeholder="Select Time Zone" - defaultValue={'Etc/UTC'} + defaultValue={Intl.DateTimeFormat().resolvedOptions().timeZone} onChange={props?.onClickDateRangeTimeZone} /> diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/dateUIView.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/dateUIView.tsx index 80ab630f6..2d55bfd1c 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/dateUIView.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/dateUIView.tsx @@ -70,7 +70,7 @@ export const DateUIView = (props: DataUIViewProps) => { option.value !== 'UserChoice')} placeholder="Select Time Zone" - defaultValue={'Etc/UTC'} + defaultValue={Intl.DateTimeFormat().resolvedOptions().timeZone} onChange={props.onClickDateTimeZone} /> diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx index 5b8809e9a..9e74e3d3f 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx @@ -1,8 +1,5 @@ import _ from "lodash"; import dayjs from "dayjs"; -import utc from 'dayjs/plugin/utc'; -import timezone from 'dayjs/plugin/timezone'; -import customParseFormat from 'dayjs/plugin/customParseFormat'; import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core"; import { BoolCodeControl, @@ -58,9 +55,6 @@ import { EditorContext } from "comps/editorState"; import { dropdownControl } from "comps/controls/dropdownControl"; import { timeZoneOptions } from "./timeZone"; -dayjs.extend(utc); -dayjs.extend(timezone); -dayjs.extend(customParseFormat); const EventOptions = [changeEvent, focusEvent, blurEvent] as const; @@ -90,7 +84,7 @@ const commonChildren = { ), inputFieldStyle: styleControl(DateTimeStyle, 'inputFieldStyle'), suffixIcon: withDefault(IconControl, "/icon:regular/clock"), - timeZone: dropdownControl(timeZoneOptions, "Etc/UTC"), + timeZone: dropdownControl(timeZoneOptions, Intl.DateTimeFormat().resolvedOptions().timeZone), viewRef: RefControl, ...validationChildren, }; @@ -130,7 +124,7 @@ function validate( const childrenMap = { value: stringExposingStateControl("value"), - userTimeZone: stringExposingStateControl("userTimeZone" , 'Etc/UTC'), + userTimeZone: stringExposingStateControl("userTimeZone", Intl.DateTimeFormat().resolvedOptions().timeZone), ...commonChildren, ...formDataChildren, }; @@ -275,7 +269,7 @@ export const timeRangeControl = (function () { const childrenMap = { start: stringExposingStateControl("start"), end: stringExposingStateControl("end"), - userRangeTimeZone: stringExposingStateControl("userRangeTimeZone" , 'Etc/UTC'), + userRangeTimeZone: stringExposingStateControl("userRangeTimeZone" , Intl.DateTimeFormat().resolvedOptions().timeZone), ...formDataChildren, ...commonChildren, }; @@ -420,26 +414,30 @@ export const timeRangeControl = (function () { .build(); })(); +const getTimeZoneInfo = (timeZone: any, othereTimeZone: any) => { + const tz = timeZone === 'UserChoice' ? othereTimeZone : timeZone ; + return { + TimeZone: tz, + Offset: dayjs().tz(tz).format('Z') // Get the UTC offset for the selected timezone + }; +}; + export const TimePickerComp = withExposingConfigs(timePickerControl, [ new NameConfig("value", trans("export.timePickerValueDesc")), depsConfig({ name: "formattedValue", desc: trans("export.timePickerFormattedValueDesc"), - depKeys: ["value", "format", "timeZone", "userTimeZone"], + depKeys: ["value", "format", "timeZone", "userTimeZone"], func: (input) => { - let mom = null; - - // Loop through TimeParser to find a valid format - for (const format of TimeParser) { - if (dayjs.utc(input.value, format).isValid()) { - mom = dayjs.utc(input.value, format); - break; - } - } - - const tz = input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone || 'UTC'; - return mom?.isValid() ? mom.tz(tz).format(input.format) : ''; + const mom = Boolean(input.value) ? dayjs(input.value, TimeParser) : null; + const tz = input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone; // Get the selected timezone + const timeInTz = mom?.clone().tz(tz, true); // Apply the selected timezone without altering the time itself (do not convert the time) + const formattedTimeWithoffset = timeInTz?.format(input?.format); + return mom?.isValid() ? (!input.format || input.format.includes('Z') || input.format.includes('z')) // Check if format is not available or contains 'Z' + ? formattedTimeWithoffset // Return formattedDateWithoffset if format includes 'Z' or is not available + : mom.format(input.format) // Otherwise, return mom.format(input.format) + : ""; }, }), @@ -447,11 +445,8 @@ export const TimePickerComp = withExposingConfigs(timePickerControl, [ name: "timeZone", desc: trans("export.timeZoneDesc"), depKeys: ["timeZone", "userTimeZone"], - func: (input) => { - return input.timeZone === 'UserChoice' ? input.userTimeZone : input.timeZone || 'UTC'; - }, + func: (input: { timeZone: any; userTimeZone: any; }) => getTimeZoneInfo(input.timeZone, input.userTimeZone) }), - depsConfig({ name: "invalid", desc: trans("export.invalidDesc"), @@ -460,158 +455,87 @@ export const TimePickerComp = withExposingConfigs(timePickerControl, [ validate({ ...input, value: { value: input.value }, - }).validateStatus !== "success", + } as any).validateStatus !== "success", }), - ...CommonNameConfig, ]); - export let TimeRangeComp = withExposingConfigs(timeRangeControl, [ - // new NameConfig("start", trans("export.timeRangeStartDesc")), - // new NameConfig("end", trans("export.timeRangeEndDesc")), - depsConfig({ - name: "start", - desc: trans("export.timeRangeStartDesc"), - depKeys: ["start", "timeZone", "userRangeTimeZone"], - func: (input) => { - let start = null; - - // Loop through TimeParser to find a valid format for start - for (const format of TimeParser) { - if (dayjs.utc(input.start, format).isValid()) { - start = dayjs.utc(input.start, format); - break; - } - } - - if (start?.hour() === 0 && start?.minute() === 0 && start?.second() === 0) { - start = start?.hour(12); - } - - // Apply timezone conversion if valid - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return start?.isValid() ? start.tz(tz).format(input.format || "HH:mm:ss") : null; - }, - }), - - depsConfig({ - name: "end", - desc: trans("export.timeRangeEndDesc"), - depKeys: ["end", "timeZone", "userRangeTimeZone"], - func: (input) => { - let end = null; - - // Loop through TimeParser to find a valid format for end - for (const format of TimeParser) { - if (dayjs.utc(input.end, format).isValid()) { - end = dayjs.utc(input.end, format); - break; - } - } - - // Apply timezone conversion if valid - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return end?.isValid() ? end.tz(tz).format(input.format || "HH:mm:ss") : null; - }, - }), - + new NameConfig("start", trans("export.timeRangeStartDesc")), + new NameConfig("end", trans("export.timeRangeEndDesc")), depsConfig({ name: "formattedValue", desc: trans("export.timeRangeFormattedValueDesc"), depKeys: ["start", "end", "format", "timeZone", "userRangeTimeZone"], func: (input) => { - let start = null; - let end = null; - for (const format of TimeParser) { - if (dayjs.utc(input.start, format).isValid()) { - start = dayjs.utc(input.start, format); - break; - } - } - for (const format of TimeParser) { - if (dayjs.utc(input.end, format).isValid()) { - end = dayjs.utc(input.end, format); - break; - } - } - - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - const formattedStart = start?.isValid() ? start.tz(tz).format(input.format) : ''; - const formattedEnd = end?.isValid() ? end.tz(tz).format(input.format) : ''; - - return [formattedStart, formattedEnd].filter(Boolean).join(" - "); + const start = Boolean(input.start) ? dayjs(input.start, TimeParser) : null; + const end = Boolean(input.end) ? dayjs(input.end, TimeParser) : null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; // Get the selected timezone + const startTimeInTz = start?.clone().tz(tz, true); // Apply the selected timezone without altering the time itself (do not convert the time) + const endTimeInTz = end?.clone().tz(tz, true); + return [ + start?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) // Check if format is not available or contains 'Z' + ? startTimeInTz?.format(input?.format) // Return formattedTimeWithoffset if format includes 'Z' or is not available + : start?.format(input.format), + end?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) + ? endTimeInTz?.format(input?.format) + : end?.format(input.format) , + ] + .filter((item) => item) + .join(" - "); }, }), - depsConfig({ name: "formattedStartValue", desc: trans("export.timeRangeFormattedStartValueDesc"), - depKeys: ["start", "format", "timeZone", "userRangeTimeZone"], + depKeys: ["start", "format" , "timeZone", "userRangeTimeZone"], func: (input) => { - let start = null; - for (const format of TimeParser) { - if (dayjs.utc(input.start, format).isValid()) { - start = dayjs.utc(input.start, format); - break; - } - } - - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return start?.isValid() ? start.tz(tz).format(input.format) : ''; + const start = Boolean(input.start) ? dayjs(input.start, TimeParser) : null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; + const startTimeInTz = start?.clone().tz(tz, true); + const formattedDate = startTimeInTz?.format(input?.format); + return start?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) + ? formattedDate + : start?.format(input.format); }, }), - depsConfig({ name: "formattedEndValue", desc: trans("export.timeRangeFormattedEndValueDesc"), depKeys: ["end", "format", "timeZone", "userRangeTimeZone"], func: (input) => { - let end = null; - for (const format of TimeParser) { - if (dayjs.utc(input.end, format).isValid()) { - end = dayjs.utc(input.end, format); - break; - } - } - - const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - return end?.isValid() ? end.tz(tz).format(input.format) : ''; + const end = Boolean(input.end) ? dayjs(input.end, TimeParser) : null; + const tz = input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone; + const endTimeInTz = end?.clone().tz(tz, true); + return end?.isValid() && (!input.format || input.format.includes('Z') || input.format.includes('z')) + ? endTimeInTz?.format(input?.format) + : end?.format(input.format); }, }), - depsConfig({ name: "timeZone", desc: trans("export.timeZoneDesc"), depKeys: ["timeZone", "userRangeTimeZone"], - func: (input) => { - return input.timeZone === 'UserChoice' ? input.userRangeTimeZone : input.timeZone || 'UTC'; - }, - }), + func: (input:any) => getTimeZoneInfo(input.timeZone, input.userRangeTimeZone) + }), depsConfig({ name: "invalid", desc: trans("export.invalidDesc"), depKeys: ["start", "end", "required", "minTime", "maxTime", "customRule"], - func: (input) => { - const startInvalid = validate({ + func: (input) => + validate({ ...input, value: { value: input.start }, - }).validateStatus !== "success"; - - const endInvalid = validate({ + }).validateStatus !== "success" || + validate({ ...input, value: { value: input.end }, - }).validateStatus !== "success"; - - return startInvalid || endInvalid; - }, + }).validateStatus !== "success", }), - ...CommonNameConfig, ]); - TimeRangeComp = withMethodExposing(TimeRangeComp, [ ...dateRefMethods, { @@ -653,4 +577,4 @@ TimeRangeComp = withMethodExposing(TimeRangeComp, [ comp.children.end.getView().onChange(data.end); }, }, -]); +]); \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeRangeUIView.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/timeRangeUIView.tsx index 4d837b94a..fd8ce4d69 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/timeRangeUIView.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeRangeUIView.tsx @@ -21,7 +21,7 @@ const RangePickerStyled = styled((props: any) => )<{ $ `; const StyledAntdSelect = styled(AntdSelect)` - width: 100%; + width: 300px; margin: 10px 0px; .ant-select-selector { font-size: 14px; @@ -71,7 +71,7 @@ export const TimeRangeUIView = (props: TimeRangeUIViewProps) => { option.value !== 'UserChoice')} // Filter out 'userChoice' - defaultValue={'Etc/UTC'} + defaultValue={Intl.DateTimeFormat().resolvedOptions().timeZone} onChange={props.handleTimeRangeZoneChange} /> ) diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeUIView.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/timeUIView.tsx index 942061e68..9e4d414c7 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/timeUIView.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeUIView.tsx @@ -21,7 +21,7 @@ const TimeMobileUIView = React.lazy(() => ); const StyledAntdSelect = styled(AntdSelect)` - width: 100%; + width: 300px; margin: 10px 0; .ant-select-selector { font-size: 14px; @@ -54,7 +54,7 @@ export const TimeUIView = (props: TimeUIViewProps) => { placeholder="Select Time Zone" options={timeZoneOptions.filter(option => option.value !== 'UserChoice')} // Filter out 'userChoice' onChange={props?.handleTimeZoneChange} - defaultValue={'Etc/UTC'} + defaultValue={Intl.DateTimeFormat().resolvedOptions().timeZone} /> ) )} diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeZone.ts b/client/packages/lowcoder/src/comps/comps/dateComp/timeZone.ts index 33108bcad..72e0206a6 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/timeZone.ts +++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeZone.ts @@ -1,8 +1,9 @@ import { trans } from "i18n"; export const timeZoneOptions = [ - { label: trans("timeZone.UTC-12:00"), value: "Etc/GMT+12" }, - { label: trans("timeZone.UTC-11:00"), value: "Etc/GMT+11" }, + { label: trans("timeZone.UserChoice"), value: "UserChoice" }, + { label: trans("timeZone.UTC-12:00"), value: "Pacific/Baker" }, + { label: trans("timeZone.UTC-11:00"), value: "Pacific/Niue" }, { label: trans("timeZone.UTC-10:00"), value: "Pacific/Honolulu" }, { label: trans("timeZone.UTC-09:00"), value: "America/Anchorage" }, { label: trans("timeZone.UTC-08:00"), value: "America/Tijuana" }, @@ -11,7 +12,7 @@ export const timeZoneOptions = [ { label: trans("timeZone.UTC-05:00"), value: "America/New_York" }, { label: trans("timeZone.UTC-04:00"), value: "America/Halifax" }, { label: trans("timeZone.UTC-03:00"), value: "America/Argentina/Buenos_Aires" }, - { label: trans("timeZone.UTC-02:00"), value: "Etc/GMT+2" }, + { label: trans("timeZone.UTC-02:00"), value: "Atlantic/South_Georgia" }, { label: trans("timeZone.UTC-01:00"), value: "Atlantic/Cape_Verde" }, { label: trans("timeZone.UTC+00:00"), value: "Etc/UTC" }, { label: trans("timeZone.UTC+01:00"), value: "Europe/Berlin" }, @@ -20,10 +21,15 @@ export const timeZoneOptions = [ { label: trans("timeZone.UTC+04:00"), value: "Asia/Dubai" }, { label: trans("timeZone.UTC+05:00"), value: "Asia/Karachi" }, { label: trans("timeZone.UTC+05:30"), value: "Asia/Kolkata" }, + { label: trans("timeZone.UTC+05:45"), value: "Asia/Kathmandu" }, { label: trans("timeZone.UTC+06:00"), value: "Asia/Dhaka" }, + { label: trans("timeZone.UTC+06:30"), value: "Asia/Rangoon" }, { label: trans("timeZone.UTC+07:00"), value: "Asia/Bangkok" }, { label: trans("timeZone.UTC+08:00"), value: "Asia/Shanghai" }, { label: trans("timeZone.UTC+09:00"), value: "Asia/Tokyo" }, + { label: trans("timeZone.UTC+09:30"), value: "Australia/Darwin" }, { label: trans("timeZone.UTC+10:00"), value: "Australia/Sydney" }, - { label: trans("timeZone.UserChoice"), value: "UserChoice" }, + { label: trans("timeZone.UTC+11:00"), value: "Pacific/Guadalcanal" }, + { label: trans("timeZone.UTC+12:00"), value: "Pacific/Auckland" }, + { label: trans("timeZone.UTC+13:00"), value: "Pacific/Tongatapu" }, ]; diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 71c260e19..d5777d42c 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -3636,14 +3636,19 @@ export const en = { "UTC+04:00": "(UTC+04:00) Dubai, Muscat", "UTC+05:00": "(UTC+05:00) Karachi", "UTC+05:30": "(UTC+05:30) New Delhi", + "UTC+05:45": "(UTC+05:45) Kathmandu", "UTC+06:00": "(UTC+06:00) Dhaka", + "UTC+06:30": "(UTC+06:30) Yangon", "UTC+07:00": "(UTC+07:00) Bangkok", "UTC+08:00": "(UTC+08:00) Beijing, HK", "UTC+09:00": "(UTC+09:00) Tokyo, Seoul", + "UTC+09:30": "(UTC+09:30) Adelaide, Darwin", "UTC+10:00": "(UTC+10:00) Sydney", - "UserChoice": "User Choice" + "UTC+11:00": "(UTC+11:00) Solomon Islands, New Caledonia", + "UTC+12:00": "(UTC+12:00) Auckland, Fiji", + "UTC+13:00": "(UTC+13:00) Nuku'alofa, Samoa", + "UserChoice": "UserChoice" }, - tour: { section1Title: "Steps", section1Subtitle: "Steps",