Accueil Perl Histoire

Questions fréquemment posées sur DateTime

1 Généralités sur la FAQ

2 Utilisation élémentaire de DateTime

3 Fuseaux horaires

4 Conversion avec d'autres formats de date et d'heure

5 Groupes et plages de dates : durées, ensembles, plages et ensembles de plages

6 Exemples de calculs de dates

7 Divers

8 Remerciements

9 Copyright

1. Généralités sur la FAQ

1.1. Introduction

Cette FAQ traite des modules Perl de la série DateTime (cf. http://datetime.perl.org/ pour de plus amples renseignements, en anglais). Son but n'est pas de traiter des problèmes de date et d'heure en général, mais seulement de la façon d'utiliser les modules Perl de la famille DateTime .

La version originale de cette FAQ est en élaboration et a fortiori il en va de même pour cette traduction. De nombreux éléments sont énumérés dans le « reste à faire » (todo).

Si vous vous posez des questions qui ne figurent pas dans la FAQ, posez-les (traduites en anglais) à la liste de diffusion.

1.2. Recherche dans les archives de DateTime

Vous pouvez utiliser http://www.google.com/ pour effectuer vos recherches dans les archives de la liste de diffusion (cf. http://datetime.perl.org/mailing_list.html). Toutefois, vous pouvez limiter votre recherche au site http://archive.develooper.com/datetime@perl.org/. Il suffit de copier-coller l'URL ci-dessous dans votre navigateur web puis de remplacer « QUERY » par votre requête : http://www.google.com/search?$q=site%3Aarchive.develooper.com +inurl%3Adatetime%40perl.org%3AQUERY

1.3. Pourquoi utiliser DateTime?

La famille de modules DateTime offre une gestion unifiée des dates et des heures en Perl. Pour connaître l'état de l'art des modules de date et d'heure avant le développement de DateTime, reportez-vous à cet article (en anglais) : http://www.perl.com/pub/a/2003/03/13/datetime.html.

Le résumé de cet article est qu'il existe différents modules dont les fonctionnalités se recouvrent, mais qu'il est difficile de prendre une date provenant d'un module et de la convertir pour l'utiliser dans un autre module, dans le cas où l'on aurait besoin d'une fonction existant dans ce deuxième module.

Avantages :

Inconvénients :

1.4. Où puis-je trouver le calendrier X ?

Si vous avez consulté http://datetime.perl.org/modules.html et que rien ne semble correspondre à votre requête, essayez sur la liste de diffusion. Il manque encore quelques calendriers importants (islamique, hébreu et chinois si je me souviens bien). Et vous aurez notre gratitude si vous en écrivez un (mais vérifiez d'abord auprès de la liste de diffusion que personne n'a commencé à écrire le même que vous).

2. Utilisation élémentaire de DateTime

2.1. Comment utiliser DateTime?

  # C'est la ligne de code essentielle
  use DateTime;

2.2. Comment créer un objet DateTime ?

Il existe différentes méthodes de créer un objet DateTime. Une façon consiste à spécifier chacun des éléments de la date. Tous les paramètres sont facultatifs sauf l'année. Voici la liste des paramètres, avec les valeurs par défaut :

  my $dt1 = 
    DateTime->new( year       => 2003,
                   month      => 1,
                   day        => 1,
                   hour       => 0,
                   minute     => 0,
                   second     => 0,
                   nanosecond => 0,
                   time_zone  => "floating",
                  );

Vous pouvez créer un objet DateTime à partir d'une epoch Unix (c'est-à-dire à partir d'une valeur semblable à ce que renvoie la fonction Perl time()) :

  # Créer un objet DateTime s'appliquant à un instant donné
  my $dt2 = DateTime->from_epoch( epoch => time() );

  # Et pour recalculer l'epoch à partir de l'objet
  my $time = $dt2->epoch();

Cf. DateTime::Format::Epoch si vous voulez une maîtrise plus fine de la conversion de l'epoch.

Pour l'heure donnée par l'horloge système, vous avez une syntaxe simplifiée :

  my $dt3 = DateTime->now();   # La date et l'heure
  my $dt4 = DateTime->today(); # La date seulement

Dans chaque cas, il est possible de spécifier les paramètres facultatifs time_zone (fuseau horaire) et locale (changement de langue). Pour plus de précisions sur le fuseau horaire flottant, cf. 3.1 Qu'est-ce que le fuseau horaire flottant ?. Pour des constructeurs plus évolués, voir la documentation du module DateTime.

2.3. Pourquoi faut-il tronquer les dates ?

Étant donné qu'un objet DateTime représente un instant précis à la nanoseconde près, lorsque vous voulez comparer deux dates, vous devez décider du degré de précision, c'est-à-dire de l'unité de temps à utiliser. Par exemple :

  my $une_date = ...;
  my $maintenant = DateTime->now();

  # Comparaison naïve
  if ($une_date == $maintenant) { # MAUVAIS !
  }

  my $une_date2 = $une_date->clone()->truncate(to => 'days');
  my $maintenant = DateTime->today();  # Comme now() mais tronqué au jour près
  if ($une_date == $maintenant) { # Bon !
  }

Si vous n'avez pas changé les deux objets pour les tronquer au jour près, il y a peu de chances qu'ils coïncident. Bien sûr, si vous voulez savoir si une réunion prévue pour durer une heure est encore en train de se dérouler, il faudrait tronquer à l'heure près. Mais pour ce genre de question, il est préférable d'utiliser un objet intervalle DateTime::Span.

2.4. Pourquoi faut-il cloner les dates ?

Il faut utiliser clone sur une date si vous avez copié la variable et si vous comptez changer la valeur. Étant donné qu'en interne, une date est une référence à une table de hachage, copier la variable ne crée par un nouvel objet date, donc modifier la valeur référencée par une variable revient à modifier la valeur référencée par l'autre variable.

  my $dt1 = DateTime->new( year => 2000 );

  # "copier" la date et changer la "copie"
  $dt2 = $dt1;
  $dt2->set( year => 2003 );

  print $dt1->year();  # Surprise, on obtient 2003

La bonne façon de procéder consiste à utiliser clone pour copier la variable :

  my $dt1 = DateTime->new( year => 2000 );

  # copie la date et change la copie
  $dt2 = $dt1->clone();
  $dt2->set( year => 2003 );

  print $dt1->year();  # on obtient : 2000

2.5. Comment comparer deux dates ?

Il y a quelques façons de faire. Vous pouvez le faire explicitement :

  $dt1 = DateTime->new( year => 1999 );
  $dt2 = DateTime->new( year => 2000 );

  my $cmp = DateTime->compare($dt1, $dt2);
  # $cmp vaut -1, 0, ou 1 selon que $dt1 est <, ==, or > à $dt2 respectivement

Ou bien, en utilisant la syntaxe Perl habituelle :

  if ($dt1 > $dt2)   { $foo = 3; }
  if ($dt1 == $dt2)  { $foo = 4; }
  if ($dt1 < $dt2)   { $foo = 5; }
  my @dates_triees = sort ($dt2, $dt1);

Des problèmes peuvent toutefois se produire si l'un des objets est affecté au fuseau horaire flottant. Cf. 3.1 Qu'est-ce que le fuseau horaire flottant ?

2.6. Comment convertir une date au format MACHIN en un objet DateTime (et vice versa) ?

D'abord, vérifiez s'il existe un module approprié DateTime::Format, ceux-ci ont en général un filtre en entrée et un filtre en sortie qui vous permettent de lire et d'écrire des dates vers des sources externes.

S'il n'existe pas de module approprié, vous pouvez utiliser DateTime::Format::Builder pour constituer sans peine un analyseur syntaxique.

2.7. J'ai imprimé une date avec strftime, comment la réinterpréter ?

Utilisez DateTime::Format::Strptime. Ce module implémente la fonction POSIX strptime qui est la réciproque de strftime. Il existe aussi DateTime::Format::Builder qui a la possibilité de créer un analyseur à partir des formats spécifiques à strptime. La différence entre ces deux modules est que DateTime::Format::Strptime sert à analyser quelques chaînes lors de l'exécution du programme, tandis que DateTime::Format::Builder permet de créer un nouveau module DateTime::Format et peut également enchaîner plusieurs formats qui seront essayés tour à tour, jusqu'à que l'analyse de la chaîne réussisse, ou bien que toutes les définitions ont échoué.

  use DateTime::Format::Strptime;

  my $analyseur =
      DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S' );

  my $dt = $analyseur->parse_datetime( '2003-05-04 12:55:10' );

  # imprime '2003-05-04 12:55:10';
  print $dt->ymd . ' ' . $dt->hms;

2.8. Comment obtenir une chaîne représentant une date?

  my $dt = DateTime->new(year => 1998, month  => 4,  day => 7,
                         hour => 13,   minute => 55);

  # Quelques cas courants
  my $s1 = $dt->date();     # 1998-04-07
  my $s2 = $dt->mdy('|');   # 04|07|1998
  my $s3 = $dt->datetime(); # 1998-04-07T13:55:00
  my $s4 = $dt->time();     # 13:55:00
  my $s5 = $dt->hms('x');   # 13x55x00

  # Vous pouvez utiliser des formats un peu plus exotiques (voir la documentation
  # de DateTime pour les détails).

  # 1998-04-07 01:55:00 PM
  my $s6 = $dt->strftime("%F %r");

  # Tue, 07 Apr 1998 13:55:00 +0000 (RFC 2925) 
  my $s7 = $dt->strftime("%a, %d %b %Y %H:%M:%S %z"); 

2.9. Quel est la plus petite durée de temps représentable ?

DateTime peut représenter des nanosecondes. Vous pouvez créer des objets avec cette résolution en fournissant un paramètre nanosecond aux fonctions new et set et il existe un accesseur nanosecond associé. La valeur du paramètre doit être une valeur entière.

La milliseconde est la millième partie de la seconde (10^-3 ou 0,001). L'abréviation est ms. La microseconde est la millionnième partie de la seconde (10^-6 ou 0,000001). L'abréviation est µs si votre affichage admet les lettres grecques, us sinon. La nanoseconde est la milliardième partie de la seconde (10^-9 ou 0,000000001). L'abréviation est ns.

  # Ci-dessous, la partie fractionnaire des secondes est 0,000000230
  my $dt_ns = DateTime->new(year => 2003, month => 3,   day => 1,
                            hour => 6,    minute => 55, second => 23,
                            nanosecond => 230);
  print "ns: ", $dt_ns->nanosecond, "\n";  # Imprime : "ns: 230\n"

  # En supposant que l'on reçoive un argument en millisecondes
  my $ms = 42;
  my $dt_ms = DateTime->new(year => 2003, month => 3,   day => 1,
                            hour => 6,    minute => 55, second => 23,
                            nanosecond => $ms * 1_000_000);
  print "ns: ", $dt_ms->nanosecond, "\n";  # Imprime : "ns: 42000000\n"

2.10. Comment analyser en une seule fois une date avec plusieurs formats possibles ?

Dû à Iain Truskett

Si votre date peut correspondre à un format parmi plusieurs, vous pouvez utiliser DateTime::Format::Builder pour construire un analyseur unique qui essaie les divers formats en séquence (et vous pouvez même ajouter vos règles si vous le souhaitez) :

  package DateTime::Format::Fall;
  use DateTime::Format::HTTP;
  use DateTime::Format::Mail;
  use DateTime::Format::IBeat;

  use DateTime::Format::Builder (
    parsers => { parse_datetime => [
      sub { eval { DateTime::Format::HTTP->parse_datetime( $_[1] ) } },
      sub { eval { DateTime::Format::Mail->parse_datetime( $_[1] ) } },
      sub { eval { DateTime::Format::IBeat->parse_datetime( $_[1] ) } },
    ] }
  );

Puis, dans un autre package :

  package main;
  for ( '20030719T155345', 'Sat, 19 Jul 2003 15:53:45 -0500', '@d19.07.03 @704' ) {
    print DateTime::Format::Fall->parse_datetime($_)->datetime(), "\n";
  }
  # Imprime : "2003-07-19T15:53:45
  #            2003-07-19T15:53:45
  #            2003-07-19T15:53:45"

2.11. Comment obtenir un objet DateTime à partir d'une chaîne ?

Si vous connaissez le format de la chaîne, vous pouvez utiliser DateTime::Format::Strptime. Cf. 2.7 J'ai imprimé une date avec strftime, comment la réinterpréter ?.

Si vous ne connaissez pas le format, essayez avec DateTime::Format::HTTP, qui parvient à analyser un bon nombre de formats date-heure communément rencontrés.

TODO Talk about eval if needed...

3. Fuseaux horaires

3.1. Qu'est-ce que le fuseau horaire flottant ?

Le fuseau horaire flottant correspond au cas où vous ne savez pas quel est le véritable fuseau horaire associé à l'objet DateTime, ou bien au cas où vous n'avez pas besoin de vous intéresser aux fuseaux horaires. Si vous comparez une heure flottante avec une heure associée à un vrai fuseau horaire, que ce soit avec la méthode compare ou avec une comparaison surchargée (==, etc.), alors le fuseau flottant est assimilé à l'autre fuseau dans le cadre de la comparaison :

 my $dt1 = DateTime->new(year => 2002, month  => 4,  day => 7,
                         hour => 13,   minute => 55,                         
                         time_zone => 'America/New_York');
 my $dt2 = DateTime->new(year => 2002, month  => 4,  day => 7,
                         hour => 13,   minute => 55,                         
                         time_zone => 'America/Los_Angeles');
 my $dt_float = 
           DateTime->new(year => 2002, month  => 4,  day => 7,
                         hour => 13,   minute => 55,                         
                         time_zone => 'floating');

 print "date fixe 1 == date flottante\n" if $dt1 == $dt_float;
 print "date fixe 2 == date flottante\n" if $dt2 == $dt_float;
 print "date fixe 1 != date fixe 2\n"  if $dt1 != $dt2;

Si vous voulez traiter le fuseau flottant comme s'il s'agissait du fuseau horaire UTC (c'est-à-dire avec un décalage horaire nul), alors utiliser la méthode de classe compare_ignore_floating à la place.

Toutefois, étant donné que le résultat de la comparaison dépend des différents fuseaux horaires lorsque vous comparez des dates flottantes avec des dates appartenant à des fuseaux horaires différents, cela risque de perturber fortement la fonction sort(). Dans ce cas, soit vous convertissez toutes les heures flottantes vers un fuseau fixe, soit vous utilisez la méthode de classe compare_ignore_floating dans le bloc de comparaison du tri, de manière que tous les fuseaux flottants soient assimilés au fuseau UTC.

Sauf si vous savez exactement ce que vous faites, vous n'avez aucun intérêt à mélanger les fuseaux horaires flottants avec les fuseaux fixes. Convertissez toujours les heures flottantes vers le fuseau horaire approprié (il vous faudra décider si la valeur correcte est le fuseau local, UTC ou quelque chose d'autre) :

  # Convertit toutes les heures flottantes vers le fuseau $time_zone
  # Arguments :
  #  $dates est la référence à un tableau de DateTime
  #  $time_zone est soit une chaîne, soit un objet DateTime::TimeZone
  #  $clone indique s'il faut cloner les éléments de la liste ou pas
  # Renvoie : la référence à un tableau contenant les DateTime nettoyés (notez que le
  #   tableau source est modifié si vous n'avez pas spécifié un $clone à « vrai »)
  sub unfloat_dates {
      my ($dates, $time_zone, $clone) = @_;
      $time_zone = "UTC" unless $time_zone;

      my @clean_dates = ();
      foreach my $d (@$dates) {
          $d = $d->clone() if $clone;
          $d->set_time_zone($time_zone)
              if $d->time_zone()->is_floating();
          push @clean_dates, $d;
      }
      
      return \@clean_dates;
  }

  my %time = (year => 2003, month  => 3,  day => 1,
              hour => 1,    minute => 32);
  my @dates = 
      (DateTime->new(%time, time_zone => "America/New_York"),
       DateTime->new(%time, time_zone => "floating"),
       DateTime->new(%time, time_zone => "UTC"),
       );
       
  if ($dates[0] == $dates[1] and $dates[2] == $dates[1]) {
      # La condition est vérifiée
      print "L'heure flottante est égale aux deux autres\n";
  }

  unfloat_dates(\@dates, "UTC", 0);

  if ($dates[0] != $dates[1] and $dates[2] == $dates[1]) {
      # Cette nouvelle condition est vérifiée
      print "L'heure flottante est maintenant une heure UTC\n";
  }

Par exemple, MySQL ne stocke pas les informations de fuseau horaire avec les dates, de sorte que DateTime::Format::MySQL renvoie des dates générées avec un fuseau horaire flottant. C'est à l'utilisateur de savoir à quel fuseau horaire l'objet DateTime appartient. Nous espérons que les développeurs du système basé sur MySQL ont réfléchi à la question est qu'ils utilisent des dates appartenant au même fuseau horaire.

Veuillez également noter que si le fuseau horaire de l'objet est le fuseau horaire flottant, alors les calculs sur cet objet ne tiennent pas compte des secondes intercalaires, car si l'on ne connaît pas le fuseau horaire, il est impossible de savoir quand s'appliquent les secondes intercalaires.

Vous serez amenés à utiliser le fuseau flottant comme étape intermédiaire si vous voulez changer un objet de fuseau horaire sans modifier l'heure locale. Cf. 3.3 Comment changer de fuseau horaire sans modifier l'heure locale ?.

3.2. Si je connais mon fuseau horaire, comment déterminer l'heure locale dans un autre fuseau ?

  my $source   = DateTime->new(year => 1998, month  => 4,  day => 7,
                             hour => 13,   minute => 55,
                             time_zone => 'America/New_York');
  my $resultat = $source->clone()
                        ->set_time_zone( 'America/Los_Angeles' );
  print $source->strftime("%F %r %Z"), " devient ", 
        $resultat->strftime("%F %r %Z");
  # imprime : 1998-04-07 01:55:00 PM EDT devient 1998-04-07 10:55:00 AM PDT

3.3. Comment changer de fuseau horaire sans modifier l'heure locale ?

Changez d'abord le fuseau horaire pour adopter le fuseau flottant (cf. 3.1 Qu'est-ce que le fuseau horaire flottant ?) sinon le temps affiché serait ajusté (en conservant la même heure interne, au lieu de conserver la même heure locale).

  my $source   = DateTime->new(year => 1998, month  => 4,  day => 7,
                             hour => 13,   minute => 55,
                             time_zone => 'America/New_York');
  my $resultat = $source->clone()
                        ->set_time_zone( 'floating' )
                        ->set_time_zone( 'America/Los_Angeles'    );
  print $source->strftime("%F %r %Z"), " devient ", 
        $result->strftime("%F %r %Z");
  # imprime : 1998-04-07 01:55:00 PM EDT devient 1998-04-07 01:55:00 PM PDT

