+sub format_date {
+ $main::lxdebug->enter_sub();
+
+ my $self = shift;
+ my $myconfig = shift;
+ my $yy = shift;
+ my $mm = shift;
+ my $dd = shift;
+ my $yy_len = shift || 4;
+
+ ($yy, $mm, $dd) = ($yy->year, $yy->month, $yy->day) if ref $yy eq 'DateTime';
+
+ $main::lxdebug->leave_sub() and return "" unless $yy && $mm && $dd;
+
+ $yy = $yy % 100 if 2 == $yy_len;
+
+ my $format = ref $myconfig eq '' ? "$myconfig" : $myconfig->{dateformat};
+ $format =~ s{ d+ }{ sprintf("%0" . (length($&)) . "d", $dd) }gex;
+ $format =~ s{ m+ }{ sprintf("%0" . (length($&)) . "d", $mm) }gex;
+ $format =~ s{ y+ }{ sprintf("%0${yy_len}d", $yy) }gex;
+
+ $main::lxdebug->leave_sub();
+
+ return $format;
+}
+
+sub quote_special_chars {
+ my $self = shift;
+ my $format = lc shift;
+ my $string = shift;
+
+ if ($self->{special_chars_map} && $self->{special_chars_map}->{$format} && $self->{special_chars_map}->{$format}->{order}) {
+ my $scmap = $self->{special_chars_map}->{$format};
+
+ map { $string =~ s/\Q${_}\E/$scmap->{$_}/g } @{ $scmap->{order} };
+ }
+
+ return $string;
+}
+
+sub unquote_special_chars {
+ my $self = shift;
+ my $format = shift;
+
+ return $self->quote_special_chars("${format}-reverse", shift);
+}
+
+sub remap_special_chars {
+ my $self = shift;
+ my $src_format = shift;
+ my $dst_format = shift;
+
+ return $self->quote_special_chars($dst_format, $self->quote_special_chars("${src_format}-reverse", shift));
+}
+
+sub raw_io_active {
+ my $self = shift;
+
+ return !!$self->{raw_io_active};
+}
+
+sub with_raw_io {
+ my $self = shift;
+ my $fh = shift;
+ my $code = shift;
+
+ $self->{raw_io_active} = 1;
+ binmode $fh, ":raw";
+ $code->();
+ binmode $fh, ":utf8";
+ $self->{raw_io_active} = 0;
+}
+
+sub set_numberformat_wo_thousands_separator {
+ my $self = shift;
+ my $myconfig = shift || \%::myconfig;
+
+ $self->{saved_numberformat} = $myconfig->{numberformat};
+ $myconfig->{numberformat} =~ s/^1[,\.]/1/;
+}
+
+sub restore_numberformat {
+ my $self = shift;
+ my $myconfig = shift || \%::myconfig;
+
+ $myconfig->{numberformat} = $self->{saved_numberformat} if $self->{saved_numberformat};
+}
+
+sub get_local_time_zone {
+ my $self = shift;
+ $self->{local_time_zone} ||= DateTime::TimeZone->new(name => 'local');
+ return $self->{local_time_zone};
+}
+
+sub language_join {
+ my ($self, $items, %params) = @_;
+
+ $items ||= [];
+ $params{conjunction} ||= $::locale->text('and');
+ my $num = scalar @{ $items };
+
+ return 0 == $num ? ''
+ : 1 == $num ? $items->[0]
+ : join(', ', @{ $items }[0..$num - 2]) . ' ' . $params{conjunction} . ' ' . $items->[$num - 1];
+}
+