tweb/src/helpers/date.ts

508 lines
15 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { MOUNT_CLASS_TO } from "../config/debug";
import I18n, { i18n } from "../lib/langPack";
import tsNow from './tsNow';
export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
export const ONE_DAY = 86400;
// https://stackoverflow.com/a/6117889
export const getWeekNumber = (date: Date) => {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d.getTime() - yearStart.getTime()) / ONE_DAY) + 1) / 7);
};
export function formatDateAccordingToTodayNew(time: Date) {
const today = new Date();
const now = today.getTime() / 1000 | 0;
const timestamp = time.getTime() / 1000 | 0;
const options: Intl.DateTimeFormatOptions = {};
if((now - timestamp) < ONE_DAY && today.getDate() === time.getDate()) { // if the same day
options.hour = options.minute = '2-digit';
} else if(today.getFullYear() !== time.getFullYear()) { // different year
options.year = options.day = 'numeric';
options.month = '2-digit';
} else if((now - timestamp) < (ONE_DAY * 7) && getWeekNumber(today) === getWeekNumber(time)) { // current week
options.weekday = 'short';
} else { // same year
options.month = 'short';
options.day = 'numeric';
}
return new I18n.IntlDateElement({
date: time,
options
}).element;
}
export function formatFullSentTimeRaw(timestamp: number, options: {
capitalize?: boolean
} = {}) {
const date = new Date();
const time = new Date(timestamp * 1000);
const now = date.getTime() / 1000;
const timeEl = formatTime(time);
let dateEl: Node | string;
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
dateEl = i18n(options.capitalize ? 'Date.Today' : 'Peer.Status.Today');
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
dateEl = i18n(options.capitalize ? 'Yesterday' : 'Peer.Status.Yesterday');
if(options.capitalize) {
(dateEl as HTMLElement).style.textTransform = 'capitalize';
}
} else if(date.getFullYear() !== time.getFullYear()) { // different year
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric',
year: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate() + ', ' + time.getFullYear();
} else {
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
}
return {dateEl, timeEl};
}
export function formatFullSentTime(timestamp: number) {
const {dateEl, timeEl} = formatFullSentTimeRaw(timestamp, {
capitalize: true
});
const fragment = document.createDocumentFragment();
fragment.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
return fragment;
}
export function formatTime(date: Date) {
return new I18n.IntlDateElement({
date,
options: {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}
}).element;
}
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.formatDateAccordingToTodayNew = formatDateAccordingToTodayNew);
export const getFullDate = (date: Date, options: Partial<{
noTime: true,
noSeconds: true,
monthAsNumber: true,
leadingZero: true
}> = {}) => {
const joiner = options.monthAsNumber ? '.' : ' ';
const time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2) + (options.noSeconds ? '' : ':' + ('0' + date.getSeconds()).slice(-2));
return (options.leadingZero ? ('0' + date.getDate()).slice(-2) : date.getDate()) +
joiner + (options.monthAsNumber ? ('0' + (date.getMonth() + 1)).slice(-2) : months[date.getMonth()]) +
joiner + date.getFullYear() +
(options.noTime ? '' : ', ' + time);
};
export {tsNow};
// https://github.com/DrKLO/Telegram/blob/d52b2c921abd3c1e8d6368858313ad0cb0468c07/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java
const minYear = 2013;
const yearPattern = new RegExp("20[0-9]{1,2}");
const monthYearOrDayPattern = new RegExp("(\\w{3,}) ([0-9]{0,4})", 'i');
const yearOrDayAndMonthPattern = new RegExp("([0-9]{0,4}) (\\w{2,})", 'i');
const shortDate = new RegExp("^([0-9]{1,4})(\\.| |/|\\-)([0-9]{1,4})$", 'i');
const longDate = new RegExp("^([0-9]{1,2})(\\.| |/|\\-)([0-9]{1,2})(\\.| |/|\\-)([0-9]{1,4})$", 'i');
const numberOfDaysEachMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
export type DateData = {
title: string,
minDate: number,
maxDate: number,
};
export function fillTipDates(query: string, dates: DateData[]) {
const q = query.trim().toLowerCase();
if(q.length < 3) {
return;
}
if("today".indexOf(q) === 0) {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth();
const day = date.getDate();
date.setFullYear(year, month, day);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(year, month, day + 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: 'Today',
minDate,
maxDate
});
return;
}
if("yesterday".indexOf(q) === 0) {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth();
const day = date.getDate();
date.setFullYear(year, month, day);
date.setHours(0, 0, 0);
const minDate = date.getTime() - 86400000;
date.setFullYear(year, month, day + 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 86400001;
dates.push({
title: 'Yesterday',
minDate,
maxDate
});
return;
}
const dayOfWeek = getDayOfWeek(q);
if(dayOfWeek >= 0) {
const date = new Date();
const now = date.getTime();
const currentDay = date.getDay();
const distance = dayOfWeek - currentDay;
date.setDate(date.getDate() + distance);
if(date.getTime() > now) {
date.setTime(date.getTime() - 604800000);
}
const year = date.getFullYear()
const month = date.getMonth();
const day = date.getDate();
date.setFullYear(year, month, day);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(year, month, day + 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: formatWeekLong(minDate),
minDate,
maxDate
});
return;
}
let matches: any[];
if((matches = shortDate.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[3];
const k = parseInt(g1);
const k1 = parseInt(g2);
if(k > 0 && k <= 31) {
if(k1 >= minYear && k <= 12) {
const selectedYear = k1;
const month = k - 1;
createForMonthYear(dates, month, selectedYear);
return;
} else if (k1 <= 12) {
const day = k - 1;
const month = k1 - 1;
createForDayMonth(dates, day, month);
}
} else if (k >= minYear && k1 <= 12) {
const selectedYear = k;
const month = k1 - 1;
createForMonthYear(dates, month, selectedYear);
}
return;
}
if((matches = longDate.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[3];
const g3 = matches[5];
if(!matches[2] === matches[4]) {
return;
}
const day = parseInt(g1);
const month = parseInt(g2) - 1;
let year = parseInt(g3);
if(year >= 10 && year <= 99) {
year += 2000;
}
const currentYear = new Date().getFullYear();
if(validDateForMonth(day - 1, month) && year >= minYear && year <= currentYear) {
const date = new Date();
date.setFullYear(year, month, day);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(year, month, day + 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: formatterYearMax(minDate),
minDate,
maxDate
});
return;
}
return;
}
if((matches = yearPattern.exec(q)) !== null) {
let selectedYear = +q;
const currentYear = new Date().getFullYear();
if(selectedYear < minYear) {
selectedYear = minYear;
for(let i = currentYear; i >= selectedYear; i--) {
const date = new Date();
date.setFullYear(i, 0, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(i + 1, 0, 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: '' + i,
minDate,
maxDate
});
}
} else if(selectedYear <= currentYear) {
const date = new Date();
date.setFullYear(selectedYear, 0, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
date.setFullYear(selectedYear + 1, 0, 1);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
dates.push({
title: '' + selectedYear,
minDate,
maxDate
});
}
return;
}
if((matches = monthYearOrDayPattern.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[2];
const month = getMonth(g1);
if(month >= 0) {
const k = +g2;
if(k > 0 && k <= 31) {
const day = k - 1;
createForDayMonth(dates, day, month);
return;
} else if(k >= minYear) {
const selectedYear = k;
createForMonthYear(dates, month, selectedYear);
return;
}
}
}
if((matches = yearOrDayAndMonthPattern.exec(q)) !== null) {
const g1 = matches[1];
const g2 = matches[2];
const month = getMonth(g2);
if(month >= 0) {
const k = +g1;
if(k > 0 && k <= 31) {
const day = k - 1;
createForDayMonth(dates, day, month);
return;
} else if (k >= minYear) {
const selectedYear = k;
createForMonthYear(dates, month, selectedYear);
}
}
}
}
function createForMonthYear(dates: DateData[], month: number, selectedYear: number) {
const currentYear = new Date().getFullYear();
const today = Date.now();
if(selectedYear >= minYear && selectedYear <= currentYear) {
const date = new Date();
date.setFullYear(selectedYear, month, 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
if(minDate > today) {
return;
}
date.setMonth(date.getMonth() + 1);
const maxDate = date.getTime() - 1;
dates.push({
title: formatterMonthYear(minDate),
minDate,
maxDate
});
}
}
function createForDayMonth(dates: DateData[], day: number, month: number) {
if(validDateForMonth(day, month)) {
const currentYear = new Date().getFullYear();
const today = Date.now();
for(let i = currentYear; i >= minYear; i--) {
if(month === 1 && day === 28 && !isLeapYear(i)) {
continue;
}
const date = new Date();
date.setFullYear(i, month, day + 1);
date.setHours(0, 0, 0);
const minDate = date.getTime();
if(minDate > today) {
continue;
}
date.setFullYear(i, month, day + 2);
date.setHours(0, 0, 0);
const maxDate = date.getTime() - 1;
if(i === currentYear) {
dates.push({
title: formatterDayMonth(minDate),
minDate,
maxDate
});
} else {
dates.push({
title: formatterYearMax(minDate),
minDate,
maxDate
});
}
}
}
}
function formatterMonthYear(timestamp: number) {
const date = new Date(timestamp);
return months[date.getMonth()].slice(0, 3) + ' ' + date.getFullYear();
}
function formatterDayMonth(timestamp: number) {
const date = new Date(timestamp);
return months[date.getMonth()].slice(0, 3) + ' ' + date.getDate();
}
function formatterYearMax(timestamp: number) {
const date = new Date(timestamp);
return ('0' + date.getDate()).slice(-2) + '.' + ('0' + (date.getMonth() + 1)).slice(-2) + '.' + date.getFullYear();
}
function formatWeekLong(timestamp: number) {
const date = new Date(timestamp);
return days[date.getDay()];
}
function validDateForMonth(day: number, month: number) {
if(month >= 0 && month < 12) {
if(day >= 0 && day < numberOfDaysEachMonth[month]) {
return true;
}
}
return false;
}
function isLeapYear(year: number) {
return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
}
function getMonth(q: string) {
/* String[] months = new String[]{
LocaleController.getString("January", R.string.January).toLowerCase(),
LocaleController.getString("February", R.string.February).toLowerCase(),
LocaleController.getString("March", R.string.March).toLowerCase(),
LocaleController.getString("April", R.string.April).toLowerCase(),
LocaleController.getString("May", R.string.May).toLowerCase(),
LocaleController.getString("June", R.string.June).toLowerCase(),
LocaleController.getString("July", R.string.July).toLowerCase(),
LocaleController.getString("August", R.string.August).toLowerCase(),
LocaleController.getString("September", R.string.September).toLowerCase(),
LocaleController.getString("October", R.string.October).toLowerCase(),
LocaleController.getString("November", R.string.November).toLowerCase(),
LocaleController.getString("December", R.string.December).toLowerCase()
}; */
/* String[] monthsEng = new String[12];
Calendar c = Calendar.getInstance();
for (int i = 1; i <= 12; i++) {
c.set(0, 0, 0, 0, 0, 0);
c.set(Calendar.MONTH, i);
monthsEng[i - 1] = c.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH).toLowerCase();
} */
q = q.toLowerCase();
for(let i = 0; i < 12; i++) {
const month = months[i].toLowerCase();
if(month.indexOf(q) === 0) {
return i;
}
}
return -1;
}
function getDayOfWeek(q: string) {
const c = new Date();
if(q.length <= 3) {
return -1;
}
for(let i = 0; i < 7; i++) {
c.setDate(c.getDate() + 1);
if(formatWeekLong(c.getTime()).toLowerCase().indexOf(q) === 0) {
return c.getDay();
}
}
return -1;
}
MOUNT_CLASS_TO.fillTipDates = fillTipDates;