Skip to content

Commit

Permalink
Upgrade to date-fns 2.x (#2927)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vultraz authored May 11, 2020
1 parent 15c5731 commit e9a0de2
Show file tree
Hide file tree
Showing 29 changed files with 99 additions and 61 deletions.
31 changes: 31 additions & 0 deletions .changeset/polite-trains-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@keystonejs/demo-project-blog': major
'@keystonejs/demo-project-meetup': major
'@arch-ui/day-picker': major
'@keystonejs/fields': major
'@keystonejs/fields-datetime-utc': major
'@keystonejs/list-plugins': major
'@keystonejs/cypress-project-basic': major
'@keystonejs/cypress-project-client-validation': major
---

Upgraded to date-fns 2.x. This version uses [Unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) for its formatting strings. A conversion table is available [here](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md#200---2019-08-20).

This change only affects the `CalendarDay` and `DateTime` fields' `format` config option.

The following script utilizes the [`@date-fns/upgrade`](https://github.com/date-fns/date-fns-upgrade) package and can be used to convert old formatting strings:

```js
const { convertTokens } = require('@date-fns/upgrade/v2');

console.table(
[
// Add date-dns 1.x formatting strings here.
].map(str => ({
v1: str,
v2: convertTokens(str).replace(/'/g, ''),
}))
);
```

Do note this converts symbols to standalone style as opposed to formatted style which may not always be desired. For example, `DD/MM/YYYY` would be converted to `dd/LL/yyyy` instead of `dd/MM/yyyy`. See [here](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Stand-Alone-vs.-Format-Styles) for more information on which you should use.
4 changes: 2 additions & 2 deletions demo-projects/blog/app/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import gql from 'graphql-tag';
import { useQuery } from '@apollo/react-hooks';

import { jsx } from '@emotion/core';
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';

import Layout from '../templates/layout';
import Header from '../components/header';
Expand Down Expand Up @@ -33,7 +33,7 @@ const Post = ({ post }) => {
<div css={{ marginTop: '1em', borderTop: '1px solid hsl(200, 20%, 80%)' }}>
<p css={{ fontSize: '0.8em', marginBottom: 0, color: 'hsl(200, 20%, 50%)' }}>
Posted by {post.author ? post.author.name : 'someone'} on{' '}
{format(post.posted, 'DD/MM/YYYY')}
{format(parseISO(post.posted), 'dd/MM/yyyy')}
</p>
</div>
</article>
Expand Down
6 changes: 3 additions & 3 deletions demo-projects/blog/app/pages/post/[slug].js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useMutation, useQuery } from '@apollo/react-hooks';
import { useState } from 'react';

import { jsx } from '@emotion/core';
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';

import Layout from '../../templates/layout';
import Header from '../../components/header';
Expand Down Expand Up @@ -98,7 +98,7 @@ const Comments = ({ data }) => (
margin: '8px 0',
}}
>
{comment.author.name} on {format(comment.posted, 'DD MMM YYYY')}
{comment.author.name} on {format(parseISO(comment.posted), 'dd MMM yyyy')}
</p>
<p css={{ margin: '8px 0' }}>{comment.body}</p>
</div>
Expand Down Expand Up @@ -250,7 +250,7 @@ const PostPage = withApollo(({ slug }) => {
<div css={{ marginTop: '1em', borderTop: '1px solid hsl(200, 20%, 80%)' }}>
<p css={{ fontSize: '0.8em', marginBottom: 0, color: 'hsl(200, 20%, 50%)' }}>
Posted by {post.author ? post.author.name : 'someone'} on{' '}
{format(post.posted, 'DD/MM/YYYY')}
{format(parseISO(post.posted), 'dd/MM/yyyy')}
</p>
</div>
</article>
Expand Down
2 changes: 1 addition & 1 deletion demo-projects/blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"apollo-client": "^2.6.8",
"apollo-upload-client": "^12.1.0",
"cross-env": "^7.0.0",
"date-fns": "^1.30.1",
"date-fns": "^2.13.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"graphql-tag": "^2.10.1",
Expand Down
6 changes: 3 additions & 3 deletions demo-projects/blog/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const {
const { Wysiwyg } = require('@keystonejs/fields-wysiwyg-tinymce');
const { AuthedRelationship } = require('@keystonejs/fields-authed-relationship');
const { LocalFileAdapter } = require('@keystonejs/file-adapters');
const getYear = require('date-fns/get_year');
const getYear = require('date-fns/getYear');

const { staticRoute, staticPath, distDir } = require('./config');
const dev = process.env.NODE_ENV !== 'production';
Expand Down Expand Up @@ -44,7 +44,7 @@ exports.User = {
email: { type: Text, isUnique: true },
dob: {
type: CalendarDay,
format: 'Do MMMM YYYY',
format: 'do MMMM yyyy',
yearRangeFrom: 1901,
yearRangeTo: getYear(new Date()),
},
Expand Down Expand Up @@ -89,7 +89,7 @@ exports.Post = {
],
},
body: { type: Wysiwyg },
posted: { type: DateTime, format: 'DD/MM/YYYY' },
posted: { type: DateTime, format: 'dd/MM/yyyy' },
image: {
type: File,
adapter: fileAdapter,
Expand Down
2 changes: 1 addition & 1 deletion demo-projects/meetup/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"apollo-upload-client": "^12.1.0",
"body-parser": "^1.18.2",
"cross-env": "^7.0.0",
"date-fns": "^1.30.1",
"date-fns": "^2.13.0",
"dotenv": "^8.2.0",
"eslint-plugin-emotion": "^10.0.27",
"express": "^4.17.1",
Expand Down
6 changes: 3 additions & 3 deletions demo-projects/meetup/site/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { format, isFuture } from 'date-fns';
import { format, parseISO, isFuture } from 'date-fns';
import getConfig from 'next/config';
import contrast from 'get-contrast';

Expand All @@ -13,8 +13,8 @@ const {
export const isInFuture = date => isFuture(date);

// Pretty date formatting
export const formatFutureDate = date => format(date, 'ddd D MMM, h:mm A');
export const formatPastDate = date => format(date, 'MMM YYYY');
export const formatFutureDate = date => format(parseISO(date), 'iii d MMM, h:mm a');
export const formatPastDate = date => format(parseISO(date), 'LLL yyyy');

// Singular / Plural
export const pluralLabel = (num, single, plural) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/arch/packages/day-picker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"chrono-node": "^1.4.6",
"date-fns": "^1.30.1",
"date-fns": "^2.13.0",
"intersection-observer": "^0.10.0",
"moment": "^2.24.0",
"react-window": "^1.7.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/arch/packages/day-picker/src/DayPicker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function scrollToDate(date, yearRangeFrom, yearRangeTo, list) {
let weekLabels = (
<WeekLabels>
{[...new Array(7)]
.map((_, day) => format(setDay(new Date(), day), 'ddd'))
.map((_, day) => format(setDay(new Date(), day), 'iii'))
.map(d => (
<Day key={d}>{d}</Day>
))}
Expand Down
2 changes: 1 addition & 1 deletion packages/arch/packages/day-picker/src/DayPicker/month.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const Month = memo(({ style, index, data }) => {
);
});

let readableMonths = months.map(month => format(setMonth(new Date(), month), 'MMMM'));
const readableMonths = months.map(month => format(setMonth(new Date(), month), 'LLLL'));

const MonthHeader = memo(({ month, year }) => {
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/arch/packages/day-picker/src/DayPicker/selects.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { months, yearRange, usePrevious, isNumberInRange } from './utils';

const monthOptions = months.map((month, i) => (
<option key={i} value={i}>
{format(setMonth(new Date(), month), 'MMM')}
{format(setMonth(new Date(), month), 'LLL')}
</option>
));

Expand Down
4 changes: 2 additions & 2 deletions packages/arch/packages/day-picker/src/DayPicker/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
isSameMonth,
startOfMonth,
eachDay,
eachDayOfInterval,
addWeeks,
startOfWeek,
endOfWeek,
Expand Down Expand Up @@ -36,7 +36,7 @@ export function getWeeksInMonth(date) {
const lastDayOfFirstWeek = endOfWeek(firstDayOfMonth, weekOptions);

const getWeeks = (startDay, endDay, weekArray) => {
const week = eachDay(startDay, endDay).map(createDayObject);
const week = eachDayOfInterval({ start: startDay, end: endDay }).map(createDayObject);
const weeks = [...weekArray, week];
const nextWeek = addWeeks(startDay, 1);

Expand Down
6 changes: 3 additions & 3 deletions packages/arch/packages/day-picker/src/DayTimePicker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { parse } from 'date-fns';
import { parseISO } from 'date-fns';
import { DayPicker } from './DayPicker';
import { Input } from '@arch-ui/input';
import Select from '@arch-ui/select';
Expand Down Expand Up @@ -56,8 +56,8 @@ export const DayTimePicker = props => {
yearRangeFrom={yearRangeFrom}
yearRangeTo={yearRangeTo}
yearPickerType={yearPickerType}
startCurrentDateAt={date ? parse(date) : TODAY}
selectedDate={date ? parse(date) : null}
startCurrentDateAt={date ? parseISO(date) : TODAY}
selectedDate={date ? parseISO(date) : null}
/>
<Input
type="time"
Expand Down
8 changes: 4 additions & 4 deletions packages/arch/packages/day-picker/src/TextDayPicker.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useState, useEffect, forwardRef, useRef } from 'react';
import chrono from 'chrono-node';
import { Input } from '@arch-ui/input';
import { format } from 'date-fns';
import { format, formatISO, parseISO } from 'date-fns';

export const TextDayPicker = forwardRef(
({ date = '', onChange, format: displayFormat = 'Do MMMM YYYY', ...props }, ref) => {
const formatDate = newDate => (newDate ? format(newDate, displayFormat) : '');
({ date = '', onChange, format: displayFormat = 'do MMMM yyyy', ...props }, ref) => {
const formatDate = newDate => (newDate ? format(parseISO(newDate), displayFormat) : '');

const [isEditing, setIsEditing] = useState(false);
const [value, setValue] = useState({
Expand All @@ -20,7 +20,7 @@ export const TextDayPicker = forwardRef(
const parsedDate = chrono.parseDate(value.raw);

// If valid, convert it to ISO 8601.
const isoDate = parsedDate ? format(parsedDate, 'YYYY-MM-DD') : null;
const isoDate = parsedDate ? formatISO(parsedDate, { representation: 'date' }) : null;

// Pass it up the tree. The parent can handle the null case.
onChange(isoDate);
Expand Down
2 changes: 1 addition & 1 deletion packages/fields-datetime-utc/src/Implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class DateTimeUtcImplementation extends Implementation {
}

extendAdminMeta(meta) {
return { ...meta, format: 'YYYY-MM-DD[T]HH:mm:ss.SSSZZ' };
return { ...meta, format: 'yyyy-MM-dd[T]HH:mm:ss.SSSxx' };
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/fields/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"apollo-errors": "^1.9.0",
"bcryptjs": "^2.4.3",
"cuid": "^2.1.8",
"date-fns": "^1.30.1",
"date-fns": "^2.13.0",
"dumb-passwords": "^0.2.1",
"google-maps-react": "^2.0.2",
"graphql": "^14.6.0",
Expand Down
12 changes: 6 additions & 6 deletions packages/fields/src/types/CalendarDay/Implementation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import parse from 'date-fns/parse';
import format from 'date-fns/format';
import { formatISO, parseISO } from 'date-fns';
import { Implementation } from '../../Implementation';
import { MongooseFieldAdapter } from '@keystonejs/adapter-mongoose';
import { KnexFieldAdapter } from '@keystonejs/adapter-knex';
Expand All @@ -8,7 +7,7 @@ export class CalendarDay extends Implementation {
constructor(
path,
{
format = 'YYYY-MM-DD',
format = 'yyyy-MM-dd',
yearRangeFrom = new Date().getFullYear() - 100,
yearRangeTo = new Date().getFullYear(),
}
Expand Down Expand Up @@ -63,12 +62,13 @@ const CommonCalendarInterface = superclass =>

export class MongoCalendarDayInterface extends CommonCalendarInterface(MongooseFieldAdapter) {
addToMongooseSchema(schema) {
const validator = a => typeof a === 'string' && format(parse(a), 'YYYY-MM-DD') === a;
const validator = a =>
typeof a === 'string' && formatISO(parseISO(a), { representation: 'date' }) === a;
const schemaOptions = {
type: String,
validate: {
validator: this.buildValidator(validator),
message: '{VALUE} is not an ISO8601 date string (YYYY-MM-DD)',
message: '{VALUE} is not an ISO8601 date string (yyyy-MM-dd)',
},
};
schema.add({ [this.path]: this.mergeSchemaOptions(schemaOptions, this.config) });
Expand All @@ -93,7 +93,7 @@ export class KnexCalendarDayInterface extends CommonCalendarInterface(KnexFieldA
setupHooks({ addPostReadHook }) {
addPostReadHook(item => {
if (item[this.path]) {
item[this.path] = format(item[this.path], 'YYYY-MM-DD');
item[this.path] = formatISO(item[this.path], { representation: 'date' });
}
return item;
});
Expand Down
8 changes: 4 additions & 4 deletions packages/fields/src/types/CalendarDay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ keystone.createList('User', {
#### `format`

Defines the format of date string that the will be displayed in the Admin UI.
The Admin UI uses the [`date-fns` v1.x](https://date-fns.org/v1.30.1/docs/format) library for formatting, and any format supported by that library is valid. E.g.
The Admin UI uses the [`date-fns` v2.x](https://date-fns.org/v2.13.0/docs/format) library for formatting, and any format supported by that library is valid. E.g.

- `YYYY-MM-DD` => "1970-01-31"
- `MM/DD/YYYY` => "01/31/1970"
- `MMM Do, YY` => "Jan 31st, 70"
- `yyyy-MM-dd` => "1970-01-31"
- `MM/dd/yyyy` => "01/31/1970"
- `MMM do, yy` => "Jan 31st, 70"

#### `yearRangeFrom`

Expand Down
4 changes: 2 additions & 2 deletions packages/fields/src/types/CalendarDay/views/Cell.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';

const CalendarDayCell = ({ data, field: { config } }) => {
if (!data) {
Expand All @@ -9,7 +9,7 @@ const CalendarDayCell = ({ data, field: { config } }) => {
return data;
}

return format(data, config.format);
return format(parseISO(data), config.format);
};

export default CalendarDayCell;
4 changes: 2 additions & 2 deletions packages/fields/src/types/CalendarDay/views/Controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FieldController from '../../../Controller';
import { getYear } from 'date-fns';
import { getYear, parseISO } from 'date-fns';

export default class CalendarDayController extends FieldController {
getFilterGraphQL = ({ type, value }) => {
Expand Down Expand Up @@ -61,7 +61,7 @@ export default class CalendarDayController extends FieldController {
validateInput = ({ resolvedData, addFieldValidationError }) => {
const { yearRangeFrom, yearRangeTo } = this.config;

const inputYear = getYear(resolvedData[this.path]);
const inputYear = getYear(parseISO(resolvedData[this.path]));
const inRange = yearRangeFrom <= inputYear && inputYear <= yearRangeTo;

if (!inRange) {
Expand Down
4 changes: 2 additions & 2 deletions packages/fields/src/types/DateTime/views/Cell.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';

const DateTimeCell = props => {
if (!props.data) {
Expand All @@ -8,7 +8,7 @@ const DateTimeCell = props => {
if (!formatConfig) {
return props.data;
}
return format(props.data, formatConfig);
return format(parseISO(props.data), formatConfig);
};

export default DateTimeCell;
4 changes: 2 additions & 2 deletions packages/fields/src/types/DateTime/views/Controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { format } from 'date-fns';
import { format, parseISO } from 'date-fns';
import FieldController from '../../../Controller';

export default class DateTimeController extends FieldController {
Expand All @@ -14,7 +14,7 @@ export default class DateTimeController extends FieldController {
let formattedValue = value;

if (formatConfig) {
formattedValue = format(value, formatConfig);
formattedValue = format(parseISO(value), formatConfig);
}

return `${this.getFilterLabel({ label })}: "${formattedValue}"`;
Expand Down
12 changes: 7 additions & 5 deletions packages/fields/src/types/DateTime/views/Filter.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import React from 'react';
import { format } from 'date-fns';
import { formatISO } from 'date-fns';
import { DayTimePicker } from '@arch-ui/day-picker';
import { stringifyDate, parseDate } from './utils';

const DateTimeFilterView = props => {
const parsedDate = props.value ? parseDate(props.value) : parseDate(new Date().toISOString());

let handleDayChange = day => {
props.onChange(stringifyDate({ ...parsedDate, date: format(day, 'YYYY-MM-DD') }));
const handleDayChange = day => {
props.onChange(
stringifyDate({ ...parsedDate, date: formatISO(day, { representation: 'date' }) })
);
};

let handleTimeChange = event => {
const handleTimeChange = event => {
props.onChange(stringifyDate({ ...parsedDate, time: event.target.value }));
};

let handleOffsetChange = offset => {
const handleOffsetChange = offset => {
props.onChange(stringifyDate({ ...parsedDate, offset }));
};

Expand Down
Loading

0 comments on commit e9a0de2

Please sign in to comment.