Конвертировать даты с отсутствующими месяцами и датами в определенный формат в Perl - PullRequest
0 голосов
/ 22 декабря 2018

Я использую Perl только неделю, поэтому надеюсь, что здесь кто-нибудь сможет помочь.

Скрипт, который мне помог в написании, импортирует файл с разделителями табуляции в хеш,с одним столбцом, содержащим дату, сохраненную в формате ГГГГММДД.Это выводится в файл как День Месяц Год (например, 20180712 печатается как 12 июля 2018).Я нашел способ преобразовать это здесь в Как я могу изменить форматы даты в Perl? следующим образом:

my $date = '20111230';
my @months = ('January','February','March','April','May','June','July','August','September','October','November','December');

if($date =~ m/^(\d{4})(\d{2})(\d{2})$/){
    print $3 . ' ' . $months[$2-1] . ' ' . $1;
}               

Однако иногда дата сохраняется только как годи месяц, и в очень редких случаях это только год.Это хранится в хэше с нулями, заменяющими день (и месяц, если необходимо).Следовательно, я потребовал, чтобы 20180700 было напечатано как июль 2018 года, а 20180000 напечатано как 2018.

Я могу изменить код, чтобы проверить, являются ли последние два символа 00, а затем печатать только месяц и год, и аналогичнопроверьте, являются ли последние четыре символа 0000 и т. д., но есть ли более элегантный подход.

Ответы [ 2 ]

0 голосов
/ 25 декабря 2018

zdim предложил использовать unpack (), что не намного лучше, чем регулярное выражение в этом случае.Так что я бы сказал, что оригинальное решение уже в порядке;вам просто нужно завершить его, добавив немного кода, что-то вроде:

my $date = '20111230';
my @months = ('January','February','March','April','May','June','July','August','September','October','November','December');

if ($date =~ m/^(\d{4})(\d{2})(\d{2})$/){
    print ($3 > 0 ? $3 . ' ' : '') . ($2 > 0 ? $months[$2-1] . ' ' : '') . $1;
} else {
    die "Invalid date: $date";
}
0 голосов
/ 22 декабря 2018

Формат с 00 для отсутствующего дня / месяца хорошо определен, но он кодирует особые случаи, которые не соответствуют формату yyyymmdd.Я не вижу, как может быть подход, который избегает явных тестов для этих особых случаев, когда день / месяц просто пропущен.

Я хотел бы предложить не выбирать дату и время с помощью регулярного выражения, так как для этой работы есть хорошие модули.Даже если этот пример прост, как указано, рабочие места имеют тенденцию развиваться;Кроме того, нет ничего плохого в использовании хорошего инструмента даже в простом случае.

Использование модуля ядра Time :: Piece

use warnings;
use strict;
use feature 'say';

use Time::Piece;

my $d = shift || '20180712';

my $date = fmt_date($d);

say $date;

sub fmt_date {
    my ($date) = @_;         
    my ($yr, $mm, $dd) = grep { $_ != 0 } unpack "A4A2A2", $date;
    my $d_fmt;

    if ($yr and $mm and $dd) {
        $d_fmt = Time::Piece
            ->strptime($date, "%Y%m%d")
            ->strftime("%d %B %Y");
    }   
    elsif (not $dd and $mm) {
        $d_fmt = Time::Piece
            ->strptime($yr.$mm.'01', "%Y%m%d")
            ->strftime("%B %Y");
    }   
    elsif (not $mm) {
        $d_fmt = $yr 
    }   
    return $d_fmt;
}

Я фильтрую списоквозвращается распаковать , чтобы не иметь дело с 00 строк ;таким образом, соответствующие переменные будут undef, что можно проверить более просто.

strptime возвращает объект Time::Piece, для которого напрямую вызывается метод strftime, возвращая строку вжелаемый формат.Если с этими датами есть больше работы, вы, конечно, можете сохранить объект в переменной, затем сформировать строку из нее и вернуть оба.

Но возникает вопрос дизайна: какой это будет дата, когда день / понедельник не указан?При работе с датами решение часто заключается в том, чтобы установить их на 01, и тогда приложение может использовать только ту часть, которую оно хочет.

Это можно сделать более компактной и, возможно, "более приятной"."но я бы посоветовал не беспокоиться об элегантности, когда вы должны выбрать список тестов.

Другой, более крупный и гораздо более округлый вариант для обработки даты и времени - это DateTime module.


Например,

sub fmt_date {
    my ($date) = @_;
    my ($yr, $mm, $dd) = grep { $_ != 0 } unpack "A4A2A2", $date;

    my $dt_obj = Time::Piece->strptime(
        $yr . ($mm // '01') . ($dd // '01'), "%Y%m%d"  # legit format
    );

    my $d_fmt = do {
        if ($yr and $mm and $dd) { $dt_obj->strftime("%d %B %Y") }
        elsif (not $dd and $mm)  { $dt_obj->strftime("%B %Y")    }
        elsif (not $mm)          { $dt_obj->strftime("%Y")       }  # or, $yr 
    };  

    return wantarray ? ($d_fmt, $dt_obj) : $d_fmt;
}

, где wantarray знает вызывающий context , и поэтому это можеттеперь может называться либо

my ($date, $obj) = fmt_date($d);

, либо

my $date = fmt_date($d);

в зависимости от того, хочет ли вызывающий объект для дальнейшей работы или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...