سؤال

أحتاج إلى إجراء حساب التاريخ في البرامج النصية Unix Shell التي أستخدمها للتحكم في تنفيذ برامج الطرف الثالث.

أنا أستخدم دالة لزيادة يوم وآخر لتقليل:

IncrementaDia(){
echo $1 | awk '
BEGIN {
        diasDelMes[1] = 31
        diasDelMes[2] = 28
        diasDelMes[3] = 31
        diasDelMes[4] = 30
        diasDelMes[5] = 31
        diasDelMes[6] = 30
        diasDelMes[7] = 31
        diasDelMes[8] = 31
        diasDelMes[9] = 30
        diasDelMes[10] = 31
        diasDelMes[11] = 30
        diasDelMes[12] = 31
}
{
        anio=substr($1,1,4)
        mes=substr($1,5,2)
        dia=substr($1,7,2)

        if((anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0)
        {
                diasDelMes[2] = 29;
        }

        if( dia == diasDelMes[int(mes)] ) {
                if( int(mes) == 12 ) {
                        anio = anio + 1
                        mes = 1
                        dia = 1
                } else {
                        mes = mes + 1
                        dia = 1
                }
        } else {
                dia = dia + 1
        }
}
END {
        printf("%04d%02d%02d", anio, mes, dia)
}
'
}

if [ $# -eq 1 ]; then
        tomorrow=$1
else
        today=$(date +"%Y%m%d")
        tomorrow=$(IncrementaDia $hoy)
fi

لكني الآن بحاجة إلى إجراء عمليات حسابية أكثر تعقيدًا.

ما هي الطريقة الأفضل والأكثر توافقًا للقيام بذلك؟

هل كانت مفيدة؟

المحلول

لقد كتبت نص BASH لتحويل التواريخ التي يتم التعبير عنها باللغة الإنجليزية إلى تواريخ MM/DD/YYYY التقليدية.تسمى تاريخ الحساب.

فيما يلي بعض الأمثلة على استخدامه.للإيجاز ، وضعت إخراج كل الاحتجاج على نفس الخط مثل الاحتجاج ، مفصولًا بواسطة القولون (:).الاقتباسات المبينة أدناه هي لا ضروري عند التشغيل تاريخ الحساب:

$ ComputeDate 'yesterday': 03/19/2010
$ ComputeDate 'yes': 03/19/2010
$ ComputeDate 'today': 03/20/2010
$ ComputeDate 'tod': 03/20/2010
$ ComputeDate 'now': 03/20/2010
$ ComputeDate 'tomorrow': 03/21/2010
$ ComputeDate 'tom': 03/21/2010
$ ComputeDate '10/29/32': 10/29/2032
$ ComputeDate 'October 29': 10/1/2029
$ ComputeDate 'October 29, 2010': 10/29/2010
$ ComputeDate 'this monday': 'this monday' has passed.  Did you mean 'next monday?'
$ ComputeDate 'a week after today': 03/27/2010
$ ComputeDate 'this satu': 03/20/2010
$ ComputeDate 'next monday': 03/22/2010
$ ComputeDate 'next thur': 03/25/2010
$ ComputeDate 'mon in 2 weeks': 03/28/2010
$ ComputeDate 'the last day of the month': 03/31/2010
$ ComputeDate 'the last day of feb': 2/28/2010
$ ComputeDate 'the last day of feb 2000': 2/29/2000
$ ComputeDate '1 week from yesterday': 03/26/2010
$ ComputeDate '1 week from today': 03/27/2010
$ ComputeDate '1 week from tomorrow': 03/28/2010
$ ComputeDate '2 weeks from yesterday': 4/2/2010
$ ComputeDate '2 weeks from today': 4/3/2010
$ ComputeDate '2 weeks from tomorrow': 4/4/2010
$ ComputeDate '1 week after the last day of march': 4/7/2010
$ ComputeDate '1 week after next Thursday': 4/1/2010
$ ComputeDate '2 weeks after the last day of march': 4/14/2010
$ ComputeDate '2 weeks after 1 day after the last day of march': 4/15/2010
$ ComputeDate '1 day after the last day of march': 4/1/2010
$ ComputeDate '1 day after 1 day after 1 day after 1 day after today': 03/24/2010

لقد قمت بتضمين هذا البرنامج النصي كإجابة على هذه المشكلة لأنه يوضح كيفية القيام بحساب التاريخ عبر مجموعة من وظائف bash وقد تكون هذه الوظائف مفيدة للآخرين.يتعامل مع السنوات الكبيسة والقرون الكبيسة بشكل صحيح:

#! /bin/bash
#  ConvertDate -- convert a human-readable date to a MM/DD/YY date
#
#  Date ::= Month/Day/Year
#        |  Month/Day
#        |  DayOfWeek
#        |  [this|next] DayOfWeek
#        |  DayofWeek [of|in] [Number|next] weeks[s]
#        |  Number [day|week][s] from Date
#        |  the last day of the month
#        |  the last day of Month
#
#  Month ::= January | February | March | April | May | ...  | December
#  January  ::= jan | january | 1
#  February  ::= feb | january | 2
#  ...
#  December ::=  dec | december | 12
#  Day   ::= 1 | 2 | ... | 31
#  DayOfWeek ::= today | Sunday | Monday | Tuesday | ...  | Saturday
#  Sunday    ::= sun*
#  ...
#  Saturday  ::= sat*
#
#  Number ::= Day | a
#
#  Author: Larry Morell

if [ $# = 0 ]; then
   printdirections $0
   exit
fi



# Request the value of a variable
GetVar () {
   Var=$1
   echo -n "$Var= [${!Var}]: "
   local X
   read X
   if [ ! -z $X ]; then
      eval $Var="$X"
   fi
}

IsLeapYear () {
   local Year=$1
   if [ $[20$Year % 4]  -eq  0 ]; then
      echo yes
   else
      echo no
   fi
}

# AddToDate -- compute another date within the same year

DayNames=(mon tue wed thu fri sat sun )  # To correspond with 'date' output

Day2Int () {
   ErrorFlag=
   case $1 in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   local dow=$1
   n=0
   while  [ $n -lt 7 -a $dow != "${DayNames[n]}" ]; do
      let n++
   done
   if [ -z "$ErrorFlag" -a $n -eq 7 ]; then
      echo Cannot convert $dow to a numeric day of wee
      exit
   fi
   echo $[n+1]

}

Months=(31 28 31 30 31 30 31 31 30 31 30 31)
MonthNames=(jan feb mar apr may jun jul aug sep oct nov dec)
# Returns the month (1-12) from a date, or a month name
Month2Int () {
   ErrorFlag=
   case $1 in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   M=$1
   Month=${M%%/*}  # Remove /...
   case $Month in
      [a-z]* )
         Month=${Month:0:3}
         M=0
         while [ $M -lt 12 -a ${MonthNames[M]} != $Month ]; do
            let M++
         done
         let M++
   esac
   if [  -z "$ErrorFlag" -a $M -gt 12 ]; then
      echo "'$Month' Is not a valid month."
      exit
   fi
   echo $M
}

# Retrieve month,day,year from a legal date
GetMonth() {
   echo ${1%%/*}
}

GetDay() {
   echo $1 | col / 2
}

GetYear() {
   echo ${1##*/}
}


AddToDate() {

   local Date=$1
   local days=$2
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`   # Day of Date
   local Year=`echo $Date | col / 3`  # Year of Date
   local LeapYear=`IsLeapYear $Year`

   if [ $LeapYear = "yes" ]; then
      let Months[1]++
   fi
   Day=$[Day+days]
   while [ $Day -gt ${Months[$Month-1]} ]; do
       Day=$[Day -  ${Months[$Month-1]}]
       let Month++
   done
   echo "$Month/$Day/$Year"
}

# Convert a date to normal form
NormalizeDate () {
   Date=`echo "$*" | sed 'sX  *X/Xg'`
   local Day=`date +%d`
   local Month=`date +%m`
   local Year=`date +%Y`
   #echo Normalizing Date=$Date > /dev/tty
   case $Date in
      */*/* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=`echo $Date | col / 2`
         Year=`echo $Date | col / 3`
         ;;
      */* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=1
         Year=`echo $Date | col / 2 `
         ;;
      [a-z]* ) # Better be a month or day of week
         Exp=${Date:0:3}
         case $Exp in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               Month=$Exp
               Month=`Month2Int $Month`
               Day=1
               #Year stays the same
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               # Compute the next such day
               local DayOfWeek=`date +%u`
               D=`Day2Int $Exp`
               if [ $DayOfWeek -le $D ]; then
                  Date=`AddToDate $Month/$Day/$Year $[D-DayOfWeek]`
               else
                  Date=`AddToDate $Month/$Day/$Year $[7+D-DayOfWeek]`
               fi

               # Reset Month/Day/Year
               Month=`echo $Date | col / 1 `
               Day=`echo $Date | col / 2`
               Year=`echo $Date | col / 3`
               ;;
            * ) echo "$Exp is not a valid month or day"
                exit
               ;;
            esac
         ;;
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
   case $Day in
      [0-9]* );;  # Day must be numeric
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
      [0-9][0-9][0-9][0-9] );;  # Year must be 4 digits
      [0-9][0-9] )
          Year=20$Year
      ;;
   esac
   Date=$Month/$Day/$Year
   echo $Date
}
# NormalizeDate jan
# NormalizeDate january
# NormalizeDate jan 2009
# NormalizeDate jan 22 1983
# NormalizeDate 1/22
# NormalizeDate 1 22
# NormalizeDate sat
# NormalizeDate sun
# NormalizeDate mon

ComputeExtension () {

   local Date=$1; shift
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`
   local Year=`echo $Date | col / 3`
   local ExtensionExp="$*"
   case $ExtensionExp in
      *w*d* )  # like 5 weeks 3 days or even 5w2d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            weeks=`echo $ExtensionExp | col  1`
            days=`echo $ExtensionExp | col 2`
            days=$[7*weeks+days]
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      *d )    # Like 5 days or 5d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            days=$ExtensionExp
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      * )
            Due=$ExtensionExp
      ;;
   esac
   echo $Due

}


