web-dev-qa-db-fra.com

Comment une séquence de dates aléatoires pourrait-elle être générée, compte tenu de l'intervalle annuel?

Ce dont nous avons besoin ici, c'est d'une commande qui génère six dates pour une plage d'années (1987 à 2017). Par exemple:

12/10/1987
30/04/1998
22/02/2014
17/08/2017
19/07/2011
14/05/2004

Comment cela pourrait-il être fait, avec sed, gawk, etc.?

12
Raymond83

Vous pouvez transformer le problème en générant un nombre aléatoire entre un nombre représentant la première date possible et un nombre représentant la dernière date possible (en fait celle juste après la dernière possible), au format Unix Epoch. Tout le reste est géré par des conversions de date standard. gawk a une meilleure résolution de nombres aléatoires que bash (float vs entier 15 bits), donc j'utiliserai gawk. Notez que le résultat Rand() N est un flottant tel que 0 <= N <1, c'est pourquoi la limite supérieure est augmentée en dessous, c'est une limite qui ne peut pas être roulée. Il y a un 3e paramètre facultatif pour le nombre de résultats.

#!/usr/bin/gawk -f
BEGIN {
    first=mktime(ARGV[1] " 01 01 00 00 00")
    last=mktime(ARGV[2]+1 " 01 01 00 00 00")
    if (ARGC == 4) { num=ARGV[3] } else { num=1 }
    ARGC=1
    range=last-first
    srand(sprintf("%d%06d", systime(), PROCINFO["pid"]))
    for (i=1; i <= num; i++) {
        print strftime("%d/%m/%Y", range*Rand()+first)
    }
}   

Par exemple:

./randomdate.gawk 1987 2017 6
26/04/1992
28/04/2010
21/04/2005
17/02/2010
06/10/2016
04/04/1998
10
A.B

Avec date, shuf et xargs:

Convertissez la date de début et de fin en "secondes depuis le 01-01-1970 00:00:00 UTC" et utilisez shuf pour imprimer six valeurs aléatoires dans cette plage. Dirigez ce résultat vers xargs et convertissez les valeurs au format de date souhaité.

Edit: Si vous voulez que les dates de l'année 2017 soient incluses dans la sortie, vous devez ajouter un an -1s (2017-12-31 23:59:59) jusqu'à la date de fin. shuf -i génère des nombres aléatoires comprenant le début et la fin.

shuf -n6 -i$(date -d '1987-01-01' '+%s')-$(date -d '2017-01-01' '+%s')\
 | xargs -I{} date -d '@{}' '+%d/%m/%Y'

Exemple de sortie:

07/12/1988
22/04/2012
24/09/2012
27/08/2000
19/01/2008
21/10/1994
19
Freddy

Perl:

Perl -MTime::Piece -sE '
    my $t1 = Time::Piece->strptime("$y1-01-01 00:00:00", "%Y-%m-%d %H:%M:%S");
    my $t2 = Time::Piece->strptime("$y2-12-31 23:59:59", "%Y-%m-%d %H:%M:%S");
    do {
        my $time = $t1 + int(Rand() * ($t2 - $t1));
        say $time->dmy;
    } for 1..6;
' -- -y1=1987 -y2=2017

Exemple de sortie:

10-01-1989
30-04-1995
10-12-1998
02-01-2016
04-06-2006
11-04-1987
10
glenn jackman

Voici ne façon, principalement en awk:

#!/bin/sh

[ "$end" -ge "$start" ] || exit 1

awk -v start=$1 -v end=$2 '
  BEGIN {
    srand();
    for(i=1; i <= 6; i++)
      printf "%02d/%02d/%d\n", 1 + Rand() * 28, 1 + Rand() * 12, start + Rand() * (end-start);
  }
' < /dev/null

Le script Shell prend deux paramètres et les transmet en tant que variables à awk, qui ne lit aucune entrée et fait tout le travail dans le bloc BEGIN.

Après avoir amorcé le générateur de nombres aléatoires, il boucle 6 fois sur une instruction printf. Cette instruction d'impression sélectionne un sous-ensemble des dates possibles dans la plage en générant un nombre aléatoire entre 1 et 28 pour la journée (sans danger pour février), entre 1 et 12 pour le mois, et entre les années de début et de fin données. C'est aléatoire, mais ce n'est pas une couverture complète - il n'imprimera jamais les jours 29 à 31 pour les mois qui en ont.

Une autre façon, en utilisant GNU fonctionnalités de date et bash:

#!/bin/bash

start=$1
end=$2

[[ "$start" -le "$end" ]] || exit 1

startsec=$(date -d "1/1/$start" +%s)
for((i=1; i<=6; i++))
do
  r=$((RANDOM % (1 + end - start)*365*24*60*60))
  date -d @$((startsec + r)) +%d/%m/%Y
done

Il fonctionne en calculant les secondes depuis l'époque du 1er janvier à la date de début, puis pour chacune des boucles, il arrive avec un nombre aléatoire de secondes de décalage à ajouter; le nombre aléatoire est limité au nombre de secondes couvrant la plage donnée. GNU date manipule ensuite cette date dans le format souhaité.

9
Jeff Schaller

Si la différence dans le nombre d'années est inférieure à environ 90, vous pouvez utiliser le $RANDOM variable en bash pour vous donner un décalage en nombre de jours et utiliser la capacité limitée de la commande date pour faire le calcul.

#!/bin/bash
s=$(date +%s -d "1/1/$1")          # start in seconds since 1 Jan 1970
e=$(date +%s -d "1/1/$(($2+1))")   # start of end year +1 in seconds
days=$(((e-s)/(24*3600)))          # number of days from start to end
factor=$((32767/$days))            # RANDOM is 0 to 32767. See how many
toobig=$(($factor*$days))          # exact multiples of days.
                                   # if RANDOM is too large, draw again
for((i=0;i<${3:-1};i++))           # produce $3 random dates
do
    r=$RANDOM                      # find a random number < toobig
    while (( r >= toobig ))        # if toobig, then loop.
    do r=$RANDOM
    done
    offset=$(($r/$factor))         # get (0,days) from (0,factor*days)
    # output horrible day/month/year for N days past start date
    date -d "$offset days 1/1/$1" +%d/%m/%Y
done

La boucle interne pour sélectionner le nombre aléatoire tente de corriger le biais. Si une source de nombres aléatoires peut vous donner 0, 1, 2, 3, 4, 5, 6, 7, 8 ou 9 également et que vous voulez un nombre aléatoire entre 0 et 3 inclus, alors si vous obtenez 0 ou 1 de la source vous déclarez 0, si vous obtenez 2 ou 3, alors vous déclarez 1, si vous obtenez 4 ou 5 vous déclarez 2, si vous obtenez 6 ou 7 vous déclarez 3, et si vous obtenez 8 ou 9 vous ignorez ce nombre de la source .

4
icarus