3.4. Pourquoi DateTime refuse-t-il le fuseau EST ?

En fait, il existe plusieurs fuseaux EST... l'un aux États-Unis, l'autre en Australie. Si vous voulez le fuseau horaire américain, spécifiez EST5EDT, ou mieux, America/New_York.

Les noms abrégés des fuseaux horaires ne sont pas discriminants. Toute tentative pour déterminer le fuseau horaire en fonction du nom abrégé implique une certaine dose de devinette. Utilisez plutôt les noms complets.

4. Conversion avec d'autres formats de date et d'heure

4.1. Comment convertir entre les epochs et les objets DateTime ?

DateTime procure un constructeur from_epoch(...) qui reçoit un argument epoch dont la valeur est le nombre de secondes depuis la date origine. La définition exacte de cette date origine dépend du système, mais si vous utilisez une fonction time() ou assimilée pour obtenir cette valeur, le constructeur fonctionnera correctement. En outre, notez que ce constructeur admet également les paramètres time_zone et locale.

Cf. DateTime::Format::Epoch pour une maîtrise plus fine de la définition exacte de la date origine à utiliser. Vous pouvez également avoir besoin de ce module si vous importez une valeur epoch d'un autre système. Par exemple, la date origine sous Unix est fixée au 1er janvier 1970 à 00:00:00 et la valeur epoch est le nombre de secondes depuis cet instant, sans tenir compte des secondes intercalaires.

Pour connaître la date origine associée à un objet DateTime, utilisez la méthode epoch(). Si vous la voulez avec une haute précision incluant les nanosecondes, utilisez hires_epoch().

Lorque vous créez un objet DateTime à partir d'une valeur epoch, le fuseau horaire est implicitement fixé à 'UTC', à moins que vous n'ayez spécifié un paramètre time_zone explicitement.

  my $time = 1057632876;
  my $dt = DateTime->from_epoch(epoch => $time);
  print $dt->datetime(), "\n";

  my $epoch = $dt->epoch();
  print $dt->epoch(), "\n";

4.2. Comment convertir les valeurs entre les fonctions internes de Perl et DateTime ?

Cf. 4.1 Comment convertir entre les epochs et les objets DateTime ? pour la solution. Utilisez la fonction time() pour obtenir une valeur epoch ou le module Time::Local si vous avez la liste de valeurs renvoyées par gmtime ou localtime.

4.3. Comment convertir les dates et heures entre les formats POSIX et DateTime ?

Cf. 4.1 Comment convertir entre les epochs et les objets DateTime ? pour la solution.

4.4. Comment convertir les dates et heures entre les formats Time::HiRes et DateTime ?

Cf. 4.1 Comment convertir entre les epochs et les objets DateTime ? pour la solution. Notez que la méthode from_epoch(...) de DateTime conservera le nombre de nanosecondes, mais il faut utiliser hires_epoch() pour la conversion inverse.