# Pop -- remove the first element from an array and shift left
Pop () {
   Var=$1
   eval "unset $Var[0]"
   eval "$Var=(\${$Var[*]})"
}

ComputeDate () {
   local Date=`NormalizeDate $1`; shift
   local Expression=`echo $* | sed 's/^ *a /1 /;s/,/ /' | tr A-Z a-z `
   local Exp=(`echo $Expression `)
   local Token=$Exp  # first one
   local Ans=
   #echo "Computing date for ${Exp[*]}" > /dev/tty
   case $Token in
      */* ) # Regular date
         M=`GetMonth $Token`
         D=`GetDay $Token`
         Y=`GetYear $Token`
         if [ -z "$Y" ]; then
            Y=$Year
         elif [ ${#Y} -eq 2 ]; then
            Y=20$Y
         fi
         Ans="$M/$D/$Y"
         ;;
      yes* )
         Ans=`AddToDate $Date -1`
         ;;
      tod*|now )
         Ans=$Date
         ;;
      tom* )
         Ans=`AddToDate $Date 1`
         ;;
      the )
         case $Expression in
            *day*after* )  #the day after Date
               Pop Exp;   # Skip the
               Pop Exp;   # Skip day
               Pop Exp;   # Skip after
               #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
               Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
               #echo "New date is " $Date > /dev/tty
               Ans=`AddToDate $Date 1`
               ;;
            *last*day*of*th*month|*end*of*th*month )
               M=`date +%m`
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Year` = yes ]; then
                  let Day++
               fi
               Ans=$Month/$Day/$Year
               ;;
            *last*day*of* )
               D=${Expression##*of }
               D=`NormalizeDate $D`
               M=`GetMonth $D`
               Y=`GetYear $D`
               # echo M is $M > /dev/tty
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Y` = yes ]; then
                  let Day++
               fi
               Ans=$[M]/$Day/$Y
               ;;
            * )
               echo "Unknown expression: " $Expression
               exit
               ;;
         esac
         ;;
      next* ) # next DayOfWeek
         Pop Exp
         dow=`Day2Int $DayOfWeek` # First 3 chars
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         n=$[7-dow+tdow]
         Ans=`AddToDate $Date $n`
         ;;
      this* )
         Pop Exp
         dow=`Day2Int $DayOfWeek`
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         if [ $dow -gt $tdow ]; then
            echo "'this $Exp' has passed.  Did you mean 'next $Exp?'"
            exit
         fi
         n=$[tdow-dow]
         Ans=`AddToDate $Date $n`
         ;;
      [a-z]* ) # DayOfWeek ...

         M=${Exp:0:3}
         case $M in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               ND=`NormalizeDate ${Exp[*]}`
               Ans=$ND
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               dow=`Day2Int $DayOfWeek`
               Ans=`NormalizeDate $Exp`

               if [ ${#Exp[*]} -gt 1 ]; then # Just a DayOfWeek
                  #tdow=`GetDay $Exp` # First 3 chars
                  #if [ $dow -gt $tdow ]; then
                     #echo "'this $Exp' has passed.  Did you mean 'next $Exp'?"
                     #exit
                  #fi
                  #n=$[tdow-dow]
               #else  # DayOfWeek in a future week
                  Pop Exp  # toss monday
                  Pop Exp  # toss in/off
                  if [ $Exp = next ]; then
                     Exp=2
                  fi
                  n=$[7*(Exp-1)]   # number of weeks
                  n=$[n+7-dow+tdow]
                  Ans=`AddToDate $Date $n`
               fi
               ;;
         esac
         ;;
      [0-9]* ) # Number  weeks [from|after] Date
         n=$Exp
         Pop Exp;
         case $Exp in
            w* ) let n=7*n;;
         esac

         Pop Exp; Pop Exp
         #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
         Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
         #echo "New date is " $Date > /dev/tty
         Ans=`AddToDate $Date $n`
         ;;
   esac
   echo $Ans
}

Year=`date +%Y`
Month=`date +%m`
Day=`date +%d`
DayOfWeek=`date +%a |tr A-Z a-z`

Date="$Month/$Day/$Year"
ComputeDate $Date $*

يستخدم هذا البرنامج النصي على نطاق واسع برنامجًا نصيًا آخر كتبته (يُسمى العقيد ...العديد من الاعتذارات لأولئك الذين يستخدمون المعيار العقيد مزود بنظام Linux).هذا الإصدار منالعقيد يبسط استخراج الأعمدة من stdin.هكذا،

$ echo a b c d e | col 5 3 2

مطبوعات

e c b

وهنا ال العقيد النصي:

#!/bin/sh
# col -- extract columns from a file
# Usage:
#    col [-r] [c] col-1 col-2 ...
#   where [c] if supplied defines the field separator
#   where each col-i represents a column interpreted according to  the presence of -r as follows:
#        -r present : counting starts from the right end of the line
#        -r absent  : counting starts from the left side of the line
Separator=" "
Reverse=false
case "$1" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator="$1"; shift;
 ;;
esac

case "$1" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator="$1"; shift;
 ;;
esac

#  Replace each col-i with $i
Cols=""
for  f in $*
do
  if [ $Reverse = true ]; then
     Cols="$Cols \$(NF-$f+1),"
  else
     Cols="$Cols \$$f,"
  fi

done

Cols=`echo "$Cols" | sed 's/,$//'`
#echo "Using column specifications of $Cols"
awk -F "$Separator"  "{print $Cols}"

ويستخدم أيضا اتجاهات الطباعة لطباعة التوجيهات عند استدعاء البرنامج النصي بشكل غير صحيح:

#!/bin/sh
#
#  printdirections -- print header lines of a shell script
#
#  Usage:
#      printdirections path
#  where
#      path is a *full* path to the shell script in question
#      beginning with '/'
#
#  To use printdirections, you must include (as comments at the top
#  of your shell script) documentation for running the shell script.

if [ $# -eq 0 -o "$*" = "-h" ]; then
   printdirections $0
   exit
fi
#  Delete the command invocation at the top of the file, if any
#  Delete from the place where printdirections occurs to the end of the file
#  Remove the # comments
#  There is a bizarre oddity here.
   sed '/#!/d;/.*printdirections/,$d;/ *#/!d;s/# //;s/#//' $1 > /tmp/printdirections.$$

#  Count the number of lines
numlines=`wc -l /tmp/printdirections.$$ | awk '{print $1}'`

#  Remove the last   line
numlines=`expr $numlines - 1`


head -n $numlines /tmp/printdirections.$$
rm /tmp/printdirections.$$

لاستخدام هذا المكان البرامج النصية الثلاثة في الملفات تاريخ الحساب, العقيد, ، و اتجاهات الطباعة, ، على التوالى.ضع الملف في الدليل المسمى بواسطة PATH، عادةً ~/bin.ثم اجعلها قابلة للتنفيذ باستخدام:

$ chmod a+x ComputeDate col printdirections

مشاكل؟أرسل لي بعض البريد الإلكتروني:موريل في cs.atu.edu مكان تاريخ الحساب في هذا الموضوع.

نصائح أخرى

على افتراض أن لديك تاريخ جنو, ، مثل ذلك:

date --date='1 days ago' '+%a'

و عبارات مماثلة.

فيما يلي طريقة سهلة لإجراء حسابات التاريخ في البرمجة النصية لـ Shell.

meetingDate='12/31/2011' # MM/DD/YYYY Format
reminderDate=`date --date=$meetingDate'-1 day' +'%m/%d/%Y'`
echo $reminderDate

فيما يلي المزيد من الاختلافات في حساب التاريخ التي يمكن تحقيقها باستخدام date جدوى.http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

لقد نجح هذا بالنسبة لي على RHEL.

للتوافق مع BSD / OS X، يمكنك أيضًا استخدام أداة التاريخ مع -j و -v للقيام الرياضيات التاريخ.انظر صفحة FreeBSD للتاريخ.يمكنك دمج إجابات Linux السابقة مع هذه الإجابة والتي قد توفر لك التوافق الكافي.

على BSD، كما لينكس، قيد التشغيل date سيعطيك التاريخ الحالي:

$ date
Wed 12 Nov 2014 13:36:00 AEDT

الآن مع تاريخ BSD، يمكنك إجراء العمليات الحسابية باستخدامه -v, ، على سبيل المثال إدراج تاريخ الغد (+1d يكون بالإضافة إلى يوم واحد):

$ date -v +1d
Thu 13 Nov 2014 13:36:34 AEDT

يمكنك استخدام تاريخ موجود كأساس، واختياريًا تحديد تنسيق التحليل باستخدام strftime، والتأكد من استخدام -j حتى لا تغير تاريخ النظام الخاص بك:

$ date -j -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
Sat  9 Aug 2014 12:37:14 AEST

ويمكنك استخدام هذا كأساس لحسابات التاريخ:

$ date -v +1d -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
Sun 10 Aug 2014 12:37:14 AEST

لاحظ أن -v يدل -j.

يمكن توفير تعديلات متعددة بالتسلسل:

$ date -v +1m -v -1w
Fri  5 Dec 2014 13:40:07 AEDT

راجع الصفحة لمزيد من التفاصيل.

لإجراء العمليات الحسابية باستخدام التواريخ على UNIX، يمكنك الحصول على التاريخ كعدد الثواني منذ عصر UNIX، وإجراء بعض العمليات الحسابية، ثم تحويله مرة أخرى إلى تنسيق التاريخ القابل للطباعة.يجب أن يكون أمر التاريخ قادرًا على إعطائك الثواني منذ العصر والتحويل من هذا الرقم مرة أخرى إلى تاريخ قابل للطباعة.يقوم أمر التاريخ المحلي الخاص بي بذلك،

% date -n
1219371462
% date 1219371462
Thu Aug 21 22:17:42 EDT 2008
% 

انظر المحلية الخاصة بك date(1) صفحة الرجل.لزيادة يوم أضف 86400 ثانية.

لماذا لا تكتب نصوصك البرمجية باستخدام لغة مثل Perl أو python بدلاً من تلك التي تدعم بشكل طبيعي معالجة التاريخ المعقدة؟بالتأكيد أنت يستطيع افعل كل ذلك باستخدام bash، لكنني أعتقد أنك ستحصل أيضًا على مزيد من الاتساق عبر الأنظمة الأساسية باستخدام python على سبيل المثال، طالما يمكنك التأكد من تثبيت Perl أو python.

يجب أن أضيف أنه من السهل جدًا توصيل نصوص python و Perl في نص برمجي يحتوي على Shell.

لقد اصطدمت بهذا عدة مرات.أفكاري هي:

  1. حساب التاريخ هو دائما الألم
  2. يكون الأمر أسهل قليلاً عند استخدام تنسيق التاريخ EPOCH
  3. يتم تحويل التاريخ على Linux إلى EPOCH، ولكن ليس على Solaris
  4. للحصول على حل محمول، يتعين عليك القيام بأحد الإجراءات التالية:
    1. تثبيت GNU DATE على Solaris (سبق ذكره ، يحتاج إلى التفاعل البشري لإكمال)
    2. استخدم بيرل لجزء التاريخ (معظم تشمل عمليات تثبيت UNIX Perl ، لذلك أفترض عمومًا أن هذا الإجراء يقوم لاتتطلب عملا إضافيا).

نموذج نصي (يتحقق من عمر بعض ملفات المستخدم لمعرفة ما إذا كان من الممكن حذف الحساب):

#!/usr/local/bin/perl

$today = time();

$user = $ARGV[0];

$command="awk -F: '/$user/ {print \$6}' /etc/passwd";

chomp ($user_dir = `$command`);

if ( -f "$user_dir/.sh_history" ) {
    @file_dates   = stat("$user_dir/.sh_history");
    $sh_file_date = $file_dates[8];
} else {
    $sh_file_date = 0;
}
if ( -f "$user_dir/.bash_history" ) {
    @file_dates     = stat("$user_dir/.bash_history");
    $bash_file_date = $file_dates[8];
} else {
    $bash_file_date = 0;
}
if ( $sh_file_date > $bash_file_date ) {
    $file_date = $sh_file_date;
} else {
    $file_date = $bash_file_date;
}
$difference = $today - $file_date;

if ( $difference >= 3888000 ) {
    print "User needs to be disabled, 45 days old or older!\n";
    exit (1);
} else {
    print "OK\n";
    exit (0);
}
date --date='1 days ago' '+%a'

إنه ليس حلاً متوافقًا جدًا.وسوف تعمل فقط في لينكس.على الأقل، لم ينجح الأمر في إيكس وسولاريس.

يعمل في RHEL:

date --date='1 days ago' '+%Y%m%d'
20080807

بالنظر إلى الأمر بشكل أكبر، أعتقد أنه يمكنك ببساطة استخدام التاريخ.لقد قمت بتجربة ما يلي على OpenBSD:انا اخذت تاريخ فبراير29 2008 وساعة عشوائية (على شكل 080229301535) وإضافة +1 إلى جزء اليوم، كما يلي:

$ date -j 0802301535
Sat Mar  1 15:35:00 EST 2008

كما ترون، التاريخ قام بتنسيق الوقت بشكل صحيح...

هث

إذا كنت تريد الاستمرار مع awk، فإن الدالتين mktime وstrftime مفيدتان:


BEGIN { dateinit }
      { newdate=daysadd(OldDate,DaysToAdd)}

 # daynum: convert DD-MON-YYYY to day count
 #-----------------------------------------
function daynum(date,  d,m,y,i,n)
{
     y=substr(date,8,4)
     m=gmonths[toupper(substr(date,4,3))]
     d=substr(date,1,2)
     return mktime(y" "m" "d" 12 00 00")
}

 #numday: convert day count to DD-MON-YYYY
 #-------------------------------------------
function numday(n,  y,m,d)
{
    m=toupper(substr(strftime("%B",n),1,3))
    return strftime("%d-"m"-%Y",n)
}

 # daysadd: add (or subtract) days from date (DD-MON-YYYY), return new date (DD-MON-YYYY)
 #------------------------------------------
function daysadd(date, days)
{
    return numday(daynum(date)+(days*86400))
}

 #init variables for date calcs
 #-----------------------------------------
function dateinit(   x,y,z)
{
     # Stuff for date calcs
     split("JAN:1,FEB:2,MAR:3,APR:4,MAY:5,JUN:6,JUL:7,AUG:8,SEP:9,OCT:10,NOV:11,DEC:12", z)
     for (x in z)
     {
        split(z[x],y,":")
        gmonths[y[1]]=y[2]
     }
}

كتاب "وصفات شل النصية:نهج حل المشكلات" (رقم ISBN:978-1-59059-471-1) بقلم كريس إف.لدى جونسون مكتبة وظائف التاريخ التي قد تكون مفيدة.كود المصدر متاح في http://apress.com/book/downloadfile/2146 (وظائف التاريخ موجودة في Chapter08/data-funcs-sh داخل ملف tar).

إذا كانت نسخة GNU من date تناسبك، فلماذا لا تحصل على المصدر وتجميعه على AIX وSolaris؟

http://www.gnu.org/software/coreutils/

على أية حال، يجب أن يساعدك المصدر في الحصول على حساب التاريخ الصحيح إذا كنت ستكتب التعليمات البرمجية الخاصة بك.

جانبًا، تعليقات مثل "هذا الحل جيد ولكن يمكنك بالتأكيد ملاحظة أنه ليس جيدًا بقدر الإمكان.يبدو أنه لم يفكر أحد في تعديل التواريخ عند إنشاء Unix." لا تقودنا إلى أي مكان حقًا.لقد وجدت أن كل اقتراح من الاقتراحات حتى الآن مفيد جدًا ويحقق الهدف.

ها هي قيمة البنسين اللذين أمتلكهما - وهو عبارة عن غلاف نصي يستخدم date و grep.

مثال الاستخدام

> sh ./datecalc.sh "2012-08-04 19:43:00" + 1s
2012-08-04 19:43:00 + 0d0h0m1s
2012-08-04 19:43:01

> sh ./datecalc.sh "2012-08-04 19:43:00" - 1s1m1h1d
2012-08-04 19:43:00 - 1d1h1m1s
2012-08-03 18:41:59

> sh ./datecalc.sh "2012-08-04 19:43:00" - 1d2d1h2h1m2m1s2sblahblah
2012-08-04 19:43:00 - 1d1h1m1s
2012-08-03 18:41:59

> sh ./datecalc.sh "2012-08-04 19:43:00" x 1d
Bad operator :-(

> sh ./datecalc.sh "2012-08-04 19:43:00"
Missing arguments :-(

> sh ./datecalc.sh gibberish + 1h
date: invalid date `gibberish'
Invalid date :-(

النصي

#!/bin/sh

# Usage:
#
# datecalc "<date>" <operator> <period>
#
# <date> ::= see "man date", section "DATE STRING"
# <operator> ::= + | -
# <period> ::= INTEGER<unit> | INTEGER<unit><period>
# <unit> ::= s | m | h | d

if [ $# -lt 3 ]; then
echo "Missing arguments :-("
exit; fi

date=`eval "date -d \"$1\" +%s"`
if [ -z $date ]; then
echo "Invalid date :-("
exit; fi

if ! ([ $2 == "-" ] || [ $2 == "+" ]); then
echo "Bad operator :-("
exit; fi
op=$2

minute=$[60]
hour=$[$minute*$minute]
day=$[24*$hour]

s=`echo $3 | grep -oe '[0-9]*s' | grep -m 1 -oe '[0-9]*'`
m=`echo $3 | grep -oe '[0-9]*m' | grep -m 1 -oe '[0-9]*'`
h=`echo $3 | grep -oe '[0-9]*h' | grep -m 1 -oe '[0-9]*'`
d=`echo $3 | grep -oe '[0-9]*d' | grep -m 1 -oe '[0-9]*'`
if [ -z $s ]; then s=0; fi
if [ -z $m ]; then m=0; fi
if [ -z $h ]; then h=0; fi
if [ -z $d ]; then d=0; fi

ms=$[$m*$minute]
hs=$[$h*$hour]
ds=$[$d*$day]

sum=$[$s+$ms+$hs+$ds]
out=$[$date$op$sum]
formattedout=`eval "date -d @$out +\"%Y-%m-%d %H:%M:%S\""`

echo $1 $2 $d"d"$h"h"$m"m"$s"s"
echo $formattedout

هذا يعمل بالنسبة لي:

TZ=GMT+6;
export TZ
mes=`date --date='2 days ago' '+%m'`
dia=`date --date='2 days ago' '+%d'`
anio=`date --date='2 days ago' '+%Y'`
hora=`date --date='2 days ago' '+%H'`
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top