SHARE
TWEET

Output ordinal part of date from current date or input integ

robertmarkbram Nov 3rd, 2015 (edited) 118 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. # ------------------------------------------------------------------------------
  3. # -- Output ordinal part of date.
  4. # ------------------------------------------------------------------------------
  5. # Output ordinal part of date from current date or input integer - or output formatted date.
  6. # Day of month:
  7. #    echo $(date +"%d").
  8. # This script gives you the ordinal indicator:
  9. #    th, st, nd, rd
  10. # Usage:
  11. #       echo $(date +"%A, %d`dateOrdinal.sh` of %B %Y, %I:%M:%S %p")
  12. #       Tuesday, 15th of September 2015, 09:47:05 PM
  13. # See this written up here:
  14. #               http://robertmarkbramprogrammer.blogspot.com.au/2015/09/date-time-stamps-in-bash-with-ordinal.html
  15. #               http://pastebin.com/xZ1afqqC
  16.  
  17. #
  18. # Author: Robert Mark Bram
  19. # Tuesday, 15th of September 2015, 10:04:12 PM
  20. # - Initial script.
  21. # Sunday, 1st of November 2015, 01:23:58 PM
  22. # - Adapted to script created from newScriptWithShebangLine.sh.
  23. # - Added ability to accept an argument to work with.
  24. # Tuesday, 3rd of November 2015, 11:53:36 PM
  25. # - Allowed command to accept date and a format so that it can format
  26. #      a date and not just output ordinal.
  27. # Wednesday, 4th of November 2015, 12:11:27 AM
  28. # - Check for invalid date.
  29. # Saturday, 7th of November 2015, 11:40:47 PM
  30. # - Brought this into line with my latest bash template.
  31. #               Specifically, helpLess is false by default.
  32. # Saturday, 7th of November 2015, 11:49:18 PM
  33. # - Fixed yes instead of true. Do we display help (usage) and exit?
  34. #               Treat as variable so that we can finish processing arguments first.
  35. # Sunday, 8th of November 2015, 12:00:42 AM
  36. # - Fixed issue with dateInteger having leading zeroes and failing on modulus.
  37.  
  38. # ------------------------------------------------------------------------------
  39. # -- TODO.
  40. # ------------------------------------------------------------------------------
  41. #
  42.  
  43. # ------------------------------------------------------------------------------
  44. # -- Dependencies.
  45. # ------------------------------------------------------------------------------
  46. # - Date.
  47.  
  48. # ------------------------------------------------------------------------------
  49. # -- Variables for this script.
  50. # ------------------------------------------------------------------------------
  51. # -
  52. # Common variables.
  53. # --------------------------------
  54. # Shortcut for the name of this file - for docs.
  55. commandName=`echo $0 | sed 's|.*/||'`
  56. # Verbose output?.
  57. verbose=false
  58. # Count of non option parameters.
  59. countNonOptionParameters=0
  60. # Do we display help in less on ERROR?
  61. helpLess=false
  62. # Default exitCode.
  63. exitCode=0
  64. # Do we display help (usage) and exit?
  65. # Treat as variable so that we can finish processing arguments first.
  66. helpThenExit=false
  67. # -
  68. # Script specific variables.
  69. # --------------------------------
  70. # Date integer, defaults to current date.
  71. dateInteger=
  72. # Format to use on date.
  73. dateFormat=
  74. # Date to format. Defaults to now.
  75. dateToFormat=$(date)
  76. # The ordinal value: st, nd, rd or th.
  77. dateOrdinal=
  78.  
  79. # ------------------------------------------------------------------------------
  80. # -- Common functions for this script.
  81. # ------------------------------------------------------------------------------
  82.  
  83. # ===  FUNCTION  ===============================================================
  84. #   DESCRIPTION:  Output message if verbose is on
  85. #    PARAMETERS:  message to be printed
  86. #       RETURNS:  -
  87. # ==============================================================================
  88. function message() {
  89.    if [ "$verbose" = true ] ; then
  90.       echo -e "${1}"
  91.    fi
  92. }
  93.  
  94. # ===  FUNCTION  ===============================================================
  95. #   DESCRIPTION:  Output help and usage message
  96. #    PARAMETERS:  error message to be printed
  97. #       RETURNS:  -
  98. #                                               Example to call usage with a varable and new line in
  99. #                                                       error message:
  100. #                                                               usage $'blah ['"${variable}"$'] with \nnew line.'
  101. # ==============================================================================
  102. function usage() {
  103. errMessage=
  104. exitCodeRe='^[0-9]+$'
  105. if [ "$#" -eq 2 ] ; then
  106.         # Error message.
  107.    errMessage=$'\nERROR:\n*** '"${1}"$' ***\n'
  108.         # Error code (if int)
  109.         if [[ "$2" =~ $exitCodeRe ]] ; then
  110.            exitCode="${2}"
  111.            errMessage=${errMessage}$'Exit code: '"${exitCode}"$' \n'
  112.                 # If help without less, exit now.
  113.                 if [ "${helpLess}" = false ] ; then
  114.                         echo "*** ${1} ***"
  115.                         outputAllArgs
  116.                 exit ${exitCode}
  117.                 fi
  118.         fi
  119. elif [ "$#" -eq 1 ] ; then
  120.         if [[ "$1" =~ $exitCodeRe ]] ; then
  121.            exitCode="${1}"
  122.         fi
  123.    errMessage=HELP
  124. else
  125.    errMessage=HELP
  126. fi
  127.  
  128. # Give a value to allArgs if verbose.
  129. if [ "${verbose}" = true ] ; then
  130.         outputAllArgs silent
  131. fi
  132.  
  133. less -XF << STOP_HELP
  134. $errMessage
  135. Output ordinal part of date from current date or input integer - or output formatted date.
  136.  
  137. Just output ordinal
  138.    Usage: $commandName [-i dateInteger|-d dateToFormat] [Miscellaneous Arguments]
  139.         Just output the ordinal: st, nd, rd or th for day of month (given as integer or
  140.         part of date string). No args means output ordinal for current date.
  141. Output entire date from given format.
  142.    Usage: $commandName -f dateFormat [-d dateToFormat]
  143.         Given a date format and a date (defaults to current date if not given), output
  144.         formatted date with %O as ordinal within pattern.
  145.  
  146. --- Main args
  147.         no args                 Use current date and just output ordinal.
  148.         [-i dateInteger]
  149.                                                 Outputs ordinal for given day of month (which must be an integer).
  150.         [-d dateToFormat]
  151.                                                 If not given, current date/time will be used [$(date)]. Without
  152.                                                 "-f dateFormat", output ordinal from day of month in this date. With
  153.                                                 "-f dateFormat", output fully formatted date for this date.
  154.                                                 Here are a few examples of what sorts of date we can accept (whatever
  155.                                                 the *date* command can accept).
  156.                                                         date --date='April 14 2015 09:45 am'
  157. $(date --date='April 14 2015 09:45 am' | sed "s/^/                                                         /")
  158.                                                         date --date='April 14 2015'
  159. $(date --date='April 14 2015' | sed "s/^/                                                          /")
  160.                                                         date --date='today'
  161. $(date --date='today' | sed "s/^/                                                          /")
  162.                                                         date --date='now'
  163. $(date --date='now' | sed "s/^/                                                    /")
  164.                                                         date --date='14 April 2014 9:45:00 pm'
  165. $(date --date='14 April 2014 9:45:00 pm' | sed "s/^/                                                       /")
  166.                                                         date --date='next Thursday'
  167. $(date --date='next Thursday' | sed "s/^/                                                          /")
  168.         -f dateFormat
  169.                                                 Means we will output given date with this format.
  170.                                                 "Date string" as per the date command (see man date) with one
  171.                                                 exepction:
  172.                                                         - use %O (capital O) where you want us to insert the ordinal.
  173.  
  174. --- Miscellaneous Arguments
  175.        [-v]             Verbose output.
  176.        [-h|-help] Displays this message and exits.
  177.        [-H|]            If command exits with error, display help in less.
  178.                                         Useful if NOT using command in a pipe stream to get better help.
  179.                                         Default is false.
  180.  
  181. --- Exit codes.
  182.    0 - Success!
  183.    0 - User asked to display help.
  184.    9 - Function processSingleLetterArguments returned false.
  185.   10 - Undefined single letter option supplied to processSingleLetterArguments.
  186.   11 - Option processSingleLetterArguments required an argument and none was supplied.
  187.   12 - Arg dateInteger must be an integer.
  188.   13 - Too many trailing arguments. None are accepted.
  189.   14 - How many days do you think there are in a monnth? Should be 31 or less btw.
  190.   15 - Cannot supply both dateInteger and dateFormat.
  191.   16 - Cannot supply both dateInteger and dateToFormat.
  192.   17 - Cannot supply dateFormat without also supplying dateToFormat.
  193.   18 - Invalid dateToFormat supplied.
  194.  
  195.  --- Examples
  196.         - Output ordinal for current date.
  197.                 $commandName
  198. $($commandName | sed "s/^/              /")
  199.  
  200.         - Output ordinal for tomorrow.
  201.                 $commandName -d "tomorrow"
  202. $($commandName -d "tomorrow" | sed "s/^/                /")
  203.  
  204.         - Output ordinal for specific date and time.
  205.                 $commandName -d "Tue, Nov 03, 2015 11:46:03 PM"
  206. $($commandName -d "Tue, Nov 03, 2015 11:46:03 PM" | sed "s/^/           /")
  207.  
  208.         - Output fully formatted date that includes ordinal for tomorrow.
  209.                 $commandName -d "tomorrow" -f "%A, %-d%O of %B %Y, %I:%M:%S %p"
  210. $($commandName -d "tomorrow" -f "%A, %-d%O of %B %Y, %I:%M:%S %p" | sed "s/^/           /")
  211.  
  212. ${allArgs}
  213.  
  214. STOP_HELP
  215.  
  216.         # Exit if given error code.
  217.         if [ -n "${exitCode}" ] ; then
  218.            exit ${exitCode}
  219.         fi
  220.  
  221. }
  222.  
  223. # ===  FUNCTION  ===============================================================
  224. #   DESCRIPTION:  Process all arguments to script. How to handle getopts args
  225. #                 and operands at the same time:
  226. #                    http://stackoverflow.com/a/21169366/257233
  227. #    PARAMETERS:  -
  228. #       RETURNS:  -
  229. # ==============================================================================
  230. function processArguments() {
  231.    # Process all arguments. Make sure number of args is correct.
  232.    # if [ $# -lt 3 ] ; then
  233.    #     usage "** Incorrect number of args specified **" 9999
  234.    # fi
  235.  
  236.    # Loop to handle all parameters
  237.    non_option_parameters=()
  238.    while true; do
  239.       # Process single letter args.
  240.       while getopts "$OPTIONS" option; do
  241.          if ! processSingleLetterArguments "$option"; then
  242.                 usage "Function processSingleLetterArguments returned false." 9
  243.          fi
  244.       done
  245.       if ((OPTIND > $#)); then break; fi
  246.       non_option_parameters+=(${!OPTIND})
  247.       ((countNonOptionParameters++))
  248.  
  249.      ((OPTIND++))
  250.    done
  251.  
  252.         # Help?
  253.         if [ "${helpThenExit}" = true ] ; then
  254.                 usage 0
  255.         fi
  256.  
  257.    # ---------------------------------------------------------------------------
  258.    # Post argument processing - logic that must be applied once we know all args.
  259.    # ---------------------------------------------------------------------------
  260.         # Only room for one trailing arg or none.
  261.         if [ "${countNonOptionParameters}" -ge 1 ] ; then
  262.            usage "Too many trailing arguments [$countNonOptionParameters]. None are allowed." 13
  263.         fi
  264.  
  265.         # Cannot supply both dateInteger and dateFormat.
  266.         if [ -n "${dateFormat}" -a -n "${dateInteger}" ] ; then
  267.                 usage "Cannot supply both dateInteger [$dateInteger] and dateFormat [$dateFormat]." 15
  268.         fi
  269.  
  270.         # Cannot supply both dateInteger and dateToFormat.
  271.         if [ -n "${dateToFormat}" -a -n "${dateInteger}" ] ; then
  272.                 usage "Cannot supply both dateInteger [$dateInteger] and dateToFormat [$dateToFormat]." 16
  273.         fi
  274.  
  275.         if [ -n "${dateFormat}" -a -z "${dateToFormat}" ] ; then
  276.                 usage "Cannot supply dateFormat [$dateFormat] without also supplying dateToFormat." 17
  277.         fi
  278.  
  279.         if [ -n "${dateToFormat}" ] ; then
  280.                 date --date="${dateToFormat}" &>/dev/null
  281.                 if [ "$?" -ne 0 ] ; then
  282.                         usage "Invalid dateToFormat [$dateToFormat] supplied." 18
  283.                 fi
  284.         fi
  285.  
  286. }
  287.  
  288. # ===  FUNCTION  ===============================================================
  289. #   DESCRIPTION:  Process single letter args via getopts
  290. #    PARAMETERS:  -
  291. #       RETURNS:  -
  292. # ==============================================================================
  293. OPTIONS=":d:f:hHi:v"
  294. function processSingleLetterArguments() {
  295.    case "$1" in
  296.         # Deal with these ones first, always.
  297.       H ) helpLess=true;;
  298.       v ) verbose=true;;
  299.       h ) helpThenExit=true;;
  300.       # Deal with the rest.
  301.       d ) dateToFormat="$OPTARG";;
  302.       i ) dateInteger="$OPTARG";;
  303.       f ) dateFormat="$OPTARG";;
  304.       \?)   usage "Invalid option: -$OPTARG" 10;;
  305.       :)    usage "Option -$OPTARG requires an argument." 11;;
  306.    esac
  307. }
  308.  
  309. # ===  FUNCTION  ===============================================================
  310. #   DESCRIPTION:  Output all the arguments we have if verbose is turned on.
  311. #                 Sets variable allArgs.
  312. #    PARAMETERS:  $1 if "silent", DOES NOT output, just sets variable allArgs.
  313. #       RETURNS:  -
  314. # ==============================================================================
  315. function outputAllArgs() {
  316.    if [ "$verbose" = true ] ; then
  317. read -r -d '' allArgs << EOM
  318. --- List of args -----------------------------------------
  319. Arg commandName: [${commandName}]
  320. Arg countNonOptionParameters: [${countNonOptionParameters}]
  321. Arg dateFormat: [${dateFormat}]
  322. Arg dateInteger: [${dateInteger}]
  323. Arg dateToFormat: [${dateToFormat}]
  324. Arg helpLess: [${helpLess}]
  325. Arg verbose: [${verbose}]
  326. ----------------------------------------------------------
  327. EOM
  328.                 if [ "${1}" != "silent" ] ; then
  329.                         echo "${allArgs}"
  330.                 fi
  331.    fi
  332. }
  333.  
  334.  
  335. # ===  FUNCTION  ===============================================================
  336. #   DESCRIPTION:  Get the ordinal from a number.
  337. #                 - analyses dateInteger - the day of month.
  338. #    PARAMETERS:  -
  339. #       RETURNS:  sets dateOrdinal to st, nd, rd or th.
  340. # ==============================================================================
  341. function computeOrdinal() {
  342.         # Remove leading zeroes.
  343.         if [ -n "${dateInteger}" ] ; then
  344.                 dateInteger=$(echo "${dateInteger}" | sed 's/^0*//')
  345.         fi
  346.  
  347.         if [ $dateInteger -ge 11 -a $dateInteger -le 13 ] ; then
  348.           dateOrdinal="th"
  349.         else
  350.                 case $(( $dateInteger%10 )) in
  351.                 1)
  352.                   dateOrdinal=st
  353.                   ;;
  354.                 2)
  355.                   dateOrdinal=nd
  356.                   ;;
  357.                 3)
  358.                   dateOrdinal=rd
  359.                   ;;
  360.                 *)
  361.                   dateOrdinal=th
  362.                   ;;
  363.                 esac
  364.         fi
  365. }
  366.  
  367. # ------------------------------------------------------------------------------
  368. # -- Script Logic.
  369. # ------------------------------------------------------------------------------
  370.  
  371. # Process all the arguments.
  372. processArguments "$@"
  373.  
  374. outputAllArgs
  375.  
  376. # If no date format, just compute ordinal.
  377. if [ -z "${dateFormat}" ] ; then
  378.  
  379.         # If dateInteger not given, get it from date.
  380.         if [ -z "${dateInteger}" ] ; then
  381.                 dateInteger=$(date --date="${dateToFormat}" +"%d")
  382.  
  383.         # Otherwise, it must be an integer.
  384.         else
  385.                 dateInteger=$(echo $dateInteger | xargs)
  386.                 if ! [[ $dateInteger =~ ^[0-9]+$ ]] ; then
  387.                    usage "Arg dateInteger [$dateInteger] must be an integer." 12
  388.                 fi
  389.                 # How many days do you think there are in a month??
  390.                 if [ "${dateInteger}" -ge 32 ] ; then
  391.                    usage "How many days do you think there are in a monnth? Certainly not [$dateInteger]. Should be 31 or less btw." 14
  392.                 fi
  393.         fi
  394.  
  395.         computeOrdinal
  396.         echo "${dateOrdinal}"
  397.  
  398. # Output whole formatted date.
  399. else
  400.         formattedDate=$(date --date="${dateToFormat}" +"${dateFormat}")
  401.         dateInteger=$(date --date="${dateToFormat}" +"%d")
  402.         computeOrdinal
  403.         formattedDate=$(echo "${formattedDate}" |  sed -e "s|[%]O|${dateOrdinal}|g")
  404.         echo "${formattedDate}"
  405.  
  406. fi
RAW Paste Data
Want to get better at Bash?
Learn to code Bash in 2017
Pastebin PRO Summer Special!
Get 40% OFF on Pastebin PRO accounts!
Top