Advertisement
robertmarkbram

Output ordinal part of date from current date or input integ

Nov 3rd, 2015
402
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 14.11 KB | None | 0 0
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement