-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DateUtils refactor, remove moment from the code, timezone fixes. #24446
Merged
mountiny
merged 11 commits into
Expensify:main
from
waterim:feat-19810-Replace-the-moment-library
Aug 21, 2023
Merged
Changes from 1 commit
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
2661c3e
refactored DateUtils, timezone fixes, additional tests, removed all u…
waterim 136fb80
fixes
waterim 3be8a26
utc fux
waterim c4cb7cf
Merge remote-tracking branch 'upstream/main' into feat-19810-Replace-…
waterim c901452
Merge remote-tracking branch 'upstream/main' into feat-19810-Replace-…
waterim 0437df0
update timezones to Intl from moment
waterim d5eb472
getStatusUntilDate rewrite to date-fns
waterim 53467ef
test improve
waterim a1d3de3
local timezone change
waterim bd65aa2
add a comment with a link
waterim 9305d75
timezone link change
waterim File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,7 @@ | ||
import moment from 'moment-timezone'; | ||
import lodashGet from 'lodash/get'; | ||
|
||
// IMPORTANT: load any locales (other than english) that might be passed to moment.locale() | ||
import 'moment/locale/es'; | ||
import {zonedTimeToUtc, utcToZonedTime, formatInTimeZone} from 'date-fns-tz'; | ||
import {es, enGB} from 'date-fns/locale'; | ||
import {formatDistanceToNow, subMinutes, isBefore, subMilliseconds, isToday, isTomorrow, isYesterday, startOfWeek, endOfWeek, format, setDefaultOptions} from 'date-fns'; | ||
|
||
import _ from 'underscore'; | ||
import Onyx from 'react-native-onyx'; | ||
|
@@ -32,6 +31,24 @@ Onyx.connect({ | |
}, | ||
}); | ||
|
||
/** | ||
* Gets the locale string and setting default locale for date-fns | ||
* | ||
* @param {String} localeString | ||
*/ | ||
function setLocale(localeString) { | ||
switch (localeString) { | ||
case 'en': | ||
waterim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setDefaultOptions({locale: enGB}); | ||
break; | ||
case 'es': | ||
waterim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setDefaultOptions({locale: es}); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
/** | ||
* Gets the user's stored time zone NVP and returns a localized | ||
* Moment object for the given ISO-formatted datetime string | ||
|
@@ -44,13 +61,15 @@ Onyx.connect({ | |
* | ||
* @private | ||
*/ | ||
function getLocalMomentFromDatetime(locale, datetime, currentSelectedTimezone = timezone.selected) { | ||
moment.locale(locale); | ||
function getLocalDateFromDatetime(locale, datetime, currentSelectedTimezone = timezone.selected) { | ||
setLocale(locale); | ||
if (!datetime) { | ||
return moment.tz(currentSelectedTimezone); | ||
const now = new Date(); | ||
const zonedNow = utcToZonedTime(now, currentSelectedTimezone); | ||
return zonedNow; | ||
waterim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return moment.utc(datetime).tz(currentSelectedTimezone); | ||
const parsedDatetime = new Date(`${datetime} UTC`); | ||
return utcToZonedTime(parsedDatetime, currentSelectedTimezone); | ||
} | ||
|
||
/** | ||
|
@@ -70,28 +89,38 @@ function getLocalMomentFromDatetime(locale, datetime, currentSelectedTimezone = | |
* @returns {String} | ||
*/ | ||
function datetimeToCalendarTime(locale, datetime, includeTimeZone = false, currentSelectedTimezone, isLowercase = false) { | ||
const date = getLocalMomentFromDatetime(locale, datetime, currentSelectedTimezone); | ||
const date = getLocalDateFromDatetime(locale, datetime, currentSelectedTimezone); | ||
const tz = includeTimeZone ? ' [UTC]Z' : ''; | ||
|
||
let todayAt = Localize.translate(locale, 'common.todayAt'); | ||
let tomorrowAt = Localize.translate(locale, 'common.tomorrowAt'); | ||
let yesterdayAt = Localize.translate(locale, 'common.yesterdayAt'); | ||
const at = Localize.translate(locale, 'common.conjunctionAt'); | ||
|
||
const dateFormatter = 'MMM d'; | ||
const elseDateFormatter = 'MMM d, yyyy'; | ||
const timeFormatter = 'h:mm a'; | ||
waterim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const startOfCurrentWeek = startOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week | ||
const endOfCurrentWeek = endOfWeek(new Date(), {weekStartsOn: 1}); // Assuming Monday is the start of the week | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This caused regression. |
||
if (isLowercase) { | ||
todayAt = todayAt.toLowerCase(); | ||
tomorrowAt = tomorrowAt.toLowerCase(); | ||
yesterdayAt = yesterdayAt.toLowerCase(); | ||
} | ||
|
||
return moment(date).calendar({ | ||
sameDay: `[${todayAt}] LT${tz}`, | ||
nextDay: `[${tomorrowAt}] LT${tz}`, | ||
lastDay: `[${yesterdayAt}] LT${tz}`, | ||
nextWeek: `MMM D [${at}] LT${tz}`, | ||
lastWeek: `MMM D [${at}] LT${tz}`, | ||
sameElse: `MMM D, YYYY [${at}] LT${tz}`, | ||
}); | ||
if (isToday(date)) { | ||
return `${todayAt} ${format(date, timeFormatter)}${tz}`; | ||
} | ||
if (isTomorrow(date)) { | ||
return `${tomorrowAt} ${format(date, timeFormatter)}${tz}`; | ||
} | ||
if (isYesterday(date)) { | ||
return `${yesterdayAt} ${format(date, timeFormatter)}${tz}`; | ||
} | ||
if (date >= startOfCurrentWeek && date <= endOfCurrentWeek) { | ||
return `${format(date, dateFormatter)} ${at} ${format(date, timeFormatter)}${tz}`; | ||
} | ||
return `${format(date, elseDateFormatter)} ${at} ${format(date, timeFormatter)}${tz}`; | ||
} | ||
|
||
/** | ||
|
@@ -113,16 +142,66 @@ function datetimeToCalendarTime(locale, datetime, includeTimeZone = false, curre | |
* @returns {String} | ||
*/ | ||
function datetimeToRelative(locale, datetime) { | ||
const date = getLocalMomentFromDatetime(locale, datetime); | ||
const date = getLocalDateFromDatetime(locale, datetime); | ||
return formatDistanceToNow(date); | ||
} | ||
|
||
/** | ||
* Gets the zone abbreviation from the date | ||
* | ||
* e.g. | ||
* | ||
* PST | ||
* EST | ||
* GMT +07 - For GMT timezone | ||
* | ||
* @param {String} datetime | ||
* @param {String} selectedTimezone | ||
* | ||
* @returns {String} | ||
*/ | ||
function getZoneAbbreviation(datetime, selectedTimezone) { | ||
return formatInTimeZone(datetime, selectedTimezone, 'zzz'); | ||
} | ||
|
||
/** | ||
* Format date to a long date format with weekday | ||
* | ||
* @param {String} datetime | ||
* | ||
* @returns {String} Sunday, July 9, 2023 | ||
*/ | ||
function formatToLongDateWithWeekday(datetime) { | ||
return format(new Date(datetime), CONST.DATE.LONG_DATE_FORMAT_WITH_WEEKDAY); | ||
} | ||
|
||
return moment(date).fromNow(); | ||
/** | ||
* Format date to a weekday format | ||
* | ||
* @param {String} datetime | ||
* | ||
waterim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @returns {String} Sunday | ||
*/ | ||
function formatToDayOfWeek(datetime) { | ||
return format(new Date(datetime), CONST.DATE.WEEKDAY_TIME_FORMAT); | ||
} | ||
|
||
/** | ||
* Format date to a local time | ||
* | ||
* @param {String} datetime | ||
* | ||
* @returns {String} 2:30 PM | ||
*/ | ||
function formatToLocalTime(datetime) { | ||
return format(new Date(datetime), CONST.DATE.LOCAL_TIME_FORMAT); | ||
} | ||
|
||
/** | ||
* A throttled version of a function that updates the current date in Onyx store | ||
*/ | ||
const updateCurrentDate = _.throttle(() => { | ||
const currentDate = moment().format('YYYY-MM-DD'); | ||
const currentDate = format(new Date(), CONST.DATE.FNS_FORMAT_STRING); | ||
CurrentDate.setCurrentDate(currentDate); | ||
}, 1000 * 60 * 60 * 3); // 3 hours | ||
|
||
|
@@ -140,25 +219,28 @@ function startCurrentDateUpdater() { | |
* @returns {Object} | ||
*/ | ||
function getCurrentTimezone() { | ||
const currentTimezone = moment.tz.guess(true); | ||
const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
if (timezone.automatic && timezone.selected !== currentTimezone) { | ||
return {...timezone, selected: currentTimezone}; | ||
} | ||
return timezone; | ||
} | ||
|
||
// Used to throttle updates to the timezone when necessary | ||
let lastUpdatedTimezoneTime = moment(); | ||
let lastUpdatedTimezoneTime = new Date(); | ||
|
||
/** | ||
* @returns {Boolean} | ||
*/ | ||
function canUpdateTimezone() { | ||
return lastUpdatedTimezoneTime.isBefore(moment().subtract(5, 'minutes')); | ||
const currentTime = new Date(); | ||
const fiveMinutesAgo = subMinutes(currentTime, 5); | ||
// Compare the last updated time with five minutes ago | ||
return isBefore(lastUpdatedTimezoneTime, fiveMinutesAgo); | ||
} | ||
|
||
function setTimezoneUpdated() { | ||
lastUpdatedTimezoneTime = moment(); | ||
lastUpdatedTimezoneTime = new Date(); | ||
} | ||
|
||
/** | ||
|
@@ -188,7 +270,8 @@ function getDBTime(timestamp = '') { | |
* @returns {String} | ||
*/ | ||
function subtractMillisecondsFromDateTime(dateTime, milliseconds) { | ||
const newTimestamp = moment.utc(dateTime).subtract(milliseconds, 'milliseconds').valueOf(); | ||
const date = zonedTimeToUtc(dateTime, 'Etc/UTC'); | ||
const newTimestamp = subMilliseconds(date, milliseconds).valueOf(); | ||
return getDBTime(newTimestamp); | ||
} | ||
|
||
|
@@ -209,10 +292,14 @@ function getDateStringFromISOTimestamp(isoTimestamp) { | |
* @namespace DateUtils | ||
*/ | ||
const DateUtils = { | ||
formatToDayOfWeek, | ||
formatToLongDateWithWeekday, | ||
formatToLocalTime, | ||
getZoneAbbreviation, | ||
datetimeToRelative, | ||
datetimeToCalendarTime, | ||
startCurrentDateUpdater, | ||
getLocalMomentFromDatetime, | ||
getLocalDateFromDatetime, | ||
getCurrentTimezone, | ||
canUpdateTimezone, | ||
setTimezoneUpdated, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. I love the alternative!
It's 3x smaller.