Skip to content

Commit

Permalink
replace moment for luxon
Browse files Browse the repository at this point in the history
  • Loading branch information
olmobrutall committed Sep 19, 2020
1 parent 0ece16e commit b209617
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 127 deletions.
226 changes: 178 additions & 48 deletions Signum.React/Scripts/ConfigureReactWidgets.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
import * as moment from "moment"
import { DateTime } from 'luxon';

import * as ReactWidgets from 'react-widgets';

export function configure() {

if (typeof moment !== 'function') throw new TypeError('You must provide a valid moment object');

const localField = (m: moment.Moment) => m.locale || m.lang,
hasLocaleData = !!moment.localeData;

if (!hasLocaleData) throw new TypeError('The Moment localizer depends on the `localeData` api, please provide a moment object v2.2.0 or higher');

function getMoment(culture: string, value: any, format: string | undefined) {
return culture ? localField(moment(value, format))(culture) : moment(value, format);
}

function endOfDecade(date: Date) {
return moment(date).add(10, 'year').add(-1, 'millisecond').toDate();
return DateTime.fromJSDate(date).plus({ years: 10 }).minus({ millisecond: 1 }).toJSDate();
}

function endOfCentury(date: Date) {
return moment(date).add(100, 'year').add(-1, 'millisecond').toDate();
return DateTime.fromJSDate(date).plus({ years: 100 }).minus({ millisecond: 1 }).toJSDate();
}

const localizer = {
formats: {
date: 'L',
time: 'LT',
'default': 'lll',
header: 'MMMM YYYY',
footer: 'LL',
weekday: 'dd',
dayOfMonth: 'DD',
date: 'D',
time: 't',
'default': 'FF',
header: 'MMMM yyyy',
footer: 'DDD',
weekday: 'EEE',
dayOfMonth: 'dd',
month: 'MMM',
year: 'YYYY',
year: 'yyyy',

decade: function decade(date: Date, culture: string, localizer: any) {
return localizer.format(date, 'YYYY', culture) + ' - ' + localizer.format(endOfDecade(date), 'YYYY', culture);
Expand All @@ -45,16 +35,24 @@ export function configure() {
},

firstOfWeek: function firstOfWeek(culture: string) {
return (moment.localeData(culture) as any).firstDayOfWeek();
var day = fistDay[culture?.tryAfter("-") ?? "ES"];

switch (day) {
case "sun": return 0;
case "mon": return 1;
case "fri": return 5;
case "sat": return 6;
default: throw new Error("Unexpected " + day);
}
},

parse: function parse(value: string, format: string, culture: string) {
if (value == undefined || value == "")
return undefined;

var moment = getMoment(culture, value, format);
if (moment.isValid())
return moment.toDate();
var t = DateTime.fromFormat(value, format ?? "F", { locale: culture })
if (t.isValid)
return t.toJSDate();

return undefined;
},
Expand All @@ -63,33 +61,165 @@ export function configure() {
if (value == undefined)
return "";

return getMoment(culture, value, undefined).format(_format);
return DateTime.fromJSDate(value, { locale: culture }).toFormat(_format);
}
};
(ReactWidgets as any).setDateLocalizer(localizer);

}

declare module "moment" {

interface Moment {
fromUserInterface(this: moment.Moment): moment.Moment;
toUserInterface(this: moment.Moment): moment.Moment;
}
//https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/weekData.json#L61
const fistDay: { [isoCode: string]: "mon" | "sat" | "sun" | "fri" } = {
"001": "mon",
"AD": "mon",
"AE": "sat",
"AF": "sat",
"AG": "sun",
"AI": "mon",
"AL": "mon",
"AM": "mon",
"AN": "mon",
"AR": "mon",
"AS": "sun",
"AT": "mon",
"AU": "sun",
"AX": "mon",
"AZ": "mon",
"BA": "mon",
"BD": "sun",
"BE": "mon",
"BG": "mon",
"BH": "sat",
"BM": "mon",
"BN": "mon",
"BR": "sun",
"BS": "sun",
"BT": "sun",
"BW": "sun",
"BY": "mon",
"BZ": "sun",
"CA": "sun",
"CH": "mon",
"CL": "mon",
"CM": "mon",
"CN": "sun",
"CO": "sun",
"CR": "mon",
"CY": "mon",
"CZ": "mon",
"DE": "mon",
"DJ": "sat",
"DK": "mon",
"DM": "sun",
"DO": "sun",
"DZ": "sat",
"EC": "mon",
"EE": "mon",
"EG": "sat",
"ES": "mon",
"ET": "sun",
"FI": "mon",
"FJ": "mon",
"FO": "mon",
"FR": "mon",
"GB": "mon",
"GB-alt-variant": "sun",
"GE": "mon",
"GF": "mon",
"GP": "mon",
"GR": "mon",
"GT": "sun",
"GU": "sun",
"HK": "sun",
"HN": "sun",
"HR": "mon",
"HU": "mon",
"ID": "sun",
"IE": "mon",
"IL": "sun",
"IN": "sun",
"IQ": "sat",
"IR": "sat",
"IS": "mon",
"IT": "mon",
"JM": "sun",
"JO": "sat",
"JP": "sun",
"KE": "sun",
"KG": "mon",
"KH": "sun",
"KR": "sun",
"KW": "sat",
"KZ": "mon",
"LA": "sun",
"LB": "mon",
"LI": "mon",
"LK": "mon",
"LT": "mon",
"LU": "mon",
"LV": "mon",
"LY": "sat",
"MC": "mon",
"MD": "mon",
"ME": "mon",
"MH": "sun",
"MK": "mon",
"MM": "sun",
"MN": "mon",
"MO": "sun",
"MQ": "mon",
"MT": "sun",
"MV": "fri",
"MX": "sun",
"MY": "mon",
"MZ": "sun",
"NI": "sun",
"NL": "mon",
"NO": "mon",
"NP": "sun",
"NZ": "mon",
"OM": "sat",
"PA": "sun",
"PE": "sun",
"PH": "sun",
"PK": "sun",
"PL": "mon",
"PR": "sun",
"PT": "sun",
"PY": "sun",
"QA": "sat",
"RE": "mon",
"RO": "mon",
"RS": "mon",
"RU": "mon",
"SA": "sun",
"SD": "sat",
"SE": "mon",
"SG": "sun",
"SI": "mon",
"SK": "mon",
"SM": "mon",
"SV": "sun",
"SY": "sat",
"TH": "sun",
"TJ": "mon",
"TM": "mon",
"TR": "mon",
"TT": "sun",
"TW": "sun",
"UA": "mon",
"UM": "sun",
"US": "sun",
"UY": "mon",
"UZ": "mon",
"VA": "mon",
"VE": "sun",
"VI": "sun",
"VN": "mon",
"WS": "sun",
"XK": "mon",
"YE": "sun",
"ZA": "sun",
"ZW": "sun"
};

function smartNow(this: moment.Moment): moment.Moment;
}

export function asumeGlobalUtcMode(m: typeof moment, utcMode: boolean) {
if (utcMode) {
m.fn.fromUserInterface = function (this: moment.Moment) { return this.utc(); };
m.fn.toUserInterface = function (this: moment.Moment) { return this.local(); };
m.smartNow = function () { return moment.utc(); };
}

else {
m.fn.fromUserInterface = function (this: moment.Moment) { return this; };
m.fn.toUserInterface = function (this: moment.Moment) { return this; };
m.smartNow = function () { return moment(); };
}
}
4 changes: 2 additions & 2 deletions Signum.React/Scripts/Exceptions/Exception.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import * as moment from 'moment'
import { DateTime } from 'luxon'
import { BigStringEmbedded, ExceptionEntity } from '../Signum.Entities.Basics'
import { ValueLine, EntityLine, TypeContext } from '../Lines'
import { Tab, Tabs } from 'react-bootstrap';
Expand All @@ -14,7 +14,7 @@ export default function Exception(p: { ctx: TypeContext<ExceptionEntity> }) {
<div className="row">
<div className="col-sm-6">
<ValueLine ctx={sc.subCtx(f => f.environment)} />
<ValueLine ctx={sc.subCtx(f => f.creationDate)} unitText={moment(sc.value.creationDate!).toUserInterface().fromNow()} />
<ValueLine ctx={sc.subCtx(f => f.creationDate)} unitText={DateTime.fromISO(sc.value.creationDate!).toRelative() ?? undefined} />
<EntityLine ctx={sc.subCtx(f => f.user)} />
<ValueLine ctx={sc.subCtx(f => f.version)} />
<ValueLine ctx={sc.subCtx(f => f.threadId)} />
Expand Down
12 changes: 6 additions & 6 deletions Signum.React/Scripts/Finder.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import * as moment from "moment"
import { DateTime } from 'luxon'
import numbro from "numbro"
import * as AppContext from "./AppContext"
import * as Navigator from "./Navigator"
Expand All @@ -21,7 +21,7 @@ import { TypeEntity, QueryEntity } from './Signum.Entities.Basics';

import {
Type, IType, EntityKind, QueryKey, getQueryNiceName, getQueryKey, isQueryDefined, TypeReference,
getTypeInfo, tryGetTypeInfos, getEnumInfo, toMomentFormat, toNumbroFormat, PseudoType, EntityData,
getTypeInfo, tryGetTypeInfos, getEnumInfo, toLuxonFormat, toNumbroFormat, PseudoType, EntityData,
TypeInfo, PropertyRoute, QueryTokenString, getTypeInfos, tryGetTypeInfo, onReloadTypesActions
} from './Reflection';

Expand Down Expand Up @@ -1602,8 +1602,8 @@ export const formatRules: FormatRule[] = [
name: "Date",
isApplicable: col => col.token!.filterType == "DateTime",
formatter: col => {
const momentFormat = toMomentFormat(col.token!.format);
return new CellFormatter((cell: string | undefined) => cell == undefined || cell == "" ? "" : <bdi className="date">{moment(cell).format(momentFormat)}</bdi>) //To avoid flippig hour and date (L LT) in RTL cultures
const luxonFormat = toLuxonFormat(col.token!.format);
return new CellFormatter((cell: string | undefined) => cell == undefined || cell == "" ? "" : <bdi className="date">{DateTime.fromISO(cell).toFormat(luxonFormat)}</bdi>) //To avoid flippig hour and date (L LT) in RTL cultures
}
},
{
Expand All @@ -1618,7 +1618,7 @@ export const formatRules: FormatRule[] = [
ctx.systemTime && ctx.systemTime.mode == "Between" && ctx.systemTime.startDate! < cell ? "date-created" :
undefined;

return <bdi className={classes("date", className)}>{moment(cell).format("YYYY-MM-DDTHH:mm:ss")}</bdi>; //To avoid flippig hour and date (L LT) in RTL cultures
return <bdi className={classes("date", className)}>{DateTime.fromISO(cell).toFormat("yyyy-MM-dd'T'HH:mm:ss")}</bdi>; //To avoid flippig hour and date (L LT) in RTL cultures
});
}
},
Expand All @@ -1634,7 +1634,7 @@ export const formatRules: FormatRule[] = [
ctx.systemTime && ctx.systemTime.mode == "Between" && cell < ctx.systemTime.endDate! ? "date-removed" :
undefined;

return <bdi className={classes("date", className)}>{moment(cell).format("YYYY-MM-DDTHH:mm:ss")}</bdi>; //To avoid flippig hour and date (L LT) in RTL cultures
return <bdi className={classes("date", className)}>{DateTime.fromISO(cell).toFormat("yyyy-MM-dd'T'HH:mm:ss")}</bdi>; //To avoid flippig hour and date (L LT) in RTL cultures
});
}
},
Expand Down
8 changes: 4 additions & 4 deletions Signum.React/Scripts/Frames/VersionChangedAlert.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import * as moment from 'moment'
import { DateTime } from 'luxon'
import { classes } from '../Globals'
import { VersionFilter } from '../Services'
import { ConnectionMessage } from '../Signum.Entities';
Expand Down Expand Up @@ -54,13 +54,13 @@ export function VersionInfo() {

function VersionInfoTooltip(p: {}) {

var bt = moment(VersionFilter.initialBuildTime!);
var bt = DateTime.fromISO(VersionFilter.initialBuildTime!);

return (
<div style={{ whiteSpace: "nowrap" }}>
{bt.format("llll")}
{bt.toFormat("FFFF")}
<br />
({bt.fromNow()})
({bt.toRelative()})
</div>
);
}
Expand Down
Loading

1 comment on commit b209617

@olmobrutall
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace moment.js by luxon

Momen.js is no considered legacy by its own author and they recommend luxon as the best alternative.

Some advantages:

  • Luxon API is immutable and called DateTime, very similar to .Net DateTime.
  • The API is in general more clear and explicit. Here a comparison
  • Luxon API relies on the native Intl.DateTimeFormat, implemented by even IE 11, so doesn't have to implement all the translations.

How to migrate

Check this commit in Southwind signumsoftware/southwind@0fd8bcf

Please sign in to comment.