Thanks for the answer!
I tried to use convert on 'date' and serialize on 'day', 'month', 'year'. Unfortunately, when 'date' had 'persist: false' and it was the only thing changed, there wasn't event raised to write changes to the base (although I wanted to change 'day', 'month', 'year' based on 'date'). Code sample:
Code:
{name: 'day', type: 'int',
serialize: function( value, record ){
return record.data.date.getFullDate();
}},
{name: 'month', type: 'int',
serialize: function( value, record ){
return record.data.date.getFullMonth();
}},
{name: 'year', type: 'int',
serialize: function( value, record ){
return record.data.date.getFullYear();
}},
{name: 'date', persist: false,
convert: function( value, record ){
return new Date(record.data.year, record.data.month, record.data.day);
}
}
But! Thanks to further investigation I happened to notice that 'convert' had value as an empty string when data was given from database and a date string when it was given from a form (unless form was not properly filled). This way I can distinguish filling form and getting data from database and have a two way convert. I believe it is a workaround but for the time being it is the best solution I could find. Still have to work on nullable date in forms though (probably check whether the date was already filled will be sufficient).
Thanks for help! If no one wants to share better solution we can close this thread 
Code sample of my solution:
Code:
{name: 'day', type: 'int'},
{name: 'month', type: 'int'},
{name: 'year', type: 'int'},
{name: 'date', persist: false,
convert: function( value, record ){
if (value == "")
{
return new Date(record.data.year, record.data.month, record.data.day);
}
else {
var date = new Date(value);
record.set('day', date.getDate());
record.set('month', date.getMonth());
record.set('year', date.getFullYear());
return date;
}
}
}