diff --git a/db/db.ddl b/db/db.ddl
index ae71eae76c401b591c1141c55d4aa1514e736f58..7d65c0583a5b6ac05c4dc699b1f555d578a324f5 100644
--- a/db/db.ddl
+++ b/db/db.ddl
@@ -95,18 +95,25 @@ CREATE TABLE rounds (
 	seq		int		NOT NULL,			-- 1=domácí kolo atd.
 	level		int		NOT NULL,			-- úroveň hierarchie míst
 	name		varchar(255)	NOT NULL,			-- zobrazované jméno ("Krajské kolo" apod.)
-	state		round_state	NOT NULL DEFAULT 'preparing',	-- stav kola
-	tasks_file	varchar(255)	DEFAULT NULL,			-- jméno souboru se zadáním úloh
-	ct_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy účastníci vidí zadání
-	ct_submit_end	timestamp with time zone	DEFAULT NULL,	-- do kdy účastníci mohou regulérně odevzdávat
-	pr_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy dozor vidí zadání
-	pr_submit_end	timestamp with time zone	DEFAULT NULL,	-- do kdy dozor může regulérně odevzdávat
+	-- termíny, odkaz na zadání a stav odsunuty do round_parts, vždy bude existovat alespoň 1 instance pro kolo
 	score_mode	score_mode	NOT NULL DEFAULT 'basic',	-- mód výsledkovky
 	score_winner_limit	int	DEFAULT NULL,			-- bodový limit na označení za vítěze
 	score_successful_limit	int	DEFAULT NULL,			-- bodový limit na označení za úspěšného řešitele
 	UNIQUE (year, category, seq)
 );
 
+CREATE TABLE round_parts (
+	round_part_id	serial		PRIMARY KEY,
+	round_id	int		NOT NULL REFERENCES rounds(round_id),
+	name		varchar(255)	NOT NULL,			-- jméno části ("I", "1. den" apod.)
+	state		round_state	NOT NULL DEFAULT 'preparing',	-- stav části kola
+	tasks_file	varchar(255)	DEFAULT NULL,			-- jméno souboru se zadáním úloh
+	ct_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy účastníci vidí zadání
+	ct_submit_end	timestamp with time zone	DEFAULT NULL,	-- do kdy účastníci mohou regulérně odevzdávat
+	pr_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy dozor vidí zadání
+	pr_submit_end	timestamp with time zone	DEFAULT NULL	-- do kdy dozor může regulérně odevzdávat
+);
+
 -- Soutěže (instance kola v konkrétním místě)
 CREATE TABLE contests (
 	contest_id	serial		PRIMARY KEY,
@@ -150,6 +157,7 @@ CREATE INDEX participations_contest_id_index ON participations (contest_id, plac
 CREATE TABLE tasks (
 	task_id		serial		PRIMARY KEY,
 	round_id	int		NOT NULL REFERENCES rounds(round_id),
+	round_part_id	int		DEFAULT NULL REFERENCES round_parts(round_part_id),	-- pokud pro úlohu mají platit jiné termíny
 	code		varchar(255)	NOT NULL,			-- např. "P-I-1"
 	name		varchar(255)	NOT NULL,
 	UNIQUE (round_id, code)
diff --git a/db/upgrade-20210304.sql b/db/upgrade-20210304.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f400ad14a4d2501e2503cc43c1047eef506000d9
--- /dev/null
+++ b/db/upgrade-20210304.sql
@@ -0,0 +1,35 @@
+SET ROLE 'mo_osmo';
+
+CREATE TABLE round_parts (
+	round_part_id	serial		PRIMARY KEY,
+	round_id	int		NOT NULL REFERENCES rounds(round_id),
+	name		varchar(255)	NOT NULL,			-- jméno části ("I", "1. den" apod.)
+	-- tato část je identická s částí tabulky rounds
+	state		round_state	NOT NULL DEFAULT 'preparing',	-- stav části kola
+	tasks_file	varchar(255)	DEFAULT NULL,			-- jméno souboru se zadáním úloh
+	ct_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy účastníci vidí zadání
+	ct_submit_end	timestamp with time zone	DEFAULT NULL,	-- do kdy účastníci mohou regulérně odevzdávat
+	pr_tasks_start	timestamp with time zone	DEFAULT NULL,	-- od kdy dozor vidí zadání
+	pr_submit_end	timestamp with time zone	DEFAULT NULL	-- do kdy dozor může regulérně odevzdávat
+);
+
+ALTER TABLE tasks
+	ADD COLUMN	round_part_id	int		DEFAULT NULL REFERENCES round_parts(round_part_id);
+
+-- v tuhle chvíli je potřeba provést migraci dat z tabulek rounds do round_parts
+INSERT INTO round_parts (round_id, name, state, tasks_file, ct_tasks_start, ct_submit_end, pr_tasks_start, pr_submit_end)
+	SELECT round_id, 'default', state, tasks_file, ct_tasks_start, ct_submit_end, pr_tasks_start, pr_submit_end FROM rounds;
+
+-- a update všech úloh, aby obsahovaly odkaz na právě vytvořené round_parts (existuje právě jedna pro kolo)
+UPDATE tasks SET round_part_id=subquery.round_part_id
+	FROM (SELECT round_part_id, round_id FROM round_parts) AS subquery
+	WHERE subquery.round_id=tasks.round_id;
+
+-- odstranění sloupců z rounds (spouštět s opatrností po ověření, že vše funguje!)
+-- ALTER TABLE rounds
+-- 	DROP COLUMN state,
+-- 	DROP COLUMN tasks_file,
+-- 	DROP COLUMN ct_tasks_start,
+-- 	DROP COLUMN ct_submit_end,
+-- 	DROP COLUMN pr_tasks_start,
+-- 	DROP COLUMN pr_submit_end;
diff --git a/mo/db.py b/mo/db.py
index 95184e96b0eb92f939fdc8199f61aa3407b3dc29..4c1ed53f34e71974b88a0ec2c5b14e3109c16290 100644
--- a/mo/db.py
+++ b/mo/db.py
@@ -203,18 +203,30 @@ class Round(Base):
     seq = Column(Integer, nullable=False)
     level = Column(Integer, nullable=False)
     name = Column(String(255), nullable=False)
+    score_mode = Column(Enum(RoundScoreMode, name='score_mode'), nullable=False, server_default=text("'basic'::score_mode"))
+    score_winner_limit = Column(Integer)
+    score_successful_limit = Column(Integer)
+
+    parts = relationship('RoundPart', primaryjoin='RoundPart.round_id == Round.round_id')
+
+    def round_code(self):
+        return f"{self.year}-{self.category}-{self.seq}"
+
+
+class RoundPart(Base):
+    __tablename__ = 'round_parts'
+
+    round_part_id = Column(Integer, primary_key=True, server_default=text("nextval('round_parts_round_part_id_seq'::regclass)"))
+    round_id = Column(Integer, ForeignKey('rounds.round_id'), nullable=False)
+    name = Column(String(255), nullable=False)
     state = Column(Enum(RoundState, name='round_state'), nullable=False, server_default=text("'preparing'::round_state"))
     tasks_file = Column(String(255))
     ct_tasks_start = Column(DateTime(True))
     ct_submit_end = Column(DateTime(True))
     pr_tasks_start = Column(DateTime(True))
     pr_submit_end = Column(DateTime(True))
-    score_mode = Column(Enum(RoundScoreMode, name='score_mode'), nullable=False, server_default=text("'basic'::score_mode"))
-    score_winner_limit = Column(Integer)
-    score_successful_limit = Column(Integer)
 
-    def round_code(self):
-        return f"{self.year}-{self.category}-{self.seq}"
+    round = relationship('Round', primaryjoin='RoundPart.round_id == Round.round_id')
 
     def has_tasks(self):
         return self.tasks_file
@@ -369,10 +381,12 @@ class Task(Base):
 
     task_id = Column(Integer, primary_key=True, server_default=text("nextval('tasks_task_id_seq'::regclass)"))
     round_id = Column(Integer, ForeignKey('rounds.round_id'), nullable=False)
+    round_part_id = Column(Integer, ForeignKey('round_parts.round_part_id'), nullable=False)
     code = Column(String(255), nullable=False)
     name = Column(String(255), nullable=False)
 
     round = relationship('Round')
+    round_part = relationship('RoundPart')
 
 
 class RoleType(MOEnum):