diff --git a/lib/admin/server.js b/lib/admin/server.js
index 0aa93706..cc5d5415 100644
--- a/lib/admin/server.js
+++ b/lib/admin/server.js
@@ -1,5 +1,5 @@
+const { intervalToDuration, secondsToMilliseconds, formatDuration } = require('date-fns/fp')
const _ = require('lodash/fp')
-const moment = require('moment')
const ticker = require('../ticker')
const settingsLoader = require('./settings-loader')
@@ -37,8 +37,8 @@ function machinesLastPing () {
if (downRows.length === 1) {
const row = downRows[0]
- const age = moment.duration(row.age, 'seconds')
- return `${row.name} down for ${age.humanize()}`
+ const age = intervalToDuration({ start: 0, end: secondsToMilliseconds(row.age) })
+ return `${row.name} down for ${formatDuration(age)}`
}
return 'Multiple machines down'
@@ -54,9 +54,9 @@ function status () {
return Promise.all([checkWasConfigured(), db.oneOrNone(sql, ['ping']), machinesLastPing()])
.then(([wasConfigured, statusRow, machineStatus]) => {
- const age = statusRow && moment.duration(statusRow.age, 'seconds')
+ const age = statusRow && intervalToDuration({ start: 0, end: secondsToMilliseconds(statusRow.age) })
const up = statusRow ? statusRow.age < CONSIDERED_UP_SECS : false
- const lastPing = statusRow && age.humanize()
+ const lastPing = statusRow && formatDuration(age)
return settingsLoader.loadLatest()
.catch(() => null)
diff --git a/lib/customers.js b/lib/customers.js
index c7aa00e3..28808f2e 100644
--- a/lib/customers.js
+++ b/lib/customers.js
@@ -6,7 +6,7 @@ const makeDir = require('make-dir')
const path = require('path')
const fs = require('fs')
const util = require('util')
-const moment = require('moment')
+const { sub, differenceInHours } = require('date-fns/fp')
const db = require('./db')
const BN = require('./bn')
@@ -215,14 +215,14 @@ function getDailyVolumeMinusCurrentTxQueries (id, txId) {
}
function getHoursTillLimitClear (cashInDate, cashOutDate) {
- let startDate = moment()
- startDate = startDate.subtract(1, 'days')
+ let startDate = new Date()
+ startDate = sub({ days: 1 }, startDate)
- const cashInMoment = moment(cashInDate || startDate)
- const cashOutMoment = moment(cashOutDate || startDate)
+ const cashInMoment = new Date(cashInDate || startDate)
+ const cashOutMoment = new Date(cashOutDate || startDate)
- const cashInDuration = moment.duration(cashInMoment.diff(startDate)).asHours()
- const cashOutDuration = moment.duration(cashOutMoment.diff(startDate)).asHours()
+ const cashInDuration = differenceInHours(cashInMoment, startDate)
+ const cashOutDuration = differenceInHours(cashOutMoment, startDate)
return _.ceil(_.max([cashInDuration, cashOutDuration, 0]))
}
diff --git a/lib/logs.js b/lib/logs.js
index 50e806b7..3b8421bf 100644
--- a/lib/logs.js
+++ b/lib/logs.js
@@ -1,5 +1,6 @@
const _ = require('lodash/fp')
-const moment = require('moment')
+const { format } = require('date-fns/fp')
+const { utcToZonedTime } = require('date-fns-tz/fp')
const db = require('./db')
const pgp = require('pg-promise')()
@@ -112,12 +113,17 @@ function simpleGetMachineLogs (deviceId, from = new Date(0).toISOString(), until
}
function logDateFormat (timezone, logs, fields) {
- const offset = timezone.split(':')[1]
-
return _.map(log => {
- const values = _.map(field => moment.utc(log[field]).utcOffset(parseInt(offset)).format('YYYY-MM-DDTHH:mm:ss.SSS'), fields)
+ const values = _.map(
+ field =>
+ {
+ if (_.isNil(log[field])) return null
+ const date = utcToZonedTime(timezone, log[field])
+ return `${format('yyyy-MM-dd', date)}T${format('HH:mm:ss.SSS', date)}`
+ },
+ fields
+ )
const fieldsToOverride = _.zipObject(fields, values)
-
return {
...log,
...fieldsToOverride
diff --git a/lib/plugins.js b/lib/plugins.js
index fe7bd663..6406d657 100644
--- a/lib/plugins.js
+++ b/lib/plugins.js
@@ -3,6 +3,8 @@ const argv = require('minimist')(process.argv.slice(2))
const crypto = require('crypto')
const pgp = require('pg-promise')()
const dateFormat = require('dateformat')
+const { getTimezoneOffset } = require('date-fns-tz')
+const { millisecondsToMinutes } = require('date-fns/fp')
const BN = require('./bn')
const dbm = require('./postgresql_interface')
@@ -230,7 +232,7 @@ function plugins (settings, deviceId) {
const localeConfig = configManager.getLocale(deviceId, settings.config)
const fiatCode = localeConfig.fiatCurrency
const cryptoCodes = localeConfig.cryptoCurrencies
- const timezone = localeConfig.timezone.split(':')
+ const timezone = millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone))
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
@@ -239,13 +241,12 @@ function plugins (settings, deviceId) {
const currentConfigVersionPromise = fetchCurrentConfigVersion()
const currentAvailablePromoCodes = loyalty.getNumberOfAvailablePromoCodes()
const supportsBatchingPromise = cryptoCodes.map(c => wallet.supportsBatching(settings, c))
- const timezoneObj = { utcOffset: timezone[0], dstOffset: timezone[1] }
const promises = [
buildAvailableCassettes(),
pingPromise,
currentConfigVersionPromise,
- timezoneObj
+ timezone
].concat(
supportsBatchingPromise,
tickerPromises,
diff --git a/lib/routes/customerRoutes.js b/lib/routes/customerRoutes.js
index 1093a91d..2ea098e3 100644
--- a/lib/routes/customerRoutes.js
+++ b/lib/routes/customerRoutes.js
@@ -4,6 +4,7 @@ const semver = require('semver')
const sms = require('../sms')
const _ = require('lodash/fp')
const BN = require('../bn')
+const { zonedTimeToUtc, utcToZonedTime } = require('date-fns-tz/fp')
const compliance = require('../compliance')
const complianceTriggers = require('../compliance-triggers')
@@ -129,14 +130,12 @@ function buildSms (data, receiptOptions) {
.then(([customer, deviceConfig]) => {
const formattedTx = _.mapKeys(_.camelCase)(tx)
const localeConfig = configManager.getLocale(formattedTx.deviceId, config)
- const timezone = localeConfig.timezone.split(':')
- const dstOffset = timezone[1]
+ const timezone = localeConfig.timezone
const cashInCommission = new BN(1).plus(new BN(formattedTx.commissionPercentage))
const rate = new BN(formattedTx.rawTickerPrice).multipliedBy(cashInCommission).decimalPlaces(2)
- const date = new Date()
- date.setMinutes(date.getMinutes() + parseInt(dstOffset))
+ const date = utcToZonedTime(timezone, zonedTimeToUtc(process.env.TZ, new Date()))
const dateString = `${date.toISOString().replace('T', ' ').slice(0, 19)}`
const data = {
diff --git a/new-lamassu-admin/package-lock.json b/new-lamassu-admin/package-lock.json
index cad5fe31..161c8a4e 100644
--- a/new-lamassu-admin/package-lock.json
+++ b/new-lamassu-admin/package-lock.json
@@ -10465,6 +10465,16 @@
"whatwg-url": "^8.0.0"
}
},
+ "date-fns": {
+ "version": "2.26.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.26.0.tgz",
+ "integrity": "sha512-VQI812dRi3cusdY/fhoBKvc6l2W8BPWU1FNVnFH9Nttjx4AFBRzfSVb/Eyc7jBT6e9sg1XtAGsYpBQ6c/jygbg=="
+ },
+ "date-fns-tz": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.1.6.tgz",
+ "integrity": "sha512-nyy+URfFI3KUY7udEJozcoftju+KduaqkVfwyTIE0traBiVye09QnyWKLZK7drRr5h9B7sPJITmQnS3U6YOdQg=="
+ },
"debug": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
@@ -18316,11 +18326,6 @@
"minimist": "^1.2.5"
}
},
- "moment": {
- "version": "2.24.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
- },
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
diff --git a/new-lamassu-admin/package.json b/new-lamassu-admin/package.json
index 6d4a0e43..bb59ee3d 100644
--- a/new-lamassu-admin/package.json
+++ b/new-lamassu-admin/package.json
@@ -19,6 +19,8 @@
"classnames": "2.2.6",
"countries-and-timezones": "^2.4.0",
"d3": "^6.2.0",
+ "date-fns": "^2.26.0",
+ "date-fns-tz": "^1.1.6",
"downshift": "3.3.4",
"file-saver": "2.0.2",
"formik": "2.2.0",
@@ -29,15 +31,14 @@
"lamassu-coins": "git+https://github.com/lamassu/lamassu-coins.git",
"libphonenumber-js": "^1.7.50",
"match-sorter": "^4.2.0",
- "moment": "2.24.0",
"pretty-ms": "^2.1.0",
"qrcode.react": "0.9.3",
"ramda": "^0.26.1",
"react": "^16.12.0",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "^16.10.2",
- "react-material-ui-carousel": "^2.2.7",
"react-dropzone": "^11.4.2",
+ "react-material-ui-carousel": "^2.2.7",
"react-number-format": "^4.4.1",
"react-otp-input": "^2.3.0",
"react-router-dom": "5.1.2",
diff --git a/new-lamassu-admin/src/components/LogsDownloaderPopper.js b/new-lamassu-admin/src/components/LogsDownloaderPopper.js
index 9d64082f..9c02760a 100644
--- a/new-lamassu-admin/src/components/LogsDownloaderPopper.js
+++ b/new-lamassu-admin/src/components/LogsDownloaderPopper.js
@@ -1,8 +1,8 @@
import { useLazyQuery } from '@apollo/react-hooks'
import { makeStyles, ClickAwayListener } from '@material-ui/core'
import classnames from 'classnames'
+import { format, isSameDay } from 'date-fns/fp'
import FileSaver from 'file-saver'
-import moment from 'moment'
import * as R from 'ramda'
import React, { useState, useCallback } from 'react'
@@ -65,12 +65,13 @@ const DateContainer = ({ date, children, ...props }) => {
{date && (
<>
-
{date.format('D')}
+
{format('d', date)}
- {`${date.format(
- 'MMM'
- )} ${date.format('YYYY')}`}
- {date.format('dddd')}
+ {`${format(
+ 'MMM',
+ date
+ )} ${format('yyyy', date)}`}
+ {format('EEEE', date)}
>
@@ -186,8 +187,8 @@ const LogsDownloaderPopover = ({
}
if (!range || !range.from) return
- if (range.from && !range.until) range.until = moment()
- if (moment(range.from).isSame(range.until, 'day')) range.until = moment()
+ if (range.from && !range.until) range.until = new Date()
+ if (isSameDay(range.until, range.from)) range.until = new Date()
if (selectedRadio === RANGE) {
fetchLogs({
@@ -203,7 +204,7 @@ const LogsDownloaderPopover = ({
const createLogsFile = (logs, range) => {
const formatDateFile = date => {
- return formatDate(date, timezone, 'YYYY-MM-DD_HH-mm')
+ return formatDate(date, timezone, 'yyyy-MM-dd_HH-mm')
}
const blob = new window.Blob([logs], {
@@ -277,7 +278,7 @@ const LogsDownloaderPopover = ({
)}
diff --git a/new-lamassu-admin/src/components/date-range-picker/Calendar.js b/new-lamassu-admin/src/components/date-range-picker/Calendar.js
index f93df5dd..c6151d73 100644
--- a/new-lamassu-admin/src/components/date-range-picker/Calendar.js
+++ b/new-lamassu-admin/src/components/date-range-picker/Calendar.js
@@ -1,5 +1,18 @@
import { makeStyles } from '@material-ui/core/styles'
-import moment from 'moment'
+import {
+ add,
+ differenceInMonths,
+ format,
+ getDay,
+ getDaysInMonth,
+ isAfter,
+ isSameDay,
+ isSameMonth,
+ lastDayOfMonth,
+ startOfMonth,
+ startOfWeek,
+ sub
+} from 'date-fns/fp'
import * as R from 'ramda'
import React, { useState } from 'react'
@@ -72,49 +85,36 @@ const styles = {
const useStyles = makeStyles(styles)
const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
- const [currentDisplayedMonth, setCurrentDisplayedMonth] = useState(moment())
+ const [currentDisplayedMonth, setCurrentDisplayedMonth] = useState(new Date())
const classes = useStyles()
- const weekdays = moment.weekdaysMin().map(day => day.slice(0, 1))
- const monthLength = month =>
- Number.parseInt(
- moment(month)
- .endOf('month')
- .format('D')
- )
+ const weekdays = Array.from(Array(7)).map((_, i) =>
+ format('EEEEE', add({ days: i }, startOfWeek(new Date())))
+ )
+
+ const monthLength = month => getDaysInMonth(month)
const monthdays = month => {
- const lastMonth = moment(month).subtract(1, 'month')
- const lastMonthRange = R.range(
- 0,
- moment(month)
- .startOf('month')
- .weekday()
- ).reverse()
+ const lastMonth = sub({ months: 1 }, month)
+ const lastMonthRange = R.range(0, getDay(startOfMonth(month))).reverse()
const lastMonthDays = R.map(i =>
- moment(lastMonth)
- .endOf('month')
- .subtract(i, 'days')
+ sub({ days: i }, lastDayOfMonth(lastMonth))
)(lastMonthRange)
const thisMonthRange = R.range(0, monthLength(month))
- const thisMonthDays = R.map(i =>
- moment(month)
- .startOf('month')
- .add(i, 'days')
- )(thisMonthRange)
+ const thisMonthDays = R.map(i => add({ days: i }, startOfMonth(month)))(
+ thisMonthRange
+ )
- const nextMonth = moment(month).add(1, 'month')
+ const nextMonth = add({ months: 1 }, month)
const nextMonthRange = R.range(
0,
42 - lastMonthDays.length - thisMonthDays.length
)
- const nextMonthDays = R.map(i =>
- moment(nextMonth)
- .startOf('month')
- .add(i, 'days')
- )(nextMonthRange)
+ const nextMonthDays = R.map(i => add({ days: i }, startOfMonth(nextMonth)))(
+ nextMonthRange
+ )
return R.concat(R.concat(lastMonthDays, thisMonthDays), nextMonthDays)
}
@@ -122,22 +122,24 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
const getRow = (month, row) => monthdays(month).slice(row * 7 - 7, row * 7)
const handleNavPrev = currentMonth => {
- const prevMonth = moment(currentMonth).subtract(1, 'month')
+ const prevMonth = sub({ months: 1 }, currentMonth)
if (!minDate) setCurrentDisplayedMonth(prevMonth)
else {
setCurrentDisplayedMonth(
- prevMonth.isSameOrAfter(minDate, 'month')
+ isSameMonth(minDate, prevMonth) ||
+ differenceInMonths(minDate, prevMonth) > 0
? prevMonth
: currentDisplayedMonth
)
}
}
const handleNavNext = currentMonth => {
- const nextMonth = moment(currentMonth).add(1, 'month')
+ const nextMonth = add({ months: 1 }, currentMonth)
if (!maxDate) setCurrentDisplayedMonth(nextMonth)
else {
setCurrentDisplayedMonth(
- nextMonth.isSameOrBefore(maxDate, 'month')
+ isSameMonth(maxDate, nextMonth) ||
+ differenceInMonths(nextMonth, maxDate) > 0
? nextMonth
: currentDisplayedMonth
)
@@ -153,9 +155,10 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
- {`${currentDisplayedMonth.format(
- 'MMMM'
- )} ${currentDisplayedMonth.format('YYYY')}`}
+ {`${format('MMMM', currentDisplayedMonth)} ${format(
+ 'yyyy',
+ currentDisplayedMonth
+ )}`}