in reply to Moo different parameters for constructor

In the spirit of TIMTOWTDI, here's a way without BUILDARGS. All the attributes are lazy and depend on each other. I used Time::Piece instead of Date::Calc::Today, but that's not relevant to the question.
#! /usr/bin/perl { package MyDate; use Moo; use Time::Piece; use namespace::clean; has year => (is => 'lazy'); has month => (is => 'lazy'); has day => (is => 'lazy'); has date => (is => 'lazy', predicate => 'has_date'); sub _from_date { my ($self, $what, $pos, $length) = @_; return substr $self->date, $pos, $length if $self->has_date; return localtime->$what } sub _build_year { $_[0]->_from_date(year => 0, 4) } sub _build_month { $_[0]->_from_date(mon => 4, 2) } sub _build_day { $_[0]->_from_date(mday => 6, 2) } my %format = ( year => '%04d' ); sub _build_date { my ($self) = @_; join "", map sprintf($format{$_} || '%02d', $self->$_), qw( year month day ) } } use Test::Spec; describe MyDate => sub { it 'initializes from now' => sub { Time::Piece->expects('year')->returns(2019); Time::Piece->expects('mon')->returns(5); Time::Piece->expects('mday')->returns(16); my $md = 'MyDate'->new(); is $md->date, '20190516'; }; it 'initializes from date' => sub { my $md = 'MyDate'->new(date => 20190201); is_deeply [ $md->year, $md->month, $md->day ], [2019, '02', '0 +1']; }; it 'initializes from ymd' => sub { my $md = 'MyDate'->new(year => 2019, month => 11, day => 3); is $md->date, '20191103'; }; }; runtests();
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]