오늘 할일을 끄면 원래 생성일로

2026-09-23 (KST). 아이폰 앱 day_plan_appl_app 에 반영된 수정이다. 안드로이드 day_plan_app 과 웹 app_schedule(DEV_ModamCore) 에는 이 문서를 보고 같은 규칙을 넣는다. 이 문서를 쓰는 시점에는 그 두 곳에 코드를 넣지 않았다.

1. 규칙

할일의 칸은 생성일(created_at)이다. “오늘 할일”(is_my_day)은 그 칸을 옮기는 스위치다.

동작결과
켠다 생성일을 오늘로 옮긴다. 원래 칸에는 복사로 남기지 않는다. 떠나기 전 날을 anchor_created에 저장한다.
저장한 뒤 다시 연다 생성일은 오늘이다. 되돌릴 날은 anchor_created에 그대로 있다.
끈다 생성일을 anchor_created의 날로 되돌리고, 그 필드는 비운다.
떠나기 전 날 = 기한 기한을 비운다. 그 칸에 기한 때문에 복사처럼 남지 않게 한다.
이미 오늘인데 다시 켠다 저장돼 있는 되돌릴 날을 오늘로 덮지 않는다.
생성일을 오늘이 아닌 날로 직접 고른다 “오늘 할일”은 꺼지고 anchor_created는 비운다. 고른 날이 새 생성일이다.
“오늘 할일”이 켜진 채로 생성일을 오늘로 고른다 스위치는 유지하고, 되돌릴 날은 덮지 않는다.
할일 보기에서 시트를 열 때 “오늘 할일”이 기본으로 켜져 있어도, 신규 생성일은 고른 칸이다. 오늘로 덮지 않는다. anchor_created는 사용자가 스위치를 켜서 다른 날에서 오늘로 떠날 때만 채운다.

예. 오늘은 2026-09-23.

  1. 9월 26일에 할일 111111111을 만들고 저장한다. 생성일 2026-09-26.
  2. 그 할일을 열어 “오늘 할일”을 켠다. 칸은 9월 23일이 되고, anchor_created는 2026-09-26.
  3. 저장한 뒤 9월 23일에서 다시 열어 스위치를 끈다. 칸은 9월 26일로 돌아간다.

2. 고친 원인

아이폰 수정 전에는 되돌릴 날을 편집 시트의 메모리(_anchorCreatedIso)에만 두고, 저장되는 생성일(created_at)은 오늘로 바뀌었다. 시트를 닫으면 그 메모리가 사라져, 다시 열면 되돌릴 날이 오늘이 되었다.

아이폰 day_plan_appl_app 에는 2026-09-23에 반영됨. DB 버전 12.

기준 파일:

3. 공통 필드

이름형식의미
anchor_created TEXT, NULL 허용. 값 예 2026-09-26 “오늘 할일”을 켜기 전 생성일. 끄면 이 날로 되돌리고 NULL로 지운다.

날짜만 저장한다. 시각은 붙이지 않는다. 업데이트할 때 키가 빠지면 이전 값이 남으므로, 끌 때는 anchor_created: null을 반드시 보낸다.

4. 안드로이드 day_plan_app

윈도우에서 연다. 날짜 규칙(생성일이 칸, “오늘 할일”이 생성일을 오늘로 이동)이 이미 들어가 있는 코드를 기준으로 한다. 스위치가 t.isMyDay = v만 하고 생성일을 옮기지 않는 옛 코드라면, 먼저 docs/2026-09-23/할일일정기록_날짜규칙.html의 “오늘 할일은 이동이다”를 넣은 다음 이 절을 적용한다.

핫 리로드만으로는 컬럼이 생기지 않는다. 앱을 완전히 다시 실행한다.

4.1 lib/db/app_db.dart

DB 버전을 현재 값보다 1 올린다. 아이폰은 11 → 12다. 윈도우 소스가 이미 12 이상이면 그 다음 번호를 쓴다.

onCreate의 tb_todo 끝에 컬럼을 넣는다.

created_at      TEXT    NOT NULL DEFAULT (datetime('now','localtime')),
anchor_created  TEXT

onUpgrade에 추가한다. 12는 실제 올린 버전 번호로 바꾼다.

if (oldV < 12) {
  await d.execute('ALTER TABLE tb_todo ADD COLUMN anchor_created TEXT');
}

반복 일정의 과거 회차를 단일 일정으로 분리하는 detachPastRepeatEvent insert 맵에 아래를 넣는다. 분리된 과거 일정은 “오늘 할일” 이동이 아니다.

..['is_my_day'] = 0
..['anchor_created'] = null

4.2 lib/models/todo.dart

필드, 생성자, fromMap, toMap, copy에 anchorCreated를 넣는다.

String? anchorCreated; // yyyy-MM-dd. 오늘 할일을 끄면 이 날로 되돌린다.
anchorCreated: m['anchor_created'] as String?,
// null도 항상 실어, 끌 때 컬럼이 지워지게 한다.
'anchor_created': anchorCreated,

이동 로직. 아이폰과 같은 메서드를 둔다.

void applyTodayMove(bool on, String today) {
  isMyDay = on;
  final cur = _dateHead(createdAt);
  if (on) {
    if (cur != null && cur != today) anchorCreated = cur;
    if (dueDate != null && dueDate!.isNotEmpty && dueDate == cur) {
      dueDate = null;
    }
    createdAt = '$today 00:00:00';
    return;
  }
  final back = _dateHead(anchorCreated);
  if (back != null) createdAt = '$back 00:00:00';
  anchorCreated = null;
}

