Slide 1

Slide 1 text

La génération de dates en PHP Par Kevin Nadin, conférencier Padawan / - Forum PHP 2017 @kevinjhappy

Slide 2

Slide 2 text

Vraiment ?? Une présentation sur les dates ?? Oui, les dates sont piégeuses! Il y a beaucoup de possibilités de les générer, et elles peuvent vous causer des soucis...

Slide 3

Slide 3 text

Nous allons voir de suite : 1. La fonction date (forcément) 2. La fonction mktime 3. La fonction strtotime 4. L'objet DateTime 5. L'objet DateTimeZone 6. L'objet DateInterval 7. Des exemples d'utilisation

Slide 4

Slide 4 text

La fonction date()

Slide 5

Slide 5 text

Commençons avec les bases : La première fonction à laquelle on pense, évidemment Elle l'a été pour moi pour en générer php.net: fonction date

Slide 6

Slide 6 text

$testDate = date('Y-m-d'); echo $testDate; // 2017-05-26 $testDate = date('Y-m-d H:i:s'); echo $testDate; // 2017-05-26 16:25:08

Slide 7

Slide 7 text

Les dates avec un timestamp date() accepte un timestamp comme second paramètre string date ( string $format [, int $timestamp = time() ] )

Slide 8

Slide 8 text

Timestamps ?? Un timestamp unix est le nombre de secondes écoulées depuis le 1er Janvier 1970 sans fuseau horaire Vient des normes de l'IEEE qui ont standardisé cette date en marquant le début de l'ère UNIX

Slide 9

Slide 9 text

La fonction mktime()

Slide 10

Slide 10 text

Prends en entrée 6 paramètres : Attention à leur ordre ! 1. Heures 2. Minutes 3. Secondes 4. Mois 5. Jours 6. Années

Slide 11

Slide 11 text

$hour = 0; $minute = 0; $second = 0; $month = 10; $day = 24; $year = 2016; $timestamp = mktime($hour, $minute, $second, $month, $day, $year); $myDate = date('Y-m-d', $timestamp); echo $myDate; // 2016-10-24

Slide 12

Slide 12 text

mktime semble un peu moisi... Ouais c'est vrai, cela peut être très compliqué. Mais cela peut apporter une précision chirurgicale

Slide 13

Slide 13 text

// I want to get the last day of last month, we are the 26th May 2017 $month = date('m') - 1; $year = date('Y'); // get the timestamp of last month $timestampLastMonth = mktime(0, 0, 0, $month, 1, $year); // get the last day of the month, so the 30th April $lastDayOfTheMonth = date('t', $timestampLastMonth); $timestampLastDayOfLastMonth = mktime( 0, 0, 0, $month, $lastDayOfTheMonth, $year ); // I have the correct timestamp, I can call the function date $lastDayOfLastMonth = date('Y-m-d', $timestampLastDayOfLastMonth); echo $lastDayOfLastMonth; // 2017-04-30

Slide 14

Slide 14 text

// I want to get the last day of last month, we are the 26th May 2017 // made in one line $lastDayOfLastMonth = date( 'Y-m-d', mktime( 0, 0, 0, date('m') - 1, date('t', mktime( 0, 0, 0, date('m') - 1, 1, date('Y') )), date('Y') ) ); // => 2017-04-30

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Comportement à connaitre Si j'essaye de mettre le 33 janvier echo date('Y-m-d', mktime(0, 0, 0, 1, 33, 2017)); // 2017-02-02 Cela va prendre la date du 31 janvier + 2 jours

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

La fonction strtotime()

Slide 19

Slide 19 text

Supposons, nous sommes le 2017-05-26 : $firstDayOfLastMonth = date('Y-m-d', strtotime("first day of last month")); // => 2017-04-01 $lastDayOfLastMonth = date('Y-m-d', strtotime("last Day of Last Month")); // => 2017-04-30 $lastSunday = date('Y-m-d', strtotime("last Sunday")); // => 2017-05-21 $mondayOfLastWeek = date('Y-m-d', strtotime("last Monday of Last Week")); // => 2017-05-15

Slide 20

Slide 20 text

Alors strtotime est bien ? Ouais, c'est pas mal du tout ! Il faut faire attention à la "magie" de la fonction Certaines dates ne peuvent pas être faites avec php.net : Formats relatifs

