fix: исправлен расчет даты переноса задач с периодами повторения
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 54s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 54s
- Добавлена поддержка сокращенных форм единиц времени (mons, min, hrs, wks, yrs и т.д.) - Исправлена обработка недель, которые PostgreSQL возвращает как дни (7 days вместо 1 week) - Добавлено приведение repetition_period к тексту при чтении из БД - Обновлена версия до 3.8.6
This commit is contained in:
@@ -374,7 +374,8 @@ func calculateNextShowAtFromRepetitionDate(repetitionDate string, fromDate time.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculateNextShowAtFromRepetitionPeriod calculates the next show date by adding repetition_period to fromDate
|
// calculateNextShowAtFromRepetitionPeriod calculates the next show date by adding repetition_period to fromDate
|
||||||
// Format: PostgreSQL INTERVAL string (e.g., "1 day", "2 weeks", "3 months")
|
// Format: PostgreSQL INTERVAL string (e.g., "1 day", "2 weeks", "3 months" or "3 mons")
|
||||||
|
// Note: PostgreSQL may return weeks as days (e.g., "7 days" instead of "1 week")
|
||||||
func calculateNextShowAtFromRepetitionPeriod(repetitionPeriod string, fromDate time.Time) *time.Time {
|
func calculateNextShowAtFromRepetitionPeriod(repetitionPeriod string, fromDate time.Time) *time.Time {
|
||||||
if repetitionPeriod == "" {
|
if repetitionPeriod == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -382,33 +383,45 @@ func calculateNextShowAtFromRepetitionPeriod(repetitionPeriod string, fromDate t
|
|||||||
|
|
||||||
parts := strings.Fields(strings.TrimSpace(repetitionPeriod))
|
parts := strings.Fields(strings.TrimSpace(repetitionPeriod))
|
||||||
if len(parts) < 2 {
|
if len(parts) < 2 {
|
||||||
|
log.Printf("calculateNextShowAtFromRepetitionPeriod: invalid format, parts=%v", parts)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := strconv.Atoi(parts[0])
|
value, err := strconv.Atoi(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("calculateNextShowAtFromRepetitionPeriod: failed to parse value '%s': %v", parts[0], err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unit := strings.ToLower(parts[1])
|
unit := strings.ToLower(parts[1])
|
||||||
|
log.Printf("calculateNextShowAtFromRepetitionPeriod: value=%d, unit='%s'", value, unit)
|
||||||
|
|
||||||
// Start from fromDate at midnight
|
// Start from fromDate at midnight
|
||||||
nextDate := time.Date(fromDate.Year(), fromDate.Month(), fromDate.Day(), 0, 0, 0, 0, fromDate.Location())
|
nextDate := time.Date(fromDate.Year(), fromDate.Month(), fromDate.Day(), 0, 0, 0, 0, fromDate.Location())
|
||||||
|
|
||||||
switch unit {
|
switch unit {
|
||||||
case "minute", "minutes":
|
case "minute", "minutes", "mins", "min":
|
||||||
nextDate = nextDate.Add(time.Duration(value) * time.Minute)
|
nextDate = nextDate.Add(time.Duration(value) * time.Minute)
|
||||||
case "hour", "hours":
|
case "hour", "hours", "hrs", "hr":
|
||||||
nextDate = nextDate.Add(time.Duration(value) * time.Hour)
|
nextDate = nextDate.Add(time.Duration(value) * time.Hour)
|
||||||
case "day", "days":
|
case "day", "days":
|
||||||
|
// PostgreSQL может возвращать недели как дни (например, "7 days" вместо "1 week")
|
||||||
|
// Если количество дней кратно 7, обрабатываем как недели
|
||||||
|
if value%7 == 0 && value >= 7 {
|
||||||
|
weeks := value / 7
|
||||||
|
nextDate = nextDate.AddDate(0, 0, weeks*7)
|
||||||
|
} else {
|
||||||
nextDate = nextDate.AddDate(0, 0, value)
|
nextDate = nextDate.AddDate(0, 0, value)
|
||||||
case "week", "weeks":
|
}
|
||||||
|
case "week", "weeks", "wks", "wk":
|
||||||
nextDate = nextDate.AddDate(0, 0, value*7)
|
nextDate = nextDate.AddDate(0, 0, value*7)
|
||||||
case "month", "months":
|
case "month", "months", "mons", "mon":
|
||||||
nextDate = nextDate.AddDate(0, value, 0)
|
nextDate = nextDate.AddDate(0, value, 0)
|
||||||
case "year", "years":
|
log.Printf("calculateNextShowAtFromRepetitionPeriod: added %d months, result=%v", value, nextDate)
|
||||||
|
case "year", "years", "yrs", "yr":
|
||||||
nextDate = nextDate.AddDate(value, 0, 0)
|
nextDate = nextDate.AddDate(value, 0, 0)
|
||||||
default:
|
default:
|
||||||
|
log.Printf("calculateNextShowAtFromRepetitionPeriod: unknown unit '%s'", unit)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7503,7 +7516,7 @@ func (a *App) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var ownerID int
|
var ownerID int
|
||||||
|
|
||||||
err = a.DB.QueryRow(`
|
err = a.DB.QueryRow(`
|
||||||
SELECT id, name, reward_message, progression_base, repetition_period, repetition_date, user_id
|
SELECT id, name, reward_message, progression_base, repetition_period::text, repetition_date, user_id
|
||||||
FROM tasks
|
FROM tasks
|
||||||
WHERE id = $1 AND deleted = FALSE
|
WHERE id = $1 AND deleted = FALSE
|
||||||
`, taskID).Scan(&task.ID, &task.Name, &rewardMessage, &progressionBase, &repetitionPeriod, &repetitionDate, &ownerID)
|
`, taskID).Scan(&task.ID, &task.Name, &rewardMessage, &progressionBase, &repetitionPeriod, &repetitionDate, &ownerID)
|
||||||
@@ -7797,14 +7810,17 @@ func (a *App) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Обычный период: обновляем счетчик и last_completed_at, вычисляем next_show_at
|
// Обычный период: обновляем счетчик и last_completed_at, вычисляем next_show_at
|
||||||
// next_show_at = last_completed_at + repetition_period
|
// next_show_at = last_completed_at + repetition_period
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
log.Printf("Calculating next_show_at for task %d: repetition_period='%s', fromDate=%v", taskID, repetitionPeriod.String, now)
|
||||||
nextShowAt := calculateNextShowAtFromRepetitionPeriod(repetitionPeriod.String, now)
|
nextShowAt := calculateNextShowAtFromRepetitionPeriod(repetitionPeriod.String, now)
|
||||||
if nextShowAt != nil {
|
if nextShowAt != nil {
|
||||||
|
log.Printf("Calculated next_show_at for task %d: %v", taskID, *nextShowAt)
|
||||||
_, err = a.DB.Exec(`
|
_, err = a.DB.Exec(`
|
||||||
UPDATE tasks
|
UPDATE tasks
|
||||||
SET completed = completed + 1, last_completed_at = NOW(), next_show_at = $2
|
SET completed = completed + 1, last_completed_at = NOW(), next_show_at = $2
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`, taskID, nextShowAt)
|
`, taskID, nextShowAt)
|
||||||
} else {
|
} else {
|
||||||
|
log.Printf("Failed to calculate next_show_at for task %d: repetition_period='%s' returned nil", taskID, repetitionPeriod.String)
|
||||||
// Если не удалось вычислить дату, обновляем как обычно
|
// Если не удалось вычислить дату, обновляем как обычно
|
||||||
_, err = a.DB.Exec(`
|
_, err = a.DB.Exec(`
|
||||||
UPDATE tasks
|
UPDATE tasks
|
||||||
@@ -7896,7 +7912,7 @@ func (a *App) completeAndDeleteTaskHandler(w http.ResponseWriter, r *http.Reques
|
|||||||
var ownerID int
|
var ownerID int
|
||||||
|
|
||||||
err = a.DB.QueryRow(`
|
err = a.DB.QueryRow(`
|
||||||
SELECT id, name, reward_message, progression_base, repetition_period, repetition_date, user_id
|
SELECT id, name, reward_message, progression_base, repetition_period::text, repetition_date, user_id
|
||||||
FROM tasks
|
FROM tasks
|
||||||
WHERE id = $1 AND deleted = FALSE
|
WHERE id = $1 AND deleted = FALSE
|
||||||
`, taskID).Scan(&task.ID, &task.Name, &rewardMessage, &progressionBase, &repetitionPeriod, &repetitionDate, &ownerID)
|
`, taskID).Scan(&task.ID, &task.Name, &rewardMessage, &progressionBase, &repetitionPeriod, &repetitionDate, &ownerID)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "3.8.5",
|
"version": "3.8.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ const calculateNextDateFromRepetitionDate = (repetitionDateStr) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Функция для вычисления следующей даты по repetition_period
|
// Функция для вычисления следующей даты по repetition_period
|
||||||
|
// Поддерживает сокращенные формы единиц времени (например, "mons" для месяцев)
|
||||||
const calculateNextDateFromRepetitionPeriod = (repetitionPeriodStr) => {
|
const calculateNextDateFromRepetitionPeriod = (repetitionPeriodStr) => {
|
||||||
if (!repetitionPeriodStr) return null
|
if (!repetitionPeriodStr) return null
|
||||||
|
|
||||||
@@ -117,26 +118,43 @@ const calculateNextDateFromRepetitionPeriod = (repetitionPeriodStr) => {
|
|||||||
switch (unit) {
|
switch (unit) {
|
||||||
case 'minute':
|
case 'minute':
|
||||||
case 'minutes':
|
case 'minutes':
|
||||||
|
case 'mins':
|
||||||
|
case 'min':
|
||||||
nextDate.setMinutes(nextDate.getMinutes() + value)
|
nextDate.setMinutes(nextDate.getMinutes() + value)
|
||||||
break
|
break
|
||||||
case 'hour':
|
case 'hour':
|
||||||
case 'hours':
|
case 'hours':
|
||||||
|
case 'hrs':
|
||||||
|
case 'hr':
|
||||||
nextDate.setHours(nextDate.getHours() + value)
|
nextDate.setHours(nextDate.getHours() + value)
|
||||||
break
|
break
|
||||||
case 'day':
|
case 'day':
|
||||||
case 'days':
|
case 'days':
|
||||||
|
// PostgreSQL может возвращать недели как дни (например, "7 days" вместо "1 week")
|
||||||
|
// Если количество дней кратно 7, обрабатываем как недели
|
||||||
|
if (value % 7 === 0 && value >= 7) {
|
||||||
|
const weeks = value / 7
|
||||||
|
nextDate.setDate(nextDate.getDate() + weeks * 7)
|
||||||
|
} else {
|
||||||
nextDate.setDate(nextDate.getDate() + value)
|
nextDate.setDate(nextDate.getDate() + value)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 'week':
|
case 'week':
|
||||||
case 'weeks':
|
case 'weeks':
|
||||||
|
case 'wks':
|
||||||
|
case 'wk':
|
||||||
nextDate.setDate(nextDate.getDate() + value * 7)
|
nextDate.setDate(nextDate.getDate() + value * 7)
|
||||||
break
|
break
|
||||||
case 'month':
|
case 'month':
|
||||||
case 'months':
|
case 'months':
|
||||||
|
case 'mons':
|
||||||
|
case 'mon':
|
||||||
nextDate.setMonth(nextDate.getMonth() + value)
|
nextDate.setMonth(nextDate.getMonth() + value)
|
||||||
break
|
break
|
||||||
case 'year':
|
case 'year':
|
||||||
case 'years':
|
case 'years':
|
||||||
|
case 'yrs':
|
||||||
|
case 'yr':
|
||||||
nextDate.setFullYear(nextDate.getFullYear() + value)
|
nextDate.setFullYear(nextDate.getFullYear() + value)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -139,26 +139,43 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
|||||||
switch (unit) {
|
switch (unit) {
|
||||||
case 'minute':
|
case 'minute':
|
||||||
case 'minutes':
|
case 'minutes':
|
||||||
|
case 'mins':
|
||||||
|
case 'min':
|
||||||
nextDate.setMinutes(nextDate.getMinutes() + value)
|
nextDate.setMinutes(nextDate.getMinutes() + value)
|
||||||
break
|
break
|
||||||
case 'hour':
|
case 'hour':
|
||||||
case 'hours':
|
case 'hours':
|
||||||
|
case 'hrs':
|
||||||
|
case 'hr':
|
||||||
nextDate.setHours(nextDate.getHours() + value)
|
nextDate.setHours(nextDate.getHours() + value)
|
||||||
break
|
break
|
||||||
case 'day':
|
case 'day':
|
||||||
case 'days':
|
case 'days':
|
||||||
|
// PostgreSQL может возвращать недели как дни (например, "7 days" вместо "1 week")
|
||||||
|
// Если количество дней кратно 7, обрабатываем как недели
|
||||||
|
if (value % 7 === 0 && value >= 7) {
|
||||||
|
const weeks = value / 7
|
||||||
|
nextDate.setDate(nextDate.getDate() + weeks * 7)
|
||||||
|
} else {
|
||||||
nextDate.setDate(nextDate.getDate() + value)
|
nextDate.setDate(nextDate.getDate() + value)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case 'week':
|
case 'week':
|
||||||
case 'weeks':
|
case 'weeks':
|
||||||
|
case 'wks':
|
||||||
|
case 'wk':
|
||||||
nextDate.setDate(nextDate.getDate() + value * 7)
|
nextDate.setDate(nextDate.getDate() + value * 7)
|
||||||
break
|
break
|
||||||
case 'month':
|
case 'month':
|
||||||
case 'months':
|
case 'months':
|
||||||
|
case 'mons':
|
||||||
|
case 'mon':
|
||||||
nextDate.setMonth(nextDate.getMonth() + value)
|
nextDate.setMonth(nextDate.getMonth() + value)
|
||||||
break
|
break
|
||||||
case 'year':
|
case 'year':
|
||||||
case 'years':
|
case 'years':
|
||||||
|
case 'yrs':
|
||||||
|
case 'yr':
|
||||||
nextDate.setFullYear(nextDate.getFullYear() + value)
|
nextDate.setFullYear(nextDate.getFullYear() + value)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user