Obtenez la date d'hier dans bash sur Linux, sans danger pour l'heure d'été


158

J'ai un script shell qui fonctionne sous Linux et utilise cet appel pour obtenir la date d'hier au YYYY-MM-DDformat:

date -d "1 day ago" '+%Y-%m-%d'

Cela fonctionne la plupart du temps, mais lorsque le script a été exécuté hier matin, 2013-03-11 0:35 CDTil est revenu "2013-03-09"au lieu de "2013-03-10".

L'heure d'été (qui a commencé hier) est probablement à blâmer. Je suppose que la façon dont "1 day ago"est mise en œuvre elle a été soustraite 24 heures, et 24 heures avant l' 2013-03-11 0:35 CDTétait 2013-03-09 23:35 CST, ce qui a conduit au résultat "2013-03-09".

Alors, quel est un bon moyen sûr pour l'heure d'été d'obtenir la date d'hier dans bash sur Linux?


Exécutez-vous toujours ceci en même temps, l'exécutez-vous à plusieurs reprises?
tink

@tink il fonctionne tous les jours à 00:35
Ike Walker

Réponses:


267

Je pense que cela devrait fonctionner, indépendamment de la fréquence et du moment où vous l'exécutez ...

date -d "yesterday 13:00" '+%Y-%m-%d'

1
Comment attribueriez-vous cela à une variable pour une utilisation ultérieure?
null

6
@null - de la même manière que vous affecteriez la sortie de toute autre commande shell. variable = $ (date -d "hier 13:00" '+% Y-% m-% d') ...
tink le

Pour GNU-date:date -d yesterday 13:00 -I
gogstad

Cela repose sur le fait que le passage de l'heure d'été à l'heure d'hiver se fait toujours la nuit, si je comprends bien?
Nicolas Raoul

2
@NicolasRaoul: Ce n'est pas garanti; mais c'est une convention largement suivie de programmer cela tôt le matin. D'autre part, il n'y a pas d'endroit où le changement se produirait vers le midi local. Ainsi, "1300J n'est jamais sur une frontière DST" est une hypothèse raisonnable .
Piskvor a quitté le bâtiment

56

la date sous Mac OSX est légèrement différente.

Pour hier

date -v-1d +%F

Pour la semaine dernière

date -v-1w +%F

8
Si vous souhaitez utiliser le style GNU dans OSX, vous pouvez également utiliser gdate, disponible dans le coreutilspackage homebrew .
user456584

11
Vous voulez dire que datec'est différent.
août

12

Cela devrait également fonctionner, mais c'est peut-être trop:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"

Des trucs comme celui-ci sont nécessaires si vous devez gérer Mac OS X datequi ne prend pas en charge la yesterdaysyntaxe ...
Mikko Rantalainen

7

Si vous êtes certain que le script s'exécute dans les premières heures de la journée, vous pouvez simplement faire

  date -d "12 hours ago" '+%Y-%m-%d'

BTW, si le script s'exécute quotidiennement à 00:35 (via crontab?), Vous devriez vous demander ce qui se passera si un changement d'heure d'été tombe dans cette heure; le script n'a pas pu s'exécuter ou s'exécuter deux fois dans certains cas. Les implémentations modernes de cronsont cependant assez intelligentes à cet égard.


5

vous pouvez utiliser

date -d "30 days ago" +"%d/%m/%Y"

pour obtenir la date d'il y a 30 jours, vous pouvez également remplacer 30 par x nombre de jours


Vous devez modifier votre format pour %Y%m%dqu'il corresponde à la question. J'ai quand même voté pour parce que cela fonctionnait correctement.
isapir

4

Voici une solution qui fonctionnera également avec Solaris et AIX.

La manipulation du fuseau horaire est possible pour changer l'horloge de quelques heures. En raison de l'heure d'été, il y a 24 heures peut être aujourd'hui ou avant-hier.

Vous êtes sûr qu'hier, c'est il y a 20 ou 30 heures. Laquelle? Eh bien, le plus récent qui n'est pas aujourd'hui.

echo -e "$(TZ=GMT+30 date +%Y-%m-%d)\n$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1

Le paramètre -e utilisé dans la commande echo est nécessaire avec bash, mais ne fonctionnera pas avec ksh. Dans ksh, vous pouvez utiliser la même commande sans l'option -e.

Lorsque votre script sera utilisé dans différents environnements, vous pouvez démarrer le script avec #! / Bin / ksh ou #! / Bin / bash. Vous pouvez également remplacer le \ n par une nouvelle ligne:

echo "$(TZ=GMT+30 date +%Y-%m-%d)
$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1

0

Il suffit d'utiliser dateet de confiance secondes:

Comme vous le faites remarquer à juste titre, de nombreux détails sur le calcul sous-jacent sont cachés si vous vous fiez à l'arithmétique du temps anglaise. Par exemple -d yesterday, et -d 1 day agoaura un comportement différent.

Au lieu de cela, vous pouvez compter de manière fiable sur les secondes (précisément documentées) depuis l'époque unix UTC, et bash l'arithmétique pour obtenir le moment souhaité:

date -d @$(( $(date +"%s") - 24*3600)) +"%Y-%m-%d"

Cela a été souligné dans une autre réponse . Ce formulaire est plus portable sur les plates-formes avec différents dateindicateurs de ligne de commande, est indépendant de la langue (par exemple "hier" vs "hier" dans les paramètres régionaux français), et franchement (à long terme) sera plus facile à retenir, car bon, vous le sais déjà. Sinon, vous pourriez continuer à vous demander: "Était-ce -d 2 hours agoou -d 2 hour agoencore?" ou "Est-ce -d yesterdayou -d 1 day agoque je veux?"). Le seul point délicat ici est le @.

Armé de bash et rien d'autre:

Bash uniquement sur bash, vous pouvez également obtenir l'heure d'hier, via le printf intégré:

 %(datefmt)T
     causes printf to output the date-time string resulting from using
     datefmt as a format string for strftime(3).  The  corresponding  argu
     ment  is an integer representing the number of seconds since the
     epoch.  Two special argument values may be used: -1 represents the
     current time, and -2 represents the time the shell was invoked.
     If no argument is specified, conversion behaves as if -1 had
     been  given.
     This is an exception to the usual printf behavior.

Alors,

# inner printf gets you the current unix time in seconds
# outer printf spits it out according to the format
printf "%(%Y-%m-%d)T\n" $(( $(printf "%(%s)T" -1) - 24*3600 ))

ou, de manière équivalente à une variable temp (sous-coque externe facultative, mais garde les variables d'environnement propres).

(
  now=$(printf "%(%s)T" -1);
  printf "%(%Y-%m-%d)T\n" $((now - 24*3600));
)

Remarque: malgré la page de manuel indiquant qu'aucun argument du %()Tformateur ne supposera une valeur par défaut -1, il semble que je reçoive un 0 à la place (merci, bash version 4.3.48 du manuel)


0
date -d "yesterday" '+%Y-%m-%d'

Pour l'utiliser plus tard:

date=$(date -d "yesterday" '+%Y-%m-%d')

2
Bien que ce code puisse résoudre le problème du PO, il est préférable d'inclure une explication sur la façon dont votre code résout le problème du PO. De cette façon, les futurs visiteurs peuvent apprendre de votre publication et l'appliquer à leur propre code. SO n'est pas un service de codage, mais une ressource de connaissances. Des réponses complètes et de haute qualité renforcent cette idée et sont plus susceptibles d'être votées. Ces fonctionnalités, ainsi que l'exigence que tous les messages soient autonomes, sont quelques atouts de SO en tant que plate-forme qui nous différencie des forums. Vous pouvez modifier pour ajouter des informations supplémentaires et / ou pour compléter vos explications avec la documentation source.
SherylHohman

0

Vous pouvez utiliser:

date -d "yesterday 13:55" '+%Y-%m-%d'

Ou quelle que soit l'heure à laquelle vous souhaitez récupérer, elle sera récupérée par bash.

Pour mois:

date -d "30 days ago" '+%Y-%m-%d'

0

Comme cette question est étiquetée "DST sécurisé"

Et en utilisant fork pour datecommander implie delay, il existe un moyen simple et plus efficace d'utiliser pure bash intégré:

printf -v tznow '%(%z %s)T' -1
TZ=${tznow% *} printf -v yesterday '%(%Y-%m-%d)T' $(( ${tznow#* } - 86400 ))
echo $yesterday

Ceci est beaucoup plus rapide sur plus convivial système que d' avoir à la fourche àdate .

De V> = 5,0 , il y a une nouvelle variable$EPOCHSECONDS

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.