Slide 21

Slide 21 text

// WARNING !! first Specific day of last week // will not give you what you want $date = date('Y-m-d', strtotime("First Monday of Last Week")); // => 2017-04-24 // WARNING !! if you are the 31th, Last month will give you // the day one of this month $timeThirtyOneOctober = mktime(0,0,0,10,31,2017); $wrongDate = date('Y-m-d', strtotime("Last Month", $timeThirtyOneOctober)); // => 2017-10-01 // Only way to get it right is by using this $goodDate = date('Y-m-d', strtotime( "Last Day of Last Month", $timeThirtyOneOctober )); // => 2017-09-30

Slide 22

Slide 22 text

// WARNING, some sentences may seem ok but do not work at all $wrongDate = date('Y-m-d', strtotime("Monday of Last Week")); // => 1970-01-01 // You can't construct with strtotime the 13th of this month $dayThirteenOfThisMonth = date( 'Y-m-d', mktime(0, 0, 0, date('m'), 13, date('Y')) ); // => 2017-05-13

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

L'objet DateTime

Slide 25

Slide 25 text

Ah ben enfin on discute !! On peut oublier les chapitres précédents !

Slide 26

Slide 26 text

Yeah !!

Slide 27

Slide 27 text

Hum... NON !! Parce que les comportements sont les mêmes !

Slide 28

Slide 28 text

$firstDayOfThisMonth = new \DateTime('first day of this month'); echo $firstDayOfThisMonth->format('Y-m-d'); // 2017-05-01 $dayFifteenOfThisMonth = new \DateTime(); $dayFifteenOfThisMonth->setTime(0, 0, 0); $dayFifteenOfThisMonth->setDate( (int) $dayFifteenOfThisMonth->format('Y'), (int) $dayFifteenOfThisMonth->format('m'), 15 ); echo $dayFifteenOfThisMonth->format('Y-m-d'); // 2017-05-15

Slide 29

Slide 29 text

Et les pièges sont les mêmes !

Slide 30

Slide 30 text

// first Specific day of last Week // Today is 2017-05-26 $date = new DateTime("First Monday of Last Week"); echo $date->format('Y-m-d') ; // 2017-04-24 // sentences that may seems ok but does not work at all $wrongDate = new DateTime("Monday of Last Week"); echo $wrongDate->format('Y-m-d'); // Fatal error: Uncaught Exception: DateTime::__construct(): // Failed to parse time string (Monday of Last Week)

Slide 31

Slide 31 text

// WARNING !! if you are the 31th, Last month will give you // the day one of this month $lastMonthDate = new DateTime("2017-10-31"); $lastMonthDate->modify('Last Month'); echo $lastMonthDate->format('Y-m-d'); // 2017-10-01 // Only way to get it right is by using this $lastMonthDate = new DateTime("2017-10-31"); $lastMonthDate->modify('Last Day of Last Month'); echo $lastMonthDate->format('Y-m-d'); // 2017-09-30

Slide 32

Slide 32 text

Petite parenthèse : correction possible avec la librairie Carbon //Taking the last month of a 31th of this month $date = Carbon::createFromDate(2016, 10, 31); Carbon::useMonthsOverflow(false); $date->subMonth(1); echo $date->format('Y-m-d'); // 2016-09-30

Slide 33

Slide 33 text

Parlons aussi de DateTimeImmutable Même utilisation que DateTime() Di érence : ne peut pas être modi é une fois créé. Toute modi cation renvoit un nouvel objet

Slide 34

Slide 34 text

$date = new DateTimeImmutable("2017-05-26"); $newDate = $date->setDate(2017, 07, 12); echo $date->format('Y-m-d'), " <=> ", $newDate->format('Y-m-d'); // 2017-05-26 <=> 2017-07-12

Slide 35

Slide 35 text

Une fonction en plus : DateTimeImmutable::createFromMutable // create a specific date $date = new DateTime('2017-09-15'); $myNewDate = DateTimeImmutable::createFromMutable($date); echo $myNewDate->format('Y-m-d'); // 2017-09-15

Slide 36

Slide 36 text

L'objet DateTimeZone

Slide 37

Slide 37 text

Toujours penser et faire attention avec le fuseau horaire Par défaut con gurez le serveur à UTC Pensez à sauvegarder celui des dates reçues N'oubliez pas de l'appliquer lorsque vous communiquez

Slide 38

Slide 38 text

// set a date with a Timezone $date = new DateTime('2017-05-01', new DateTimeZone('Europe/Paris')); echo "Europe/Paris " , $date->format('Y-m-d H:i:s P'); // Europe/Paris 2017-05-01 00:00:00 +02:00 $date->setDate(2017, 01, 01); echo "Europe/Paris en hiver " , $date->format('Y-m-d H:i:s P'); // Europe/Paris en hiver 2017-01-01 00:00:00 +01:00 $date->setTimezone(new DateTimeZone('Australia/Sydney')); echo "Australia/Sydney " , $date->format(\DateTime::ISO8601); // Australia/Sydney 2017-01-01T10:00:00+1100

Slide 39

Slide 39 text

Fuseaux Horaires possibles php.net => timezones : Attention à celles qui sont listés dans la catégorie "Autres" comme Japan, Turkey, etc... Wikipedia.org => list timezones

Slide 40

Slide 40 text

// set a date with a Timezone in number of hours $date = new DateTime('2017-05-01', new DateTimeZone('+02:00')); echo "UTC + 2 hours " , $date->format('Y-m-d H:i:sP') ; // UTC + 2 hours 2017-05-01 00:00:00+02:00 $utcDate = new DateTime('2017-05-01', new DateTimeZone('UTC')); echo "UTC date " , $utcDate->format('Y-m-d H:i:sP'); // UTC date 2017-05-01 00:00:00+00:00

Slide 41

Slide 41 text

Et bien sûr... des pièges ! // today in Paris, let's try to get the location of the timezone $dateInParis = new DateTime('now', new \DateTimeZone('Europe/Paris')); print_r($dateInParis->getTimezone()->getLocation()); /*Array ( [country_code] => FR [latitude] => 48.86666 [longitude] => 2.33333 [comments] => )*/ // now if we received this date in string format $newDateInParis = new DateTime($dateInParis->format('Y-m-d H:i:sP')); var_dump($newDateInParis->getTimezone()->getLocation()); // bool(false)

Slide 42

Slide 42 text

Pour éviter ce piège // today in Paris, let's try to get the location of the timezone $dateInParis = new DateTime('now', new \DateTimeZone('Europe/Paris')); $newDateInParis = new DateTime( $dateInParis->format('Y-m-d H:i:s'), $dateInParis->getTimezone() ); print_r($newDateInParis->getTimezone()->getLocation()); /* Array ( [country_code] => FR [latitude] => 48.86666 [longitude] => 2.33333 [comments] => )*/

Slide 43

Slide 43 text

l'object DateInterval

Slide 44

Slide 44 text

Cela permet de définir une période Seulement 2 fonctions, assez similaires En utilisation DateTime->diff, cela donnera un intervale

Slide 45

Slide 45 text

new DateInterval('P2Y4DT6H8M') Quelle est cette chose ?????? Le P est pour "Period", on précise des années aux jours Le T est pour "Time", on précise des heures aux secondes Dans notre exemple ci dessus : P: 2Year 4Day T: 6Hour 8Minute

Slide 46

Slide 46 text

// set a date Interval $dateInterval = new DateInterval('P2Y4DT6H8M'); echo $dateInterval->format('%y years, %d days and %h hours, %i minutes'); // 2 years, 4 days and 6 hours, 8 minutes // equals to : $dateInterval = DateInterval::createFromDateString( '2 year + 4 day + 6 hour + 8 minutes' ); echo $dateInterval->format('%y years, %d days and %h hours, %i minutes'); // 2 years, 4 days and 6 hours, 8 minutes

Slide 47

Slide 47 text

Quelques situations rencontrés

Slide 48

Slide 48 text

Demande simple : extraire les 10 derniers jours // we set default dates, today is 2017-05-29 $endDate = new DateTime(); $startDate = new DateTime(); // substract 10 days with the object DateInterval $startDate->sub(new DateInterval('P10D')); // or you can use Date Interval with regular string $startDate->sub(new DateInterval::createFromDateString('10 Days')); // or you can use the construct if you don't like DateInterval $startDate = new DateTime("now - 10 days"); // startDate = 2017-05-19 , endDate = 2017-05-29

Slide 49

Slide 49 text

Demande : extraire la dernière demi-semaine, soit du lundi au mercredi, ou du jeudi au dimanche // If today is between Monday to Wednesday, // We want to get dates from last Thursday to last Sunday $today = new DateTime(); // 'N' indicate the day number, Monday = 1, Tuesday = 2 ... Sunday = 7 if ($today->format('N') < 4 ){ $startDate = new DateTime('Last Thursday'); $endDate = new DateTime('Last Sunday'); } // else, today is between Thursday to Sunday, // we want to get dates from Last Monday to Last Wednesday else { $startDate = new DateTime('Last Monday'); $endDate = new DateTime('Last Wednesday'); }

Slide 50

Slide 50 text

Demande: extraire la dernière semaine, mais si la semaine est à cheval entre 2 mois, il faut prendre le 1er jour du mois // We need to check If the Last Monday is in the last month $lastMondayDate = new DateTime('Last Monday'); $lastDayOfLastMonthDate = new DateTime('Last Day of Last Month'); // if the last monday is indeed in the past month, // we take the first day of this month if ($lastMondayDate->format('m') === $lastDayOfLastMonthDate->format('m')){ $startDate = new DateTime('First Day of This Month'); } // else we take the last monday else{ $startDate = $lastMondayDate ; } // in both cases, end date is the Last Sunday $endDate = new DateTime('Last Sunday');

Slide 51

Slide 51 text

Que se passe-t-il si je fais ceci ?? // Today is 2017-05-29 $date = new DateTime("first day of last month"); echo $date->format('Y-m-d') , ' => '; $date->setDate(2013, 2, 3); echo $date->format('Y-m-d');

Slide 52

Slide 52 text

Executons ça sur 3v4l.org

Slide 53

Slide 53 text

// Today is : 2017-05-29 $date = new DateTime("first day of last month"); echo $date->format('Y-m-d') , ' => '; $date->setDate(2013, 2, 3); echo $date->format('Y-m-d'); // in PHP 7.0.17 - 7.0.19, 7.1.3 - 7.2.0rc2 // 2017-04-01 => 2013-02-03 // in PHP 5.6.0 - 5.6.30, 7.0.0 - 7.0.16, 7.1.0 - 7.1.2 // 2017-04-01 => 2013-02-01

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Whoa, c'est fort problématique ! Le comportement n'est pas le même en fonction de la version de PHP Le problème est le même avec certaines autres phrases

Slide 56

Slide 56 text

// Anoter Exemple, we create with Last day of this month $date = new DateTime("last day of this month"); echo $date->format('Y-m-d') , ' => '; $date->setDate(2012, 2, 03); echo $date->format('Y-m-d'); // in PHP 7.0.17 - 7.0.19, 7.1.3 - 7.2.0rc2 // 2017-05-31 => 2012-02-03 // in PHP 5.6.0 - 5.6.30, 7.0.0 - 7.0.16, 7.1.0 - 7.1.2 // 2017-05-31 => 2012-02-29 Finalement il a calculé le dernier jour de Février 2012 (année bissextile)

Slide 57

Slide 57 text

Mon conseil: Ne le faites pas ! En tout cas pas avant un bon moment ! Si vous travaillez sur une version inférieure à 7.1.3, cela risque de ne pas marcher...

Slide 58

Slide 58 text

Conclusion

Slide 59

Slide 59 text

Avec les bonnes pratiques d'aujourd'hui DateTimeImmutable avec DateTimeZone sont les plus utilisés Les fonctions date() avec des timestamps étaient surtout utilisées en procédural

Slide 60

Slide 60 text

Attention !! Ne créez jamais de dates avec une chaine de caractères sans les avoir testées dans plusieurs situations ! Par exemple: au 31 Janvier, sur une année Bissextile, etc... Gardez en tête qu'un comportement sur une version de PHP ne marchera pas forcément sur une autre faketime : modi e la date du système pour l'application

Slide 61

Slide 61 text

Pour simplifier...

Slide 62

Slide 62 text

Des questions ??

Slide 63

Slide 63 text

Merci ;)