4.5. Comment convertir les dates et heures entre la famille TimeDate et la famille DateTime ?

Cf. 3.1 Qu'est-ce que le fuseau horaire flottant ? pour la façon de faire.

4.6. Comment convertir entre les valeurs de Time::Piece et les objets DateTime ?

Cf. 4.1 Comment convertir entre les epochs et les objets DateTime ? pour la solution de base. Toutefois, il faudra appeler la méthode epoch de Time::Piece pour obtenir la valeur epoch de l'objet. La création d'un objet Time::Piece s'effectue en appelant gmtime ou localtime (tous deux surchargés par Time::Piece) avec la valeur retournée par la méthode epoch() de DateTime.

  use Time::Piece;

  my $lt1 = localtime(1057632876);
  my $dt = DateTime->from_epoch(epoch => $lt1->epoch());
  print $dt->datetime(), "\n"; # Imprime 2003-07-08T02:54:36

  my $lt2 = gmtime($dt->epoch());
  print $lt2, "\n";  # Imprime Tue Jul  8 02:54:36 2003

4.7. Comment convertir les valeurs entre les modules Date::Manip et DateTime ?

Utilisez le module DateTime::Format::DateManip. À noter que ce module permet également de convertir des durées (durations) du module Date::Manip.

  use DateTime::Format::DateManip;
  use Date::Manip;

  my $dt = DateTime::Format::DateManip->parse_datetime
               ("Jan 1st, 2001 12:30 AM GMT");
  my $dm = DateTime::Format::DateManip->format_datetime($dt);

  # De même, il faut utiliser l'epoch pour les comparaisons parce que
  # Date::Manip stocke les heures dans le fuseau horaire local
  my $ep = UnixDate($dm, "%s");
  is($ep,
     '978309000',
     "DateTime::Format::Manip->format_datetime()");

4.8. Comment convertir les dates entre les modules Date::Calc et DateTime ?

C'est un peu délicat parce que Date::Calc vous permet d'utiliser des heures GMT et des heures locales, mais vous devez vous-mêmes savoir quel type d'heure est stocké dans tel ou tel objet. La meilleure façon de procéder est d'utiliser les fonctions Date_To_Time(...) et Mktime(...) pour convertir une date-heure GMT ou une date-heure locale en une epoch, puis de continuer comme indiqué dans la recette 4.1 Comment convertir entre les epochs et les objets DateTime ?.

Pour convertir un objet DateTime en une valeur Date::Calc, utiliser la fonction Gmtime() ou Localtime() en lui envoyant le resultat de epoch() de DateTime, en fonction du type d'heure que vous voulez.

S'il s'agit d'utiliser un Date::Calc::Object, la démarche est similaire, seule la syntaxe d'appel change.

5. Groupes et plages de dates : durées, ensembles, plages et ensembles de plages

5.1. Qu'est-ce qu'un objet DateTime::Duration?

Un objet DateTime::Duration représente une durée. Vous obtenez un objet DateTime::Duration lorsque vous effectuez une soustraction entre deux objets DateTime et vous pouvez ajouter un objet DateTime::Duration à un objet DateTime existant pour obtenir un nouvel objet DateTime.

Un objet DateTime::Duration se décompose en durées élémentaires, étant donné que l'ajout de 31 jours n'est pas équivalent à l'ajout d'un mois, ou bien que l'ajout d'une minute n'est pas équivalent à l'ajout de 60 secondes, en raison des minutes comportant une seconde intercalaire (cf. 7.4 Secondes intercalaires, passage à l'heure d'été).

  use DateTime::Duration;
  
  # TODO Think up a good example, we already do age above

5.2. Pourquoi existe-t-il 3 modes pour end_of_month dans DateTime::Duration ?

