1

I have a hash containing keys and timestamps as values as shown below. Is there a way to find which keys have current week's timestamp.

my %hash = (
      'key1'  => '2018-01-17',
      'key2'  => '2018-02-05,2018-01-08',
      'key3'  => '2018-01-26' ,
      'key4'  => '2018-01-04',
      'key5'  => '2018-01-27,2018-02-06,2018-01-28'
    );

Expected result is : 'key2' and 'key5' with values 2018-02-05 and 2018-02-06 falling in current week.

ran1n
  • 157
  • 6

2 Answers2

5

Personally I like DateTime for all my date/time needs because it is a pretty complete solution. In the following, I've assumed that your hash's values are consistently formatted (YYYY-MM-DD separated by commas, no whitespace). Also note that this uses Monday as the first day of the week, see the DateTime truncate method for details.

use warnings;
use strict;
use DateTime;
use DateTime::Format::Strptime;

my %hash = (
    'key1' => '2018-01-17',
    'key2' => '2018-02-05,2018-01-08',
    'key3' => '2018-01-26',
    'key4' => '2018-01-04',
    'key5' => '2018-01-27,2018-02-06,2018-01-28'
);

my $strp = DateTime::Format::Strptime->new( on_error=>'croak',
    pattern => '%Y-%m-%d', time_zone=>'floating' );

my $week_start = DateTime->now(time_zone=>'local')
    ->set_time_zone('floating')->truncate(to=>'week');
my $week_end   = $week_start->clone->add( days => 7 );

for my $key (sort keys %hash) {
    for my $date (split /,/, $hash{$key}) {
        my $dt = $strp->parse_datetime($date);
        # DateTime objects overload comparison operators
        if ( $week_start <= $dt && $dt < $week_end ) {
            print "$key\n";
        }
    }
}
haukex
  • 2,973
  • 9
  • 21
  • Don't truncate unless you switch to floating, or it can [fail](https://stackoverflow.com/q/18489927/589924). Fixed. – ikegami Feb 05 '18 at 14:22
  • @ikegami - Thanks, though it's `floating` and not `float`, fixed. Update: Ah, seems we both caught that at almost exactly the same time :-) – haukex Feb 05 '18 at 14:32
  • @ikegami - Hm, yes, using `local` time is probably better, edited, thanks. – haukex Feb 05 '18 at 14:36
4

There are lots of ways of doing what you want. With Time::Piece which I believe is core as of Perl 5.9.5, you could do:

use Time::Piece;

my %hash = (
  'key1' => '2018-01-17',
  'key2' => '2018-02-05,2018-01-08',
  'key3' => '2018-01-26' ,
  'key4' => '2018-01-04',
  'key5' => '2018-01-27,2018-02-06,2018-01-28'
);

my $current = localtime->strftime('%Y-%V');

foreach my $i (sort keys %hash) {
  foreach my $j (split /\s*,\s*/, $hash{$i}) {
    if( localtime->strptime($j, '%Y-%m-%d')->strftime('%Y-%V') eq $current ) {
      print 'Key: ', $i, ', date: ', $j, "\n";
    }
  }
}

It uses the fact that %V is the ISO 8601 week number, which concatenated with the year gives something to compare against.

The function strptime turns strings into date-objects and then strftime turns date-objects into strings.

The output:

Key: key2, date: 2018-02-05
Key: key5, date: 2018-02-06

The function strftime has other week definitions which you may use: %U, %V and %W; see for instance here.

ikegami
  • 367,544
  • 15
  • 269
  • 518
Javier Elices
  • 2,066
  • 1
  • 16
  • 25