/* * 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;