Les trois modes permettent de choisir le traitement à effectuer si l'addition d'un certain nombre de mois ou d'années fait « déborder » le mois. Donc, si vous avez :

  use DateTime::Duration();
  
  sub test_duration_mode {
      my ($dt, $mode) = @_;

      my $dur = DateTime::Duration->new
                  (years => 1, end_of_month => $mode);
      my $res = $dt + $dur;

      print $res->ymd(), "\n";
  }

  my $dt1 = DateTime->new(year => 2000, month => 2, day => 29);
  my $dt2 = DateTime->new(year => 2003, month => 2, day => 28);

  # "wrap" reporte l'excédent sur le mois suivant
  test_duration_mode($dt1, "wrap");     # Imprime "2001-03-01\n"

  # "limit" empêche le report de l'excédent...
  test_duration_mode($dt1, "limit");    # Imprime "2001-02-28\n"

  # ... mais oublie 3 ans plus tard que l'on était en fin de mois 
  test_duration_mode($dt2, "limit");    # Imprime "2004-02-28\n"

  # "preserve" fait "adhérer" la valeur à la fin du mois...
  test_duration_mode($dt1, "preserve"); # Imprime "2001-02-28\n"

  # ...même si l'addition proprement dite ne serait pas allée jusque-là
  test_duration_mode($dt2, "preserve"); # Imprime "2004-02-29\n"

Si vous voulez calculer quelque chose du genre « deux jours avant la fin du mois », il vaut mieux utiliser une récurrence :

  # From Flavio Glock
  $set = DateTime::Event::Recurrence->monthly( days => -2 );
  print "Occurrence suivante ", $set->next( $dt )->datetime;

5.3. Qu'est-ce qu'un objet DateTime::Set ?

Un objet DateTime::Set permet de représenter de façon efficace un certain nombre d'objets DateTime. Vous pouvez créer un ensemble à partir d'une liste d'objets DateTime existants :

  use DateTime::Set;

  my $dt1  = DateTime->new(year => 2003, month => 6, day => 1);
  my $dt2  = DateTime->new(year => 2003, month => 3, day => 1);
  my $dt3  = DateTime->new(year => 2003, month => 3, day => 2);
  
  my $ens1 = DateTime::Set->from_datetimes( dates => [ $dt1, $dt2 ] );
  $ens1 = $ens1->union($dt3);  # Insère une autre date

  print "Le minimum de l'ensemble est la date la plus ancienne\n" if $dt2 == $ens1->min();
  print "Le maximum de l'ensemble est la date la plus récente\n"  if $dt1 == $ens1->max();

  my $it = $ens1->iterator();
  while ( my $dt = $it->next() ) {
    print $dt->ymd(), "\n";
  }
  # Imprime "2003-03-01\n2003-03-02\n2003-06-01\n"

DateTime::Set peut également traiter des ensembles qui ne sont pas énumérés entièrement. Par exemple, vous pouvez créer l'ensemble de tous les premiers jours du mois :

  my $set = DateTime::Set->from_recurrence(
              recurrence => sub {
                  $_[0]->truncate( to => 'month' )->add( months => 1 )
              });
  my $dt1 = DateTime->new(year => 2003, month => 3, day => 1);
  my $dt2 = DateTime->new(year => 2003, month => 2, day => 11);
  print "2003-03-01 est le premier jour du mois\n"
      if $set->contains($dt1);
  print "2003-02-11 n'est pas le premier jour du mois\n"
      unless $set->contains($dt2);

5.4. Comment obtenir les dates appartenant à un DateTime::Set ?

Vous pouvez utiliser contains() pour savoir si une date donnée appartient à l'ensemble, comme c'est montré dans 5.3 Qu'est-ce qu'un objet DateTime::Set ? ou bien, vous pouvez utiliser un itérateur pour boucler sur toutes les valeurs de l'ensemble.

Pour boucler sur les éléments d'un ensemble, assurez-vous d'abord que la date de début de cet ensemble est définie (et si vous souhaitez que la boucle s'arrête, assurez-vous également que la date de fin est définie). Si votre ensemble n'est a pas encore, vous pouvez créer soit un objet DateTime::Set soit un objet DateTime::Span et prendre l'intersection avec votre ensemble. Dans un but pratique, la méthode iterator() admet les mêmes arguments que DateTime::Span et les utilise pour limiter la boucle, tout se passant comme si vous aviez utilisé un objet DateTime::Span.

Dans l'exemple ci-dessous, nous utilisons un objet DateTime::Event::Recurrence pour définir plus aisément une récurrence mensuelle équivalente à celle que nous avons défini « à la main » en 5.3 Qu'est-ce qu'un objet DateTime::Set ?.

  use DateTime::Event::Recurrence;

  my $set = DateTime::Event::Recurrence->monthly();
  my $dt1 = DateTime->new(year => 2003, month => 3, day => 2);
  my $dt2 = DateTime->new(year => 2003, month => 6, day => 1);

  # Itérateur illimité sur un ensemble infini
  my $it1 = $set->iterator();
  print $it1->next(), "\n";  # Imprime "-inf\n"

  # Itérateur borné sur un ensemble infini
  my $it2 = $set->iterator(start => $dt1, end => $dt2);
  while ( $dt = $it2->previous() ) {
    print $dt->ymd(), "\n";
  }
  # Imprime "2003-06-01\n2003-05-01\n2003-04-01\n"

Dans l'exemple ci-dessus, nous avons utilisé la méthode previous() pour boucler sur l'ensemble par valeurs décroissantes.

Vous pouvez également convertir votre DateTime::Set en une simple liste d'objets DateTime avec la méthode as_list. Il vaut mieux ne pas y avoir recours, car la représentation interne de DateTime::Set est nettement plus efficace.

5.5. Quelles sont les opérations disponibles sur les objets DateTime::Set ?

L'une des fonctionnalités les plus importantes de DateTime::Set est que vous pouvez effectuer des opérations ensemblistes. Par exemple, vous pouvez créer l'ensemble de tous les premiers jours du mois et l'ensemble de tous les lundis, effectuer l'intersection des deux ensembles et vous obtenez tous les mois commençant par un lundi :

  use DateTime::Event::Recurrence;

  # Premier jour du mois
  my $fom = DateTime::Event::Recurrence->monthly();

  # Tous les lundis (premier jour de la semaine)
  my $mon = DateTime::Event::Recurrence->weekly( days => 1 );

  # Tous les lundis qui tombent le premier jour du mois
  my $set = $fom->intersection($mon);
  
  my $it = $set->iterator
             (start  =>
              DateTime->new(year => 2003, month => 1, day => 1),
              before =>
              DateTime->new(year => 2004, month => 1, day => 1));

  while ( my $dt = $it->previous() ) {
    print $dt->ymd(), "\n";
  }
  # Imprime "2003-12-01\n2003-09-01\n"

Voici la liste complète des opérations ensemblistes :

Un dernier opérateur, le complément unaire $ens3 = $ens1->complement() renvoie un objet DateTime::SpanSet contenant tous les éléments n'appartenant pas à $ens1.

5.6. Existe-t-il un moyen plus simple que d'écrire des sous-programmes, pour créer des ensembles ?

Les modules suivants permettent de créer des récurrences de façon pratique.

5.7. Qu'est-ce qu'un objet DateTime::Span ?

Un objet « intervalle » (DateTime::Span) représente un événement qui sétale dans le temps, alors qu'un objet DateTime se produit à un instant ponctuel (quoique, des objets DateTime peuvent représenter des durées s'ils sont tronqués par truncate à la même unité de temps, cf. Pourquoi faut-il tronquer les dates ?). À l'inverse des objets DateTime::Duration, les objets DateTime::Span ont un début et une fin fixés.

