discourse/plugins/discourse-local-dates/assets/javascripts/lib/local-date-builder.js.es6
Joffrey JAFFEUX 4f7a9802e7
FIX: prefer timezones over timezone for previews (#10506)
eg:

timezone="America/Detroit"
timezones="US/Eastern"

Before this commit we would show America/Detroit in previews and not US/Eastern, given US/Eastern and America/Detroit are equivalent.

After this commit, we will display the date with America/Detroit but show US/Eastern in the previews.
2020-08-21 16:31:50 +02:00

248 lines
6.6 KiB
JavaScript

import I18n from "I18n";
import DateWithZoneHelper from "./date-with-zone-helper";
const TIME_FORMAT = "LLL";
const DATE_FORMAT = "LL";
const RANGE_SEPARATOR = "→";
export default class LocalDateBuilder {
constructor(params = {}, localTimezone) {
this.time = params.time;
this.date = params.date;
this.recurring = params.recurring;
this.timezones = Array.from(
new Set((params.timezones || []).filter(Boolean))
);
this.timezone = params.timezone || "UTC";
this.calendar =
typeof params.calendar === "undefined" ? true : params.calendar;
this.displayedTimezone = params.displayedTimezone;
this.format = params.format || (this.time ? TIME_FORMAT : DATE_FORMAT);
this.countdown = params.countdown;
this.localTimezone = localTimezone;
}
build() {
const [year, month, day] = this.date.split("-").map(x => parseInt(x, 10));
const [hour, minute, second] = (this.time || "")
.split(":")
.map(x => (x ? parseInt(x, 10) : undefined));
let displayedTimezone;
if (this.time) {
displayedTimezone = this.displayedTimezone || this.localTimezone;
} else {
displayedTimezone =
this.displayedTimezone || this.timezone || this.localTimezone;
}
let localDate = new DateWithZoneHelper({
year,
month: month ? month - 1 : null,
day,
hour,
minute,
second,
timezone: this.timezone,
localTimezone: this.localTimezone
});
if (this.recurring) {
const [count, type] = this.recurring.split(".");
const repetitionsForType = localDate.repetitionsBetweenDates(
this.recurring,
moment.tz(this.localTimezone)
);
localDate = localDate.add(repetitionsForType + parseInt(count, 10), type);
}
const previews = this._generatePreviews(localDate, displayedTimezone);
return {
pastEvent:
!this.recurring &&
moment.tz(this.localTimezone).isAfter(localDate.datetime),
formated: this._applyFormatting(localDate, displayedTimezone),
previews,
textPreview: this._generateTextPreviews(previews)
};
}
_generateTextPreviews(previews) {
return previews
.map(preview => {
const formatedZone = this._zoneWithoutPrefix(preview.timezone);
return `${formatedZone} ${preview.formated}`;
})
.join(", ");
}
_generatePreviews(localDate, displayedTimezone) {
const previewedTimezones = [];
const timezones = this.timezones.filter(
timezone => !this._isEqualZones(timezone, this.localTimezone)
);
previewedTimezones.push({
timezone: this._zoneWithoutPrefix(this.localTimezone),
current: true,
formated: this._createDateTimeRange(
DateWithZoneHelper.fromDatetime(
localDate.datetime,
localDate.timezone,
this.localTimezone
),
this.time
)
});
if (
this.timezone &&
displayedTimezone === this.localTimezone &&
this.timezone !== displayedTimezone &&
!this._isEqualZones(displayedTimezone, this.timezone) &&
!this.timezones.any(t => this._isEqualZones(t, this.timezone))
) {
timezones.unshift(this.timezone);
}
timezones.forEach(timezone => {
if (this._isEqualZones(timezone, displayedTimezone)) {
return;
}
if (this._isEqualZones(timezone, this.localTimezone)) {
timezone = this.localTimezone;
}
previewedTimezones.push({
timezone: this._zoneWithoutPrefix(timezone),
formated: this._createDateTimeRange(
DateWithZoneHelper.fromDatetime(
localDate.datetime,
localDate.timezone,
timezone
),
this.time
)
});
});
return previewedTimezones.uniqBy("timezone");
}
_isEqualZones(timezoneA, timezoneB) {
if ((timezoneA || timezoneB) && (!timezoneA || !timezoneB)) {
return false;
}
if (timezoneA.includes(timezoneB) || timezoneB.includes(timezoneA)) {
return true;
}
return (
moment.tz(timezoneA).utcOffset() === moment.tz(timezoneB).utcOffset()
);
}
_createDateTimeRange(startRange, time) {
// if a time has been given we do not attempt to automatically create a range
// instead we show only one date with a format showing the time
if (time) {
return startRange.format(TIME_FORMAT);
} else {
const endRange = startRange.add(24, "hours");
return [
startRange.format("LLLL"),
RANGE_SEPARATOR,
endRange.format("LLLL")
].join(" ");
}
}
_applyFormatting(localDate, displayedTimezone) {
if (this.countdown) {
const diffTime = moment.tz(this.localTimezone).diff(localDate.datetime);
if (diffTime < 0) {
return moment.duration(diffTime).humanize();
} else {
return I18n.t("discourse_local_dates.relative_dates.countdown.passed");
}
}
const sameTimezone = this._isEqualZones(
displayedTimezone,
this.localTimezone
);
if (this.calendar) {
const inCalendarRange = moment
.tz(this.localTimezone)
.isBetween(
localDate.subtract(2, "day").datetime,
localDate.add(1, "day").datetime.endOf("day")
);
if (inCalendarRange && sameTimezone) {
return localDate
.datetimeWithZone(this.localTimezone)
.calendar(
moment.tz(localDate.timezone),
this._calendarFormats(this.time ? this.time : null)
);
}
}
if (!sameTimezone) {
return this._formatWithZone(localDate, displayedTimezone, this.format);
}
return localDate.format(this.format);
}
_calendarFormats(time) {
return {
sameDay: this._translateCalendarKey(time, "today"),
nextDay: this._translateCalendarKey(time, "tomorrow"),
lastDay: this._translateCalendarKey(time, "yesterday"),
sameElse: "L"
};
}
_translateCalendarKey(time, key) {
const translated = I18n.t(`discourse_local_dates.relative_dates.${key}`, {
time: "LT"
});
if (time) {
return translated
.split("LT")
.map(w => `[${w}]`)
.join("LT");
} else {
return `[${translated.replace(" LT", "")}]`;
}
}
_formatTimezone(timezone) {
return timezone
.replace("_", " ")
.replace("Etc/", "")
.split("/");
}
_zoneWithoutPrefix(timezone) {
const [part1, part2] = this._formatTimezone(timezone);
return part2 || part1;
}
_formatWithZone(localDate, displayedTimezone, format) {
let formated = localDate.datetimeWithZone(displayedTimezone).format(format);
return `${formated} (${this._zoneWithoutPrefix(displayedTimezone)})`;
}
}