String? _dateHead(String? raw) {
  if (raw == null || raw.length < 10) return null;
  return raw.substring(0, 10);
}

4.3 lib/widgets/todo_edit_sheet.dart

기존 항목을 열 때, “오늘 할일”이 켜져 있고 anchorCreated가 있으면 그 날을 되돌릴 날로 쓴다. 생성일(오늘)로 되돌릴 날을 초기화하지 않는다.

final created = _dateOnly(t.createdAt);
final saved = _dateOnly(t.anchorCreated);
_anchorCreatedIso = (t.isMyDay && saved.isNotEmpty)
    ? saved
    : (created.isNotEmpty ? created : t.dueDate);

스위치는 모델 메서드를 호출한다.

void _setTodayTask(bool on) {
  t.applyTodayMove(on, isoOf(DateTime.now()));
  if (on) {
    final saved = _dateOnly(t.anchorCreated);
    if (saved.isNotEmpty) _anchorCreatedIso = saved;
  } else {
    final created = _dateOnly(t.createdAt);
    if (created.isNotEmpty) _anchorCreatedIso = created;
  }
}

생성일을 직접 고를 때.

final iso = isoOf(d);
final today = isoOf(DateTime.now());
t.createdAt = _createdStamp(iso);
if (t.isMyDay && iso == today) return;
_anchorCreatedIso = iso;
t.anchorCreated = null;
if (iso != today) t.isMyDay = false;

스위치 onChanged는 setState(() => _setTodayTask(v))다.

4.4 테스트

test/today_move_test.dart를 아이폰과 같이 둔다. 확인할 경우:

5. 웹 app_schedule

서버 tb_todo와 할일 편집 화면의 “오늘”(폰에서는 “오늘 할일”)에 같은 규칙을 넣는다. 칸 위치가 생성일인 날짜 규칙이 웹에 이미 있을 때를 기준으로 한다.

5.1 DB

ALTER TABLE tb_todo ADD COLUMN anchor_created TEXT NULL;

신규 스키마를 만드는 경로에도 같은 컬럼을 넣는다. 기존 행은 NULL이다.

5.2 저장·조회

5.3 스위치

오늘은 서버·브라우저의 로컬 날짜 하루 단위다. 비교는 yyyy-MM-dd.

  1. 켠다. 현재 생성일의 앞 10자가 오늘과 다르면 그 날을 anchor_created에 넣는다. 생성일을 오늘 00:00:00으로 바꾼다. is_my_day를 1로 한다. 떠나기 전 날과 기한이 같으면 기한을 비운다.
  2. 끈다. anchor_created가 있으면 생성일을 그 날 00:00:00으로 되돌린다. anchor_created는 NULL, is_my_day는 0.
  3. 편집 화면을 다시 열 때, is_my_day가 1이고 anchor_created가 있으면 화면의 “되돌릴 날”은 그 값이다. 지금 생성일(오늘)로 바꾸지 않는다.
  4. 생성일을 오늘이 아닌 날로 직접 바꾸면 스위치를 끄고 anchor_created를 비운다.
  5. 스위치가 켜진 상태에서 생성일을 오늘로 고르면 anchor_created를 유지한다.

목록·달력은 추가 분기가 필요 없다. 칸은 여전히 생성일이다. 스위치가 켜져 저장되면 생성일이 오늘이라 오늘 칸에 있고, 끄면 생성일이 원래 날이라 그 칸으로 돌아간다.

6. 앱과 웹 동기화

폰과 웹이 tb_todo를 주고받으면 양쪽 스키마와 전송 맵에 anchor_created가 있어야 한다. 한쪽에만 있으면, 동기화 뒤에 되돌릴 날이 사라지거나 모르는 컬럼으로 저장이 거절된다.

7. 이미 옮겨 둔 항목

이 컬럼이 생기기 전에 “오늘 할일”을 켜서 저장한 행은 원래 날이 DB에 없다. 마이그레이션으로 되돌리지 않는다. 그 항목은 생성일을 원하는 날로 한 번 다시 고른다. 그 다음부터 켰다 끄면 그 날로 돌아간다.

아이폰에서 이미 9월 23일로 옮겨 둔 111111111도 같다. 생성일을 9월 26일로 한 번 고른다.

8. 확인

오늘은 2026-09-23으로 두고, 안드로이드와 웹에서 각각 본다.

  1. 9월 26일에 할일을 만들고 저장한다. 9월 26일 칸에만 있다.
  2. 열어 “오늘 할일”을 켜고 저장한다. 9월 26일 칸에서 사라지고 9월 23일에 있다.
  3. 앱 또는 브라우저를 닫았다 연다. 9월 23일 항목의 스위치를 끈다. 9월 26일로 돌아간다.
  4. 같은 항목을 다시 켜고 저장한 뒤, 다시 끄면 또 9월 26일이다.
  5. 9월 26일에 만들고, 기한도 9월 26일로 둔 뒤 스위치를 켠다. 기한은 비고, 9월 26일 칸에는 남지 않는다.
  6. 오늘에 만든 할일의 스위치를 켜고 꺼도 오늘에 남는다.
  7. 스위치를 켠 상태에서 생성일을 9월 28일로 직접 고르면 스위치가 꺼지고 9월 28일에 있다. 그 뒤 스위치를 켜고 끄면 9월 28일로 돌아간다.
  8. 폰에서 켠 항목을 웹에서 끄면 원래 날로 돌아가고, 반대로도 같다.