Par exemple, un intervalle peut représenter une réunion planifiée du 2003-03-03 12:00:00 au 2003-03-03 13:00:00. L'intervalle inclut tous les instants compris dans cette heure. Quant au point de début et au point final, vous choisissez s'ils sont inclus dans l'intervalle à sa création.

  use DateTime;
  use DateTime::Span;

  my $start = DateTime->new( year => 2003, month => 3, day => 3, hour => 12 );
  my $before = DateTime->new( year => 2003, month => 3, day => 3, hour => 13 );

  # le début est inclus mais pas la fin
  my $reunion =
      DateTime::Span->from_datetimes( start => $start, before => $before );

  my $dt = DateTime->new( year => 2003, month => 3, day => 3,
                          hour => 12, minute => 15 );

  print "Je serai occupé à ce moment-là" if $reunion->contains($dt);

5.8. Qu'est-ce qu'un objet DateTime::SpanSet ?

Note du traducteur : je n'ai pas traduit ce paragraphe car je pense qu'il contient des fautes de frappe et je n'ai pas regardé la meilleure façon de les corriger.

Un objet « ensemble d'intervalles » (DateTime::SpanSet) représente, comme on peut s'y attendre, un ensemble d'objets intervalles (DateTime::Span). Par exemple, pour représenter une semaine type de travail de 9 heures à 17 heures, du lundi au vendredi, avec une pause déjeuner de midi à 13 heures, utilisez ceci :

  ###### A VERIFIER !
  use DateTime::Event::Recurrence;
  use DateTime::SpanSet;

  # Make the set representing the work start times: M-F 9:00 to 12:00
  my $start = DateTime::Event::Recurrence->weekly
               ( days => [1 .. 5], hours => [8, 12] );
  # Make the set representing the work end times: M-F 13:00 to 17:00
  my $end   = DateTime::Event::Recurrence->weekly
               ( days => [1 .. 5], hours => [13, 17] );

  # Build a spanset from the set of starting points and ending points
  my $spanset = DateTime::SpanSet->from_sets
                  ( start_set => $start,
                    end_set   => $end );

  # Iterate from Thursday the 3rd to Monday the 6th 
  my $it = $spanset->iterator
             (start  =>
              DateTime->new(year => 2003, month => 1, day => 3),
              before =>
              DateTime->new(year => 2003, month => 1, day => 7));

  while (my $span = $it->next) {
      my ($st, $end) = ($span->start(), $span->end());
      print $st->day_abbr, " ", $st->hour, " to ", $end->hour, "\n";
  }
  # Imprime "Fri 8 to 12\nFri 13 to 17\nMon 8 to 12\nMon 13 to 17\n"

  # Now see if a given DateTime falls within working hours
  my $dt = DateTime->new(year => 2003, month => 2, day => 11, hour => 11);
  print $dt->datetime, " is a work time\n"
      if $spanset->contains( $dt );

6. Exemples de calculs de dates

6.1. Comment vérifier qu'une date donnée se trouve entre deux dates ?

  my $dt1  = DateTime->new(year => 2002, month => 3, day => 1);
  my $dt2  = DateTime->new(year => 2002, month => 2, day => 11);
  my $date = DateTime->new(year => 2002, month => 2, day => 23);

  # S'assurer que $dt1 est antérieure à $dt2
  ($dt1, $dt2) = ($dt2, $dt1) if $dt1 > $dt2;

  # Tronquer les dates au jour (ne pas faire si vous souhaitez comparer
  # les heures précises)
  $dt1->truncate(  to => 'day' );
  $dt1->truncate(  to => 'day' );
  $date->truncate( to => 'day' );

  # Et maintenant, comparer
  if ($dt1 <= $date and $date <= $dt2) {
    print '$date est comprise entre les deux autres dates';
  }

Ou bien, vous pouvez avoir recours à un intervalle DateTime::Span :

  use DateTime::Span;

  my $dt1  = DateTime->new(year => 2002, month => 3, day => 1);
  my $dt2  = DateTime->new(year => 2002, month => 2, day => 11);
  my $date = DateTime->new(year => 2002, month => 2, day => 23);

  # S'assurer que $dt1 est antérieure à $dt2
  ($dt1, $dt2) = ($dt2, $dt1) if $dt1 > $dt2;

  # Construire l'intervalle (start et end donneront des comparaisons >= et <=
  # si vous voulez les comparaisons > et <, utiliser before et after à la place)
  my $intervalle = DateTime::Span->from_datetimes(start => $dt1,
                                                  end   => $dt2);
  if ($intervalle->contains($date)) {
    print '$date est comprise entre les deux autres dates';
  }

Cf. également 2.3 Pourquoi faut-il tronquer les dates ?

