Skip to content

Commit

Permalink
Merge pull request #60 from skbkontur/replace-moment-with-date-fns
Browse files Browse the repository at this point in the history
Replace moment with date fns
  • Loading branch information
scklepova authored Sep 7, 2022
2 parents 0aa1bde + 1f51908 commit eddda7f
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 40 deletions.
14 changes: 10 additions & 4 deletions DbViewer.Tests/FrontTests/BusinessObjectsChangeValuesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,29 @@ public void TestChangeDocumentDates()
var filledDateRow = businessObjectEditingPage.RootAccordion.FindField("DocumentDate");
filledDateRow.FieldValue.WaitTextContains("2014-12-11");
filledDateRow.Edit.Click();

var expectedOffset = documentName == "CqlDocument" ? TimeSpan.Zero : DateTimeOffset.Now.Offset;
var expectedOffsetStr = $"+{expectedOffset:hh':'mm}";
filledDateRow.FieldValue.Date.ClearAndInputText("13.12.2014");
filledDateRow.FieldValue.Time.ClearAndInputText("10:18");
filledDateRow.FieldValue.Time.ClearAndInputText("10:18:13.567");
filledDateRow.FieldValue.TimeOffsetLabel.WaitText(expectedOffsetStr);
filledDateRow.Save.Click();
filledDateRow.FieldValue.WaitTextContains("2014-12-13");
var expectedStr = $"2014-12-13T10:18:13.567{expectedOffsetStr}";
filledDateRow.FieldValue.WaitTextContains(expectedStr);

browser.WebDriver.Navigate().Refresh();
filledDateRow = businessObjectEditingPage.RootAccordion.FindField("DocumentDate");
filledDateRow.FieldValue.WaitTextContains("2014-12-13");
filledDateRow.FieldValue.WaitTextContains(expectedStr);

GetDocument(documentName, documentId).DocumentDate.Should().Be(new DateTimeOffset(2014, 12, 13, 10, 18, 00, 00, TimeSpan.Zero));
GetDocument(documentName, documentId).DocumentDate.Should().Be(new DateTimeOffset(2014, 12, 13, 10, 18, 13, 567, expectedOffset));

businessObjectEditingPage.RootAccordion.FindAccordionToggle("DocumentContent").ToggleButton.Click();
var unfilledDateRow = businessObjectEditingPage.RootAccordion.FindField("DocumentContent_DeliveryDate");
unfilledDateRow.FieldValue.WaitText("null");
unfilledDateRow.Edit.Click();
unfilledDateRow.FieldValue.Date.ClearAndInputText("14.12.2014");
unfilledDateRow.FieldValue.Time.ClearAndInputText("20:19");
unfilledDateRow.FieldValue.TimeZoneSelect.SelectValueByText("UTC");
unfilledDateRow.Save.Click();
unfilledDateRow.FieldValue.WaitText("2014-12-14T20:19:00Z");

Expand Down
3 changes: 2 additions & 1 deletion DbViewer.Tests/FrontTests/Pages/AccordionFieldValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public AccordionFieldValue(ISearchContainer container, ISelector selector)

public DatePicker Date { get; set; }
public Input Time { get; set; }
public Label TimeZone { get; set; }
public Select TimeZoneSelect { get; set; }
public Label TimeOffsetLabel { get; set; }
}
}
4 changes: 2 additions & 2 deletions DbViewer/Helpers/ObjectParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ public static class ObjectParser
var format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
if (long.TryParse(value, out var ticks))
return new DateTimeOffset(ticks, TimeSpan.Zero);
return DateTimeOffset.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
return DateTimeOffset.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}

if (type == typeof(DateTime))
{
var format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
if (long.TryParse(value, out var ticks))
return new DateTime(ticks, DateTimeKind.Utc);
return DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
return DateTime.ParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
}

throw new InvalidOperationException($"Unsupported property type: {type.FullName}");
Expand Down
6 changes: 3 additions & 3 deletions db-viewer-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
"@skbkontur/react-stack-layout": "^1.0.3",
"@skbkontur/react-ui-validations": "^1.8.3",
"copy-to-clipboard": "^3.3.1",
"date-fns": "^2.29.1",
"decimal.js": "^10.2.1",
"lodash": "^4.17.20",
"moment": "^2.29.1",
"qs": "^6.9.6",
"whatwg-fetch": "^3.5.0",
"tslib": "^2.3.0"
"tslib": "^2.3.0",
"whatwg-fetch": "^3.5.0"
},
"devDependencies": {
"@babel/core": "^7.14.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DatePicker as DefaultDatePicker } from "@skbkontur/react-ui";
import moment from "moment";
import React from "react";

import { RussianDateFormat } from "../../Domain/DataTypes/DateTimeRange";
Expand Down Expand Up @@ -90,10 +89,10 @@ export class DatePicker extends React.Component<DatePickerProps, DatePickerState
private readonly convertStringToDate = (newStringifiedDate: RussianDateFormat): Date => {
const { timeZone } = this.props;
const date = DateUtils.convertStringToDate(newStringifiedDate);
const ISODate = moment(date).format("YYYY-MM-DD");
const ISODate = DateUtils.convertDateToString(date, null, "yyyy-MM-dd");
const time = this.props.defaultTime || defaultTime;
const timeZoneOffset = TimeUtils.getTimeZoneOffsetOrDefault(timeZone);
return moment(`${ISODate}T${time}${TimeUtils.timeZoneOffsetToString(timeZoneOffset)}`).toDate();
return new Date(`${ISODate}T${time}${TimeUtils.timeZoneOffsetToString(timeZoneOffset)}`);
};

private readonly convertDateToStringWithTimezone = (date: Nullable<Date | string>, timeZone?: number) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import moment from "moment";
import React from "react";

import { Time, TimeZone } from "../../Domain/DataTypes/Time";
Expand Down Expand Up @@ -35,8 +34,8 @@ export function DateTimePicker({
}

const timeZoneOffset = TimeUtils.getTimeZoneOffsetOrDefault(timeZone);
const date = DateUtils.convertDateToString(value, timeZoneOffset, "YYYY-MM-DD");
const newDateTime = moment(`${date}T${newTime}${TimeUtils.timeZoneOffsetToString(timeZoneOffset)}`).toDate();
const date = DateUtils.convertDateToString(value, timeZoneOffset, "yyyy-MM-dd");
const newDateTime = new Date(`${date}T${newTime}${TimeUtils.timeZoneOffsetToString(timeZoneOffset)}`);
onChange(newDateTime);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Select } from "@skbkontur/react-ui";
import React from "react";

import { Time } from "../../Domain/DataTypes/Time";
import { DateUtils } from "../../Domain/Utils/DateUtils";

import { DatePicker } from "./DatePicker";
import { jsStyles } from "./DateTimePicker.styles";
import { TimePicker } from "./TimePicker";

interface DateTimePickerWithTimeZone {
error?: boolean;
defaultTime: Time;
value: Nullable<string>;
onChange: (value: Nullable<string>) => void;
disabled?: boolean;
timeZoneEditable?: boolean;
}

export function DateTimePickerWithTimeZone({
error,
defaultTime,
value,
onChange,
disabled,
timeZoneEditable,
}: DateTimePickerWithTimeZone): JSX.Element {
const [time, setTime] = React.useState<Nullable<string>>(null);
const [offset, setOffset] = React.useState<Nullable<string>>(null);
const [date, setDate] = React.useState<Nullable<Date>>(null);
React.useEffect(() => loadState(value), [value]);
React.useEffect(() => handleDateTimeChange(date, time, offset), [date, time, offset]);

const handleDateTimeChange = (
newDate: Nullable<Date>,
newTime: Nullable<Time>,
newOffset: Nullable<string>
): void => {
if (!newDate) {
onChange(null);
return;
}
const date = DateUtils.convertDateToString(newDate, null, "yyyy-MM-dd");
const newDateTime = `${date}T${newTime ?? defaultTime}${newOffset ?? ""}`;
onChange(newDateTime);
};

const loadState = (dateStr: Nullable<string>): void => {
if (!dateStr) {
return;
}
const timeOffset = getTimeZoneString(dateStr);
setOffset(timeOffset);
const dateTimeWithoutTimezone = timeOffset ? dateStr.slice(0, -timeOffset.length) : dateStr;
setDate(new Date(dateTimeWithoutTimezone));
setTime(DateUtils.convertDateToString(dateTimeWithoutTimezone, null, "HH:mm:ss.SSS"));
};

const getTimeZoneString = (date: string): Nullable<string> => {
const timezoneRegex = /.*T.*(Z|[+-].*)/i;
const matches = date.match(timezoneRegex);
if (!matches || matches.length < 2) {
return null;
}
return matches[1];
};

return (
<span>
<span className={jsStyles.dateRangeItem()}>
<DatePicker
data-tid="Date"
value={date}
defaultTime={time || defaultTime}
onChange={newDate => setDate(newDate)}
error={error}
disabled={disabled}
width={110}
/>
</span>
<span className={jsStyles.dateRangeItem()}>
<TimePicker
data-tid="Time"
value={time === defaultTime ? null : time}
error={error}
defaultTime={defaultTime}
disabled={disabled || !date}
onChange={(_, newTime) => setTime(newTime)}
useSeconds
/>
</span>
<span className={jsStyles.dateRangeItem()}>
{timeZoneEditable ? (
<Select<string>
data-tid="TimeZoneSelect"
defaultValue="UTC"
value={offset ? "UTC" : "local"}
items={["UTC", "local"]}
onValueChange={v => setOffset(v === "local" ? null : "Z")}
/>
) : (
<span data-tid="TimeOffsetLabel">{offset}</span>
)}
</span>
</span>
);
}
21 changes: 12 additions & 9 deletions db-viewer-ui/src/Components/DateTimeRangePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface TimePickerProps {
disabled?: boolean;
onChange: (e: React.SyntheticEvent<any>, value: Time) => void;
warning?: boolean;
useSeconds?: boolean;
}

interface TimePickerState {
Expand Down Expand Up @@ -45,9 +46,10 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
private handleBlur = (e: React.SyntheticEvent<any>) => {
const { defaultTime } = this.props;
const { value } = this.state;
if (DateUtils.isCorrectTime(value)) {
this.props.onChange(e, value);
if (defaultTime === value) {
const trimmed = value.endsWith(".") || value.endsWith(":") ? value.slice(0, -1) : value;
if (DateUtils.isCorrectTime(trimmed)) {
this.props.onChange(e, trimmed);
if (defaultTime === trimmed) {
this.setState({ value: emptyValue });
}
} else {
Expand All @@ -64,19 +66,20 @@ export class TimePicker extends React.Component<TimePickerProps, TimePickerState
};

public render(): JSX.Element {
const { disabled, warning, error, useSeconds, defaultTime } = this.props;
return (
<Input
data-tid="Input"
disabled={this.props.disabled}
mask="99:99"
disabled={disabled}
mask={useSeconds ? "99:99:99.999" : "99:99"}
value={this.state.value}
width={58}
error={this.props.error}
placeholder={this.props.disabled ? undefined : this.props.defaultTime}
width={useSeconds ? 96 : 58}
error={error}
placeholder={disabled ? undefined : defaultTime}
onValueChange={this.handleChange}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
warning={this.props.warning}
warning={warning}
/>
);
}
Expand Down
9 changes: 5 additions & 4 deletions db-viewer-ui/src/Components/ObjectViewer/ObjectItemRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from "react";
import { PropertyMetaInformation } from "../../Domain/Api/DataTypes/PropertyMetaInformation";
import { ICustomRenderer } from "../../Domain/Objects/CustomRenderer";
import { FileUtils } from "../../Domain/Utils/FileUtils";
import { DateTimePicker } from "../DateTimeRangePicker/DateTimePicker";
import { DateTimePickerWithTimeZone } from "../DateTimeRangePicker/DateTimePickerWithTimeZone";
import { StyledSelect } from "../ObjectFilter/OperatorSelect";

function getByPath(target: Nullable<{}>, path: string[]): any {
Expand Down Expand Up @@ -113,10 +113,11 @@ export function renderForEdit(
case "DateTime":
case "DateTimeOffset":
return (
<DateTimePicker
value={value != null ? new Date(String(value)) : null}
<DateTimePickerWithTimeZone
value={value != null ? String(value) : null}
onChange={onChange}
defaultTime={""}
defaultTime={"00:00:00"}
timeZoneEditable={type === "DateTime"}
/>
);
case "Float":
Expand Down
18 changes: 12 additions & 6 deletions db-viewer-ui/src/Domain/Utils/DateUtils.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import moment from "moment";
import { addMinutes, format, parse } from "date-fns";

import { RussianDateFormat } from "../DataTypes/DateTimeRange";

export class DateUtils {
private static readonly datePickerFormat: RussianDateFormat = "DD.MM.YYYY";
private static readonly datePickerFormat: RussianDateFormat = "dd.MM.yyyy";

public static isCorrectTime(time: string): boolean {
return Boolean(time.match(/^([01]?[0-9]|2[0-3]):[0-5][0-9]/));
}

public static convertDateToString(
date: Date | string,
timeZone: number,
format: string = this.datePickerFormat
timeZone: number | null,
dateFormat: string = this.datePickerFormat
): string {
return moment.utc(date).utcOffset(timeZone).format(format);
const dateDate = timeZone == null ? new Date(date) : this.toTimeZone(new Date(date), timeZone);
return format(dateDate, dateFormat);
}

public static convertStringToDate(date: RussianDateFormat): Date {
return moment(date, this.datePickerFormat).toDate();
return parse(date, this.datePickerFormat, new Date());
}

public static toTimeZone(date: Date | string, timeZoneOffsetInMinutes: number): Date {
const dateDate = new Date(date);
return addMinutes(dateDate, dateDate.getTimezoneOffset() + timeZoneOffsetInMinutes);
}
}
10 changes: 5 additions & 5 deletions db-viewer-ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5294,6 +5294,11 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=

date-fns@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.1.tgz#9667c2615525e552b5135a3116b95b1961456e60"
integrity sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==

debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
Expand Down Expand Up @@ -8997,11 +9002,6 @@ mocha@^9.0.2:
yargs-parser "20.2.4"
yargs-unparser "2.0.0"

moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==

move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
Expand Down

0 comments on commit eddda7f

Please sign in to comment.