#!/bin/ksh #************************************************************** # Author: Sebastien Denvil # Contact: Sebastien.Denvil@ipsl.jussieu.fr # $Date: 2007/03/15 10:31:26 $ # $Name: libIGCM_v1 $ # $Revision: 1.12 $ # IPSL (2006) # This software is governed by the CeCILL licence see libIGCM/libIGCM_CeCILL.LIC # History: # Modification: # #************************************************************** #================================================== # The documentation of this file can be automatically generated # if you use the prefix #D- for comments to be extracted. # Extract with command: cat lib* | grep "^#D-" | cut -c "4-" #================================================== #D-#================================================================== #D-libIGCM_date #D-This ksh library handles date calculs and convertions in different calendars. # Number of digit in the year typeset -r dY=${dY:=4} #typeset -r MaxpY=$( echo "10^"$((dY+1)) | bc -l ) # Number of digit in non-human date representation typeset -r pY=$(( dY+4 )) #================================================================== function IGCM_date_YearDigit { IGCM_debug_PushStack "IGCM_date_YearDigit" $@ NUM=$(( 10#${1} )) echo $( awk "BEGIN { printf \"%0${dY}d\",${NUM} }" ) IGCM_debug_PopStack "IGCM_date_YearDigit" } #================================================================== function IGCM_date_GregorianDigit { IGCM_debug_PushStack "IGCM_date_GregorianDigit" $@ NUM=$(( 10#${1} )) echo $( awk "BEGIN { printf \"%0${pY}d\",${NUM} }" ) IGCM_debug_PopStack "IGCM_date_GregorianDigit" } #================================================================== function IGCM_date_HumanDigit { IGCM_debug_PushStack "IGCM_date_HumanDigit" $@ echo $( IGCM_date_GregorianDigit $( print ${1} | sed 's/-//g' ) ) \ | sed -e "s/\([0-9]\{${dY}\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1-\2-\3/" IGCM_debug_PopStack "IGCM_date_HumanDigit" } #================================================================== function IGCM_date_ConvertFormatToGregorian { IGCM_debug_PushStack "IGCM_date_ConvertFormatToGregorian" $@ # from a yyyy-mm-dd date format return # a yyymmdd date format # usage IGCM_date_ConvertFormat yyyy-mm-dd # if there is no argument on the command line, # then assume that a y-m-d formated date is being # piped in typeset ymd if [ $# = 0 ] then read ymd else ymd=$1 fi IGCM_date_GregorianDigit $( print ${ymd} | sed 's/-//g' ) IGCM_debug_PopStack "IGCM_date_ConvertFormatToGregorian" } #================================================================== function IGCM_date_ConvertFormatToHuman { IGCM_debug_PushStack "IGCM_date_ConvertFormatToHuman" $@ # from a yyyymmdd date format return # a yyyy-mm-dd date format # usage IGCM_date_ConvertFormat yyyymmdd # if there is no argument on the command line, # then assume that a yyyymmdd formated date is being # piped in typeset dt if [ $# = 0 ] then read dt else dt=$1 fi # break the yyyymmdd into separate parts for year, month and day echo $( IGCM_date_GregorianDigit ${dt} ) \ | sed -e "s/\([0-9]\{${dY}\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1-\2-\3/" IGCM_debug_PopStack "IGCM_date_ConvertFormatToHuman" } #================================================================== function IGCM_date_GetYearMonth { IGCM_debug_PushStack "IGCM_date_GetYearMonth" $@ # from a yyyymmdd date format return # a yyyy year and mm month # usage IGCM_date_GetYearMonth yyyymmdd year_var month_var # if there is no argument on the command line, # then assume that a yyyymmdd formated date is being # piped in typeset dt if [ $# = 0 ] then read dt else dt=$1 fi # break the yyyymmdd into separate parts for year, month and day eval $2=$( echo $( IGCM_date_GregorianDigit ${dt} ) \ | sed -e "s/\([0-9]\{${dY}\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1/" ) eval $3=$( echo $( IGCM_date_GregorianDigit ${dt} ) \ | sed -e "s/\([0-9]\{${dY}\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\2/" ) IGCM_debug_PopStack "IGCM_date_GetYearMonth" } #D-#================================================================== #D-function IGCM_date_DaysInYear #D-* Purpose: Return the number of days in a year #D-* Usage: IGCM_date_DaysInYear yyyy #D- if there is no argument on the command line, #D- then assume that a yyyy is being piped in #D- function IGCM_date_DaysInYear { IGCM_debug_PushStack "IGCM_date_DaysInYear" $@ # return the number of days in a year # usage IGCM_date_DaysInYear yyyy # What is the calendar : if [ "${config_UserChoices_CalendarType}" = "360d" ] ; then echo 360 IGCM_debug_PopStack "IGCM_date_DaysInYear" return else if [ "${config_UserChoices_CalendarType}" = "noleap" ] ; then echo 365 IGCM_debug_PopStack "IGCM_date_DaysInYear" return fi fi # if there is no argument on the command line, # then assume that a yyyy is being piped in if [ $# = 0 ] then read y else y=$(( 10#${1} )) fi # a year is a leap year if it is even divisible by 4 # but not evenly divisible by 100 # unless it is evenly divisible by 400 # if it is evenly divisible by 400 it must be a leap year a=$( expr $y \% 400 ) if [ $a = 0 ] then echo 366 IGCM_debug_PopStack "IGCM_date_DaysInYear" return fi #if it is evenly divisible by 100 it must not be a leap year a=$( expr $y \% 100 ) if [ $a = 0 ] then echo 365 IGCM_debug_PopStack "IGCM_date_DaysInYear" return fi # if it is evenly divisible by 4 it must be a leap year a=$( expr $y \% 4 ) if [ $a = 0 ] then echo 366 IGCM_debug_PopStack "IGCM_date_DaysInYear" return fi # otherwise it is not a leap year echo 365 IGCM_debug_PopStack "IGCM_date_DaysInYear" } #D-#================================================================== #D-function IGCM_date_DaysInMonth #D-* Purpose: Calculate the number of days in a month #D-* Usage: IGCM_date_DaysInMonth yyyy mm #D- or IGCM_date_DaysInMonth yyyymmdd #D- if there are no command line arguments then #D- assume that a yyyymmdd is being piped in and read the value. #D- if there is only one argument assume it is a yyyymmdd on the command line #D- other wise it is a yyyy and mm on the command line function IGCM_date_DaysInMonth { IGCM_debug_PushStack "IGCM_date_DaysInMonth" $@ # calculates the number of days in a month # usage IGCM_date_DaysInMonth yyyy mm # or IGCM_date_DaysInMonth yyyymmdd # What is the calendar : if [ "${config_UserChoices_CalendarType}" = "360d" ] ; then echo 30 IGCM_debug_PopStack "IGCM_date_DaysInMonth" return fi # if there are no command line arguments then assume that a yyyymmdd is being # piped in and read the value. # if there is only one argument assume it is a yyyymmdd on the command line # other wise it is a yyyy and mm on the command line if [ $# = 0 ] then read ymd elif [ $# = 1 ] then ymd=$1 else ymd=$( expr \( \( $1 \* 10000 \) \+ \( $2 \* 100 \) \+ 1 \) ) fi # extract the year and the month y=$( expr $ymd / 10000 ) ; m=$( expr \( $ymd \% 10000 \) / 100 ) ; # 30 days hath september etc. case $m in 1|3|5|7|8|10|12) echo 31 IGCM_debug_PopStack "IGCM_date_DaysInMonth" return ;; 4|6|9|11) echo 30 IGCM_debug_PopStack "IGCM_date_DaysInMonth" return ;; *) ;; esac # except for month 2 which depends on whether the year is a leap year # Use IGCM_date_DaysInYear to get the number of days in the year and return a value # accordingly. diy=$( IGCM_date_DaysInYear $y ) case $diy in 365) echo 28 IGCM_debug_PopStack "IGCM_date_DaysInMonth" return ;; 366) echo 29 IGCM_debug_PopStack "IGCM_date_DaysInMonth" return ;; esac IGCM_debug_PopStack "IGCM_date_DaysInMonth" } #D-#================================================================== #D-function IGCM_date_ConvertGregorianDateToJulian #D-* Purpose: Convert yyyymmdd to yyyyddd #D-* Usage: IGCM_date_ConvertGregorianDateToJulian 19980429 #D- if there is no command line argument, then assume that the date #D- is coming in on a pipe and use read to collect it #D- function IGCM_date_ConvertGregorianDateToJulian { IGCM_debug_PushStack "IGCM_date_ConvertGregorianDateToJulian" $@ # IGCM_date_ConvertGregorianDateToJulian converts yyyymmdd to yyyyddd # usage IGCM_date_ConvertGregorianDateToJulian 19980429 # if there is no command line argument, then assume that the date # is coming in on a pipe and use read to collect it if [ $# = 0 ] then read dt else dt=$1 fi # break the yyyymmdd into separate parts for year, month and day y=$( expr $dt / 10000 ) m=$( expr \( $dt \% 10000 \) / 100 ) d=$( expr \( $dt \% 100 \) ) # add the days in each month up to (but not including the month itself) # into the days. For example if the date is 19980203 then extract the # number of days in January and add it to 03. If the date is June 14, 1998 # then extract the number of days in January, February, March, April and May # and add them to 14. x=1 while [ $( expr $x \< $m ) = 1 ] do md=$( IGCM_date_DaysInMonth $y $x ) d=$( expr \( $d \+ $md \) ) x=$( expr \( $x \+ 1 \) ) done # combine the year and day back together again and you have the julian date. jul=$( expr \( $y \* 1000 \) + $d ) echo $jul IGCM_debug_PopStack "IGCM_date_ConvertGregorianDateToJulian" } #D-#================================================================== #D-function IGCM_date_ConvertJulianDateToGregorian() #D-* Purpose: Convert yyyyddd to yyyymmdd #D-* Usage: IGCM_date_ConvertJulianDateToGregorian 1998213 #D- if there is no command line argument, assume one is being #D- piped in and read it #D- function IGCM_date_ConvertJulianDateToGregorian { IGCM_debug_PushStack "IGCM_date_ConvertJulianDateToGregorian" $@ # IGCM_date_ConvertJulianDateToGregorian converts yyyyddd to yyyymmdd # usage IGCM_date_ConvertJulianDateToGregorian 1998213 # if there is no command line argument, assume one is being # piped in and read it if [ X$1 = X ] then read dt else dt=$1 fi # break apart the year and the days y=$( expr $dt / 1000 ) d=$( expr $dt \% 1000 ) # subtract the number of days in each month starting from 1 # from the days in the date. When the day goes below 1, you # have the current month. Add back the number of days in the # month to get the correct day of the month m=1 while [ $( expr $d \> 0 ) = 1 ] do md=$( IGCM_date_DaysInMonth $y $m ) d=$( expr $d \- $md ) m=$( expr $m \+ 1 ) done d=$( expr $d \+ $md ) # the loop steps one past the correct month, so back up the month m=$( expr $m \- 1 ) # assemble the results into a gregorian date grg=$( expr \( $y \* 10000 \) \+ \( $m \* 100 \) \+ $d ) echo $( IGCM_date_GregorianDigit $grg ) IGCM_debug_PopStack "IGCM_date_ConvertJulianDateToGregorian" } #D-#================================================================== #D-function IGCM_date_AddDaysToJulianDate #D-* Purpose: Add days to a yyyyddd formatted date #D-* Usage: IGCM_date_AddDaysToJulianDate 1998312 { ,-}14 #D- Read from the difference from the command lines #D- and the date from the command line, or standard input #D- function IGCM_date_AddDaysToJulianDate { IGCM_debug_PushStack "IGCM_date_AddDaysToJulianDate" $@ # IGCM_date_AddDaysToJulianDate adds days to a yyyyddd formatted date # usage IGCM_date_AddDaysToJulianDate 1998312 { ,-}14 # Read the difference from the command lines # and the date from the command line, or standard input if [ X$2 = X ] then dif=$1 read yd else yd=$1 dif=$2 fi # Break it into pieces d=$( expr $yd \% 1000 ) y=$( expr $yd / 1000 ) # Add the number of days (if days is negative this results is # a subtraction) d=$( expr $d \+ $dif ) # Extract the days in the year diy=$( IGCM_date_DaysInYear $y ) # If the calculated day exceeds the days in the year, # add one year to the year and subtract the days in the year from the # calculated days. Extract the days in the new year and repeat # test until you end up with a day number that falls within the # days of the year while [ $( expr $d \> $diy ) = 1 ] do d=$( expr $d - $diy ) y=$( expr $y \+ 1 ) diy=$( IGCM_date_DaysInYear $y ) done # This is the reverse process. If the calculated number of days # is less than 1, move back one year. Extract # the days in this year and add the days in the year # loop on this test until you end up with a number that # falls within the days of the year while [ $( expr $d \< 1 ) = 1 ] do y=$( expr $y - 1 ) diy=$( IGCM_date_DaysInYear $y ) d=$( expr $d \+ $diy ) done # put the year and day back together and echo the result yd=$( expr \( $y \* 1000 \) + $d ) echo $yd IGCM_debug_PopStack "IGCM_date_AddDaysToJulianDate" } #D-#================================================================== #D-function IGCM_date_AddDaysToGregorianDate #D-* Purpose: Add days to a yyyymmdd formatted date #D-* Usage: IGCM_date_AddDaysToGregorianDate 19980312 { ,-}14 #D- Read from the difference from the command lines #D- and the date from the command line, or standard input #D- function IGCM_date_AddDaysToGregorianDate { IGCM_debug_PushStack "IGCM_date_AddDaysToGregorianDate" $@ # IGCM_date_AddDaysToGregorianDate adds days to a yyyymmdd formatted date # usage IGCM_date_AddDaysToGregorianDate 19980312 { ,-}14 # Read the difference from the command lines # and the date from the command line, or standard input typeset dif yd tmp res if [ X$2 = X ] then dif=$1 read yd else yd=$1 dif=$2 fi tmp=$( IGCM_date_ConvertGregorianDateToJulian $yd ) tmp=$( IGCM_date_AddDaysToJulianDate $tmp $dif ) res=$( IGCM_date_ConvertJulianDateToGregorian $tmp ) echo $res IGCM_debug_PopStack "IGCM_date_AddDaysToGregorianDate" } #D-#================================================================== #D-function IGCM_date_DaysBetweenJulianDate #D-* Purpose: Calculate the days difference between two dates and reports #D- the number days as jul1 - jul2 #D-* Usage: IGCM_date_DaysBetweenJulianDate jul1 jul2 #D- where julian date is in the form yyyyddd #D- function IGCM_date_DaysBetweenJulianDate { IGCM_debug_PushStack "IGCM_date_DaysBetweenJulianDate" $@ # calculates the days difference between two dates and reports # the number days as jul1 - jul2 # usage IGCM_date_DaysBetweenJulianDate jul1 jul2 # where julian date is in the form yyyyddd usage () { echo "Usage:" echo " IGCM_date_DaysBetweenJulianDate jul1 jul2" echo "" echo " Calculates the day difference between" echo " two julian dates (jul1 -jul2)" echo " where a julian date is in the form of yyyyddd." } if [ $# -lt 2 ]; then usage IGCM_debug_Exit "IGCM_date_DaysBetweenJulianDate" fi # This process subtracts arg2 from arg1. If arg2 is larger # then reverse the arguments. The calculations are done, and # then the sign is reversed if [ $1 -lt $2 ] then jul1=$2 jul2=$1 elif [ $1 -gt $2 ] then jul1=$1 jul2=$2 else echo 0 IGCM_debug_PopStack "IGCM_date_DaysBetweenJulianDate" return fi # Break the dates in to year and day portions yyyy1=$( expr $jul1 / 1000 ) yyyy2=$( expr $jul2 / 1000 ) ddd1=$( expr $jul1 \% 1000 ) ddd2=$( expr $jul2 \% 1000 ) # Subtract days res=$( expr $ddd1 - $ddd2 ) # Then add days in year until year2 matches year1 if [ "${config_UserChoices_CalendarType}" = "360d" ] ; then res=$( expr \( \( $yyyy1 \- $yyyy2 \) \* 360 \) \+ $res ) elif [ "${config_UserChoices_CalendarType}" = "noleap" ] ; then res=$( expr \( \( $yyyy1 \- $yyyy2 \) \* 365 \) \+ $res ) elif ( [ "${config_UserChoices_CalendarType}" = "leap" ] || [ "${config_UserChoices_CalendarType}" = "gregorian" ] ) ; then while [ $yyyy2 -lt $yyyy1 ] do diy=$( IGCM_date_DaysInYear $yyyy2 ) res=$( expr $res + $diy ) yyyy2=$( expr $yyyy2 + 1 ) done fi # if argument 2 was larger than argument 1 then # the arguments were reversed before calculating # adjust by reversing the sign if [ $1 -lt $2 ] then res=$( expr $res \* -1 ) fi # and output the results echo $res IGCM_debug_PopStack "IGCM_date_DaysBetweenJulianDate" } #D-#================================================================== #D-function IGCM_date_DaysBetweenGregorianDate () #D-* Purpose: Calculate the days difference between two dates and reports #D- the number days as grg1 - grg2 #D-* Usage: IGCM_date_DaysBetweenGregorianDate grg1 grg2 #D- where gregorian date is in the form yyyymmdd #D- function IGCM_date_DaysBetweenGregorianDate { IGCM_debug_PushStack "IGCM_date_DaysBetweenGregorianDate" $@ # calculates the days difference between two dates and reports # the number days as grg1 - grg2 # usage IGCM_date_DaysBetweenGregorianDate grg1 grg2 # where gregorian date is in the form yyyymmdd usage () { echo "Usage:" echo " IGCM_date_DaysBetweenGregorianDate grg1 grg2" echo "" echo " Calculate day difference between" echo " two gregorian dates (grg1 - grg2)" echo " where a gregorian date is in the form of yyyymmdd." } if [ $# -lt 2 ]; then usage IGCM_debug_Exit "IGCM_date_DaysBetweenGregorianDate" fi # convert each date to julian grg1=$1 grg2=$2 jul1=$( IGCM_date_ConvertGregorianDateToJulian $grg1 ) jul2=$( IGCM_date_ConvertGregorianDateToJulian $grg2 ) if [ $jul1 -ne $jul2 ]; then # calculate the answer using IGCM_date_DaysBetweenJulianDate res=$( IGCM_date_DaysBetweenJulianDate $jul1 $jul2 ) # and output the results echo $res else echo 0 fi IGCM_debug_PopStack "IGCM_date_DaysBetweenGregorianDate" } #D-#================================================================== #D-function IGCM_date_DaysSinceJC () #D-* Purpose: Calculate the days difference between a date and 00010101 #D-* Usage: IGCM_date_DaysSinceJC grg1 #D- where gregorian date is in the form yyyymmdd #D- function IGCM_date_DaysSinceJC { IGCM_debug_PushStack "IGCM_date_DaysSinceJC" $@ # calculates the days difference between a date and 00010101 # usage IGCM_date_DaysSinceJC grg1 # where gregorian date is in the form yyyymmdd usage () { echo "Usage:" echo " IGCM_date_DaysSinceJC grg1" echo "" echo " Calculate day difference between" echo " a gregorian date and 00010101" echo " where a gregorian date is in the form of yyyymmdd." } if [ $# -lt 1 ]; then usage IGCM_debug_Exit "IGCM_date_DaysSinceJC" fi typeset aux num if [ ${1} -lt 5000000 ]; then case ${config_UserChoices_CalendarType} in 360d) aux=-360;; noleap) aux=-365;; leap) aux=-366;; esac num=101 elif [ ${1} -lt 15000000 ]; then # To save CPU type we use auxiliary value # which is number of days since JC and 10000101 case ${config_UserChoices_CalendarType} in 360d) aux=359640;; noleap) aux=364635;; leap) aux=364877;; esac num=10000101 else # To save CPU type we use auxiliary value # which is number of days since JC and 19000101 case ${config_UserChoices_CalendarType} in 360d) aux=683640;; noleap) aux=693135;; leap) aux=693595;; esac num=19000101 fi echo $( expr \( $( IGCM_date_DaysBetweenGregorianDate $1 ${num} ) \+ $aux \) ) IGCM_debug_PopStack "IGCM_date_DaysSinceJC" } #D-#================================================================== #D-function IGCM_date_Check #D- * Purpose: Check the present file by comparison with a reference file function IGCM_date_Check { IGCM_debug_PushStack "IGCM_date_Check" #--------------------- if [ ! -n "${libIGCM}" ] ; then echo "Check libIGCM_date ...........................................[ FAILED ]" echo "--Error--> libIGCM variable is not defined" IGCM_debug_Exit "IGCM_date_Check" fi #--------------------- whence -v awk > /dev/null 2>&1 if [ ! $? -eq 0 ] ; then echo "Check libIGCM_date ...........................................[ FAILED ]" echo "--Error--> awk command is not defined" IGCM_debug_Exit "IGCM_date_Check" fi #--------------------- ${libIGCM}/libIGCM_date/IGCM_date_Test.ksh > IGCM_date_Test.ref.failed 2>&1 if diff IGCM_date_Test.ref.failed ${libIGCM}/libIGCM_date/IGCM_date_Test${dY}.ref > /dev/null 2>&1 ; then echo "Check libIGCM_date ...............................................[ OK ]" rm -f IGCM_date_Test.ref.failed else echo "Check libIGCM_date ...........................................[ FAILED ]" echo "--Error--> Execution of ${libIGCM}/libIGCM_date/IGCM_date_Test.ksh" echo " has produced the file IGCM_date_Test.ref.failed" echo " Please analyse differences with the reference file by typing:" echo " diff IGCM_date_Test.ref.failed ${libIGCM}/libIGCM_date/IGCM_date_Test${dY}.ref" echo " Report errors to the author: Sebastien.Denvil@ipsl.jussieu.fr" IGCM_debug_Exit "IGCM_date_Check" fi #--------------------- IGCM_debug_PopStack "IGCM_date_Check" } #==================================================================