6.2. Comment vérifier que l'intervalle entre deux dates-heures est inférieur ou supérieur à une durée donnée ?

  use DateTime::Duration;

  my $dt1  = DateTime->new(year => 2002, month => 3, day => 1);
  my $dt2  = DateTime->new(year => 2002, month => 2, day => 11);

  # Contruire la durée à comparer aux deux dates
  $duree =
    DateTime::Duration->new( days => 19, hours => 3, minutes => 12);
  
  sub dates_proches {
      my ($dt1, $dt2, $duree) = @_;

      # S'assurer que $dt1 est antérieure à $dt2
      ($dt1, $dt2) = ($dt2, $dt1) if $dt1 > $dt2;

      # On soustrait la durée à la date de fin et on compare le
      # résultat à la date de début. Si le résultat est antérieur,
      # cela signifie que les dates sont proches.
      if ($dt2 - $duree < $dt1) {
          return 1;
      } else {
          return 0;
      }
  }

  print 'plus rapprochées que $duree'
     if dates_proches($dt1, $dt2, $duree);

6.3. Comment vérifier si une personne a un certain âge ?

C'est juste un cas particulier de 6.2 Comment vérifier que l'intervalle entre deux dates-heures est inférieur ou supérieur à une durée donnée ?

Remarquez que soustraire les deux dates et regarder la composante year ne fonctionne pas. Cf. ??????

  # Date (et heure) de naissance
  my $naissance = DateTime->new(year => 1974, month  => 2, day => 11,
                                hour => 6,    minute => 14);

  # Comme l'anniversaire ne se fête pas à la minute près, on arrondit la date au jour
  $naissance->truncate( to => 'day' );
  my $aujourdhui = DateTime->today();

  # L'âge demandé
  my $age_18 = DateTime::Duration->new( years => 18 );

  print "Vous avez le droit de vote."
     unless within_interval($naissance, $aujourdhui, $age_18);

6.4. Comment calculer pour une date quel est le numéro de la semaine (relativement au mois) ?

Exemple:

            Avril 1998
    Lun Mar Mer Jeu Ven Sam Dim
              1   2   3   4   5  =  semaine 1
      6   7   8   9  10  11  12  =  semaine 2
     13  14  15  16  17  18  19  =  semaine 3
     20  21  22  23  24  25  26  =  semaine 4
     27  28  29  30              =  semaine 5

  # Arguments en entrée :
  #  - La date
  #  - Le jour que nous considérons en début de semaine (1 pour lundi, 7 pour dimanche)
  #    (facultatif)
  sub get_week_num {
    my $dt            = shift;
    my $start_of_week = shift || 1;

    # Par quel jour commence le mois ? (1 = lundi, etc)
    my $premier = $dt->clone();
    $premier->set(day => 1);
    my $wday  = $premier->day_of_week();

    # On ramène ce numéro au début de la semaine (0 = début, 6 = dernier jour de la semaine)
    $wday = ($wday - $start_of_week + 7) % 7;

    # Puis on fait le calcul pour obtenir le numéro de la semaine
    my $mday  = $dt->day_of_month_0();

    return int ( ($mday + $wday) / 7 ) + 1;
  }

6.5. Comment savoir si un jeudi est le premier, deuxième, troisième, quatrième ou cinquième jeudi du mois ?

  # Argument en entrée : la date
  # en fait, la formule est indépendant du jour de la semaine,
  # cela fonctionne aussi bien pour les lundis et les dimanches
  sub get_day_occurrence {
    my $dt  = shift;
    return int( $dt->day_of_month_0() / 7 + 1 );
  }

6.6. Comment déterminer le mercredi de la semaine correspondant à la date du jour ?

  # Arguments en entrée
  #  - La date
  #  - Le jour souhaité (1 = lundi, 7 = dimanche)
  #  - Le jour qui, selon vous, est le premier de la semaine (1 = lundi, 7 = dimanche)
  #    (facultatif)
  # NOTE : le résultat peut très bien se trouver dans un mois différent
  sub get_day_in_same_week {
    my $dt            = shift;
    my $cible         = shift;
    my $debut_semaine = shift || 1;

    # Quel est le rang de la date source dans la semaine (0..6) ?
    my $wday = ($dt->day_of_week() - $debut_semaine + 7) % 7;

    # Quel est le rang du jour cible dans la semaine (0..6) ?
    $cible = ($cible - $debut_semaine + 7) % 7;

    # Ajuste la date source pour correspondre au jour cible
    return $dt->clone()->add(days => $cible - $wday);
  }

6.7. Comment déterminer le samedi précédent et le samedi suivant pour une date donnée ?

  # La date et la cible (1 = lundi, 7 = dimanche)
  my $dt = DateTime->new(year => 1998, month => 4, day => 3); # vendredi
  my $cible = 6; # samedi

  # Obtient le jour de la semaine pour la date
  my $jour = $dt->day_of_week();
  
  # Applique les corrections
  my ($prec, $suiv) = ($dt->clone(), $dt->clone());

  if ($jour == $cible) {
      $prec->add( days => -7 );
      $suiv->add( days =>  7 );
  } else {
      my $correction = ( $cible - $jour + 7 ) % 7;
      $prec->add( days => $correction - 7 );
      $suiv->add( days => $correction );
  }

  # $prec est le 1998-03-28, $suiv est le 1998-04-04

6.8. Comment déterminer le dernier jour ouvré du mois (c'est la paye !) ?

On se place à la fin du mois et on recule d'un jour tant que l'on se trouve sur un samedi ou un dimanche.

  my $dt = DateTime->last_day_of_month( year => 2003, month => 8 );

  # 6 = samedi, 7 = dimanche
  while ( $dt->day_of_week >= 6 ) { $dt->subtract( days => 1 ) }

  print "Vous serez payés le ", $dt->ymd, "\n";

Ce n'est peut-être pas la solution la plus efficace, mais elle est simple à comprendre.

6.9. Comment savoir quel jour tombe le 2e mercredi du mois ?

Note du traducteur : la version originale parlait du 3e vendredi du mois, mais pour le groupe Paris.PM, c'est le 2e mercredi.

  # Définit les caractéristiques de la réunion 
  my $jour_reunion  = 3; # (1 = lundi, 7 = dimanche)
  my $semaine_reunion = 2;
  my $dt = DateTime->new(year => 2004, month => 6, day => 18);

  # Repositionne au premier jour du mois demandé
  my $resultat = $dt->clone()->set( day => 1 );

  # Ajuste le résultat au jour demandé puis ajuste la semaine
  my $dow = $resultat->day_of_week();
  $resultat->add( days => ( $jour_reunion - $dow + 7 ) % 7,
                weeks => $semaine_reunion - 1 );

  # Vérifie que l'on n'est pas passé au mois suivant
  die "Aucune date ne correspond ce mois-ci"
     if $dt->month() != $result->month();

  # $resultat vaut maintenant 2004-06-09

6.10. Comment itérer sur une plage de dates ?

Cette recette considère que vous avez deux dates et que vous voulez effectuer une boucle de l'une à l'autre. Une autre façon de procéder consiste à créer un ensemble (DateTime::Set) et à effectuer une itération sur cet ensemble.

  my $debut_dt = DateTime->new(year => 1998, month  => 4,  day => 7);
  my $fin_dt   = DateTime->new(year => 1998, month  => 7,  day => 7);

  my $semaines = 0;
  for (my $dt = $debut_dt->clone();
       $dt <= $fin_dt;
       $dt->add(weeks => 1) )
  {
    $semaines++;
  }

6.11. Comment créer la liste des dates dans une certaine plage ?

Il existe quelques façons de le faire : créer une liste d'objets DateTime, créer un ensemble DateTime::Set qui représente la liste ou simplement effectuer une boucle comme à la question 6.10 Comment itérer sur une plage de dates ?.

De ces trois possibilités, l'itération simple est probablement la plus rapide, mais vous ne pouvez pas facilement passer la liste à d'autres parties de votre traitement. Si vous avez besoin de transmettre une liste de dates, alors il faut utiliser DateTime::Set car les dates ne sont pas générées à la création de l'objet, elles sont générées au fur et à mesure des besoins. D'autre part, il est très facile de compléter ou de filtrer la liste. Cf. 5.3 Qu'est-ce qu'un objet DateTime::Set ?.

  # Une liste Perl
  my $debut_dt = DateTime->new(year => 1998, month  => 4,  day => 7);
  my $fin_dt   = DateTime->new(year => 1998, month  => 7,  day => 7);

  my @liste = ();
  for (my $dt = $debut_dt->clone();
       $dt <= $fin_dt;
       $dt->add(weeks => 1) )
  {
    push @liste, $dt->clone();
  }
  
  # Un ensemble DateTime::Set.  Nous utilisons DateTime::Event::Recurrence car cela
  # facilite la création des ensembles (Cf. également DateTime::Event::ICal si vos
  # ensembles sont plus compliqués)
  use DateTime::Event::Recurrence;
  use DateTime::Span;
  my $set = DateTime::Event::Recurrence->daily(start    => $debut_dt,
                                               interval => 7);
  $set = $set->intersection(DateTime::Span->from_datetimes
                                (start => $debut_dt, end => $fin_dt ));

6.12. Comment calculer le nombre de jours entre deux dates, à l'exclusion des samedis et des dimanches ?

TODO

6.13. Comment obtenir la date de la veille ?

     my $dt = DateTime->now()->subtract( days => 1 );
     print $dt->ymd;

7. Divers

7.1. Comment faire un calcul sur les jours ouvrés ?

TODO

Par exemple, dans trois jours ouvrés...

7.2. Comment traduire les dates dans une autre langue ?

DateTime.pm est prévu pour intégrer les modules DateTime::Locale et adapter ainsi la sortie aux us et coutumes locaux ou nationaux. De plus, le module DateTime::Format::Strptime utilise lui aussi ces modules pour adapter l'analyse des chaînes aux particularités locales.

     # fr_FR = French (France)
     my $dt = DateTime->new( year => 2000, month => 3, locale => 'fr_FR' );

     print $dt->month_name;

7.3. Que signifient GMT, TAI, UTC et UT1 ?

Explications fournies par Flavio Glock (fglock at pucrs dot br), au cours d'une discussion entre Peter J. Acklam, Flavio Glock, John Peacock, Eugene Van Der Pijll et d'autres

Avant 1972, la référence du « temps international » était le GMT. En GMT, tous les jours ont le même nombre de secondes. Un jour commence à « minuit » et il dure 86400 secondes. La durée de la seconde était sujette à variation, puisqu'elle dépendait d'observations astronomiques.

Le TAI est un autre système de mesure du temps, dans lequel les secondes dépendent d'un « temps atomique » uniquement, pas de la position relative de la Terre et du Soleil. Un jour TAI dure exactement 86400 secondes TAI et ce système a pour origine le 1er janvier 1958.

En parallèle avec ceux-ci existe le temps UT1, le « temps astronomique ». Le temps UT1 dépend uniquement de la position du Soleil et de la Terre. La différence UT1 - TAI a augmenté au fil du temps, étant donné que la rotation de la Terre ralentit et que la longueur du jour a augmenté en conséquence. La différence entre les deux est un nombre flottant.

En 1972, le temps UTC a été introduit pour avoir une approximation entre le « temps international » et le « temps astronomique ». Maintenant, chaque fois que la différence entre UTC et UT1 dépasse un certain seuil, on introduit une seconde intercalaire. Le temps UTC est synchronisé avec le TAI, c'est-à-dire que la différence UTC - TAI est un nombre entier de secondes. La différence UTC - UT1 est une fraction de seconde.

Le module DateTime se réfère au temps UTC, sauf lorsque le fuseau horaire utilisé est le fuseau flottant.

7.4. Secondes intercalaires, passage à l'heure d'été

TODO Explain why some days have 23 or 25 hours, and so on.

7.5. Conversion en chaîne (Stringificiation)

TODO Explain how to stringify

TODO Other Modules that are useful

8. Remerciements

Un grand remerciement à Dave Rolsky pour avoir été suffisamment dingue pour écrire le module DateTime pour commencer et pour avoir dirigé le reste de l'asile de fous et les avoir incités à faire quelque chose de super.

Un grand remerciement également aux autres fous de l'asile (Flavio Glock, Rick Measham, Iain Truskett, Eugene van der Pijll, Claus Färber, Kellan Elliot-McCrea, Daniel Yacob, Jean Forget, Joshua Hoblitt, Matt Sisk, Ron Hill et de nombreux autres), pour avoir travaillé sur ce fabuleux projet et m'avoir supporté avec mes questions saugrenues.

Merci à Steffen Beyer pour avoir écrit le module Date::Calc et la documentation POD qui l'accompagne, étant donné que j'ai pompé les premières questions sur cette documentation (et merci à Ron Hill qui m'a suggéré de le faire).

9. Copyright

Copyright (C) 2003, Benjamin Bennett pour le texte original. Tous droits réservés.

Copyright (C) 2004, Jean Forget et les Mongueurs de Perl pour la traduction. Tous droits réservés.

Diffusé sous les mêmes conditions que Perl.


Accueil Perl Histoire
Copyright Jean Forget et Les Mongueurs de Perl