The Wayback Machine - https://web.archive.org/web/20200603230615/https://www.shell-tips.com/linux/how-to-format-date-and-time-in-linux-macos-and-bash/

HowTo Format Date and Time in Linux, macOS, and Bash?

  • HOME
  • >
  • LINUX
  • >
  • How To Format Date and Time in Linux, macOS, and Bash?
Last Updated: 

Manipulating and formatting date and time in a shell script is a fairly common task when automating backup processes or generating scheduled reports. Unfortunately, dealing with date and time is never straightforward.

This post provides examples on how to manipulate and format dates on Linux and macOS with the date and awk command lines and with the printfbash shell command builtin.

👉 All the Linux options presented in this post are fully compatible with any Linux deployed on Windows 10 with WSL. Read more about Linux on Windows with my post Running Bash Script With Ubuntu On Windows 10 Using WSL.

Introduction to Date and Time

The complexity of dealing with date and time goes beyond the tooling or programming problem. Very often, consideration will need to be taken regarding timezone, daylight saving, leap year, leap second, standard format, and even sometimes the type of calendar.

What are the different calendar types?

Calendars are a way to organize days for various purposes by giving names to given periods like days, weeks, months, and years. A date is the representation of a single day in a calendar. There is a lot of calendar systems in use still to date some are based on the motion of the moon (Lunar calendars) or perceived seasons (Solar calendars).

The Gregorian calendar is the most widely used calendar system today and is often considered the international standard for civil use. It is a solar calendar with 12 months of 28 to 31 days each and based on 365 days a year, except for leap years, which add an extra day (a leap day) every 4 years in February.

The following post assumes the use of the Gregorian calendar unless specified otherwise.

What are UTC and Timezone?

Universal Coordinated Time (UTC), also known as Coordinated Universal Time, is the co-ordination of time and frequency around the world which began on January 1st, 1960. It is the basis of current timekeeping and is the baseline of all local times. The timezone offset for UTC is 0 and would be indicated with the letter Z. UTC is often interchanged with the Greenwich Mean Time (GMT) though this is not correct as GMT is a timezone, used in some European and African countries, while UTC is a time standard, therefore UTC is never used as a local time.

With UTC come the time zones which are regions of the globes that observe a standard time for the day to day purposes. A time zone relates to local time and is offset from UTC. The time zone offset to UTC is not always represented by whole hours, for example, the Indian Standard Time (IST) is UTC+5:30. Timezones may be identified with a time zone identifier like America/Los_Angeles in the IANA Time Zone Database (also called tzdata, tz, or zoneinfo). Note that the time zone database from IANA contains critical historical information which is necessary when dealing with the local date and time representation at a given time.

Some local time may switch part of the year to a time zone that includes a notion of daylight saving time (DST).

What is Unix Time?

The Unix time is a point in time that describes the number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), excluding leap seconds. It is part of the POSIX specification. Other frequent names for Unix time are Epoch time, POSIX time, seconds since the Epoch, or UNIX Epoch time. Every day in Unix time assumes an 86,400 seconds day. It is not a true representation of UTC.

👉 Read more about leap seconds and go back in time with our post on the 2012 leap second bug that took down half of the internet: Getting Ready For The Leap Second.

Unix time starts at zero (0) which is 00:00:00 UTC on 1 January 1970.

⚠️ Some systems still store this value in as a signed 32-bit integer which may cause large bugs on January 19, 2038 once the upper limit of the integer is reached. This is known as the Y2038 bug (Year 2038).

What is the ISO 8601 standard?

The ISO 8601 is the International Standard for the representation of dates and times to ensure consistent representation across systems in a gregorian calendar. It is a field-based format where dates and times are identified (2020-05-09T03:44:25Z) in contrast to the Unix Time (1547092066).

The standard uses the specifiers from the below table to represent a date and time. Note that the standard calls for a minimum of 4 digits year to avoid a repeat of the Y2K bug (Year 2000).

specifier/tokenmeaning
YYYYfour-digit year
MMtwo-digit month (01=January, etc.)
wwweek number from 1 to 53
Dweek day number from 1 to 7 and beginning with Monday
DDtwo-digit day of month (01 through 31)
hhtwo digits of hour (00 through 23) (am/pm NOT allowed)
mmtwo digits of minute (00 through 59)
sstwo digits of second (00 through 59)
sone or more digits representing a decimal fraction of a second
Z or +hh:mm or -hh:mmtime zone designator
/used to express a time interval between two time points

Examples:

  • 2020-05-09T13:49:54+00:00
  • 2020-05-09T13:49:54Z

Using Calendars from the command line

Julian and Gregorian calendars with cal and ncal

The command line tools cal and ncal on linux and macOS provide a convenient way to display Julian or Gregorian calendar information from a shell. The main difference between the two is with the default calendar view as cal will show each day of the week as a column while the ncal command will show, by default, the day of the week on different rows (vertical format or 'sideways'). That said, ncal with the option -C can also display the day of the week in column as cal does, and cal itself with the -N option can show the vertical view.

The cal utility displays a simple calendar in a traditional format and ncal offers an alternative layout, more options, and the date of Easter.

Both utility will highlight the current date.

[me@host ~]$ cal May 2020 Su Mo Tu We Th Fr Sa 12345678910111213141516171819202122232425262728293031[me@host ~]$ ncal May 2020 Mo 4111825 Tu 5121926 We 6132027 Th 7142128 Fr 18152229 Sa 29162330 Su 310172431[me@host ~]$ cal20202020 January February March Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 12341123456756789101123456788910111213141213141516171891011121314151516171819202119202122232425161718192021222223242526272826272829303123242526272829293031... 

To get the calendar view of a specific year and month with cal (or ncal) you can use the -d option followed by the year and month.

[me@host ~]$ cal -d 1991-11 November 1991 Su Mo Tu We Th Fr Sa 123456789101112131415161718192021222324252627282930

To display the calendar using Julian days (numbered days from January 1 to December 31) with cal and ncal you can use the -j option, for example, you can see below that 2020 has 366 days and is a leap year and the last day of the year will be a Thursday.

[me@host ~]$ cal -j -m 12 December 2020 Su Mo Tu We Th Fr Sa 336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366

Now, to be honest, besides getting a quick calendar view from the command line, I have never found those two utilities being used reasonably in any automation or production systems. This is more for fun or to be informative than anything else.

Holidays and Events with the calendar command line on Linux

The command line calendar on Linux is defined as a reminder service that can send notifications and check a given directory for specified calendars definition to displays lines that will begin with a given date. This can be convenient to list a country's frequent holidays or create your customized list of recurring events. The tool is available on Linux and will require the cpp binary to compile the calendar files.

[me@linux ~]$ calendar -f /usr/share/calendar/calendar.usholiday -l 30 -B 30 Apr 15 Income Tax Day, USA. Apr 28* Arbor Day, USA (varies from state to state) May 10* Mother's Day (2nd Sunday of May) May 16* Armed Forces in USA Day (3rd Saturday of May) May 25* Memorial Day in USA (Last Monday of May)

Display and Format dates with the date command line

The most widely used command line tool to manipulate date in Linux and Unix is certainly the date shell command. Remember that macOS is based on Unix and will also come with the Unix date shell command. Though, there is some important catch to be aware as the implementation of the two utility diverges greatly. One way to guarantee consistent behavior is to make sure to always use the GNU date version of the utility. It comes pre-installed by default on most Linux distributions, if not you will need to install the coreutils package. On Ubuntu/Debian you can run apt install coreutils. The GNU coreutils can also be installed on macOS using homebrew brew install coreutils. The GNU date command line will then be available as gdate on macOS.

The date shell command uses the date [options] +FORMAT where the format is a serie of format specifiers used to define the output format of the date and time. The options and format specifiers may vary between platforms. The below date formatting will work across Unix and Linux platforms.

[me@host ~]$ date'+Today is %A, %B %d, %Y.' Today is Saturday, May 09, 2020. 

Difference between Linux and Unix/macOS date command line

It is important to understand that the POSIX standard (POSIX.2) only specifies the -u option and the operand +format of the shell date utility. You can find the full specification in Posix 2 IEEE Std 1003.2. This means that any other options or operands in the date shell utility are systems specific implementation and may not be portable across platforms or environments.

The macOS date shell utility is compatible with the POSIX.2 standard and the -d, -f, -j, -n, -R, -r, -t, and -v options are all extensions to the standard.

The Linux date shell utility is the GNU date shell version which is also compatible with the POSIX.2 standard and also sets a large number of options that are all extensions to the standard. The Linux version of date is sometime called gdate when installed on different platforms.

⚠️ The non-standard options are not guaranteed to have the same behavior between Linux (GNU version) and Unix/macOS. A typical example would be the -d option which displays the time as described by a string on the Linux GNU version while it is used to set the kernel's value for daylight saving time on macOS.

The option -u will set the date in UTC instead of using the local time zone. Since it's part of the POSIX.2 specification both date version from Linux and Unix/macOS would display the same output for the command date -u.

Using the GNU date command line

Before bash version 5, it was common to use the date '+%s' command to get the current Unix time, this should now be avoided since you can directly use the $EPOCHSECONDS shell variable. See What's New in GNU Bash 5?.

[me@linux ~]$ date'+%s'&&echo$EPOCHSECONDS15890538401589053840

Another variant has been to use date to get the real-time in microseconds, though GNU date will return nanoseconds while bash provides $EPOCHREALTIME which is in microseconds directly. So, depending on the case you may have to do a simple division in bash to get the correct value. The example below can be easily ported to get milliseconds instead. See the man date (or man gdate) pages for the full list of formatting options.

[me@linux ~]$ echo$((`date '+%s%N'`/1000))&&echo$EPOCHREALTIME15890538409167661589053840.918266# Extra tips to get the same microseconds formatting[me@linux ~]$ m=$((`date '+%s%N'`/1000))&&echo${m:0:${#m}-6}.${m:${#m}-6}&&echo$EPOCHREALTIME1589053840.9182661589053840.918266# Example to get time with milliseconds and separated with a dot[me@linux ~]$ m=$((`date '+%s%N'`/1000000))&&${m:0:${#m}-3}.${m:${#m}-3}1589054941.851

Another widely used option of GNU date is the -d option which makes handling dates extremely easy as you can use human-readable words to define a date. The option allows you to easily do operations on dates which is a big strength of the GNU date utility.

[me@linux ~]$ date -d 'last year' Thu May 913:01:43 PDT 2019[me@linux ~]$ date -d '90 days ago' Sun Feb 912:03:34 PST 2020[me@linux ~]$ date -d 'Feb 12 + 3 day' -u Sat Feb 15 00:00:00 UTC 2020[me@linux ~]$ date -d 'Feb 12 + 13 minutes' -u Wed Feb 12 00:13:00 UTC 2020

Using the Unix/macOS date command line

The macOS date shell command is quite more limited than the GNU version. Though, you can still use it for basic formatting. See the man date pages for the full list of formatting options. You can have the same behavior as GNU date-d option by using the -j (don't try to set date, display only) and -v (adjust date) options.

[me@macos ~]$ date'+%Y/%m/%d %H:%M:%S'2020/05/09 13:20:2 [me@macos ~]$ date -j -v +3d -f "%b %d""Feb 10" Thu Feb 1312:47:10 PST 2020

Display and Format dates with the GNU awk command line

GNU awk is often used to parse and process logs in an efficient way, luckily it can also be powerful when it comes to manipulating dates. Again, most of those features are extensions and are not specified by the POSIX standard.

mktimeTurn a datespec (YYYY MM DD HH MM SS) into a timestamp
strftimeFormat a specified time to the given format as defined by strftime()
systimeReturn the Unix Time stamp (Epoch)
[me@macos ~]$ gawk'BEGIN { print "Seconds since Epoch: " mktime("2020 05 09 14 47 27") }' Seconds since Epoch: 1589060689[me@macos ~]$ gawk'BEGIN { print "Seconds since Epoch: " systime() }' Seconds since Epoch: 1589060689[me@macos ~]$ gawk'BEGIN { print strftime("Today is %A, %B %d, %Y.", systime()) }' Today is Saturday, May 09, 2020. 

The example below from the gawkman pages show an example of awk reproducing the date command behavior and formatting.

#! /bin/sh## date --- approximate the POSIX 'date' commandcase$1in -u)TZ=UTC0 # use UTCexport TZ shift;;esacgawk'BEGIN { format = PROCINFO["strftime"] exitval = 0 if (ARGC > 2) exitval = 1 else if (ARGC == 2) { format = ARGV[1] if (format ~ /^\+/) format = substr(format, 2) # remove leading + } print strftime(format) exit exitval }'"$@"

Display and Format dates by using printf bash builtin

The bash printf builtin command support some date formatting using %(datefmt)T where datefmt is a format string passed to strftime and the corresponding argument is the Epoch time.

[me@linux ~]$ TZ=UTC printf"%(%F)T\n"15890538402020-05-09 [me@linux ~]$ TZ=UTC printf"This date occurred in week %(%W)T of the year %(%Y)T\n"$EPOCHSECONDS This date occurred in week 18 of the year 2020

Using printf makes the formatting to microseconds even simpler than when using the date command.

[me@linux ~]$ printf"%.3f\n"$EPOCHREALTIME1589054941.851[me@linux ~]$ printf"%.3f\n"$EPOCHREALTIME|tr -d .1589054941851

Detailed Examples

How to format date in Bash?

The following examples use the date command line. The date command use case-sensitive format specifiers, so the uppercase or lowercase letters in format would have different results. In the below examples, using %M instead of %m would mean minutes instead of month.

Bash Date format MM-DD-YYYY

To format date in MM-DD-YYYY format, use the command date +%m-%d-%Y or printf "%(%m-%d-%Y)T\n" $EPOCHSECONDS.

Bash Date format DD-MM-YYYY

To format date in DD-MM-YYYY format, use the command date +%d-%m-%Y or printf "%(%d-%m-%Y)T\n" $EPOCHSECONDS.

Bash Date format YYYY-MM-DD

To format date in YYYY-MM-DD format, use the command date +%F or printf "%(%F)T\n" $EPOCHSECONDS. The %F option is an alias for %Y-%m-%d.

This format is the ISO 8601 format.

Bash date format options

Below is a list of common date format options and examples output.

Date Format OptionMeaningExample Output
date +%clocale's date timeSat May 9 11:49:47 2020
date +%xlocale's date05/09/20
date +%Xlocale's time11:49:47
date +%Alocale's full weekday nameSaturday
date +%Blocale's full month nameMay
date +%m-%d-%YMM-DD-YYYY date format05-09-2020
date +%DMM/DD/YY date format05/09/20
date +%FYYYY-MM-DD date format2020-05-09
date +%THH:MM:SS time format11:44:15
date +%uDay of Week6
date +%UWeek of Year with Sunday as first day of week18
date +%VISO Week of Year with Monday as first day of week19
date +%jDay of Year130
date +%ZTimezonePDT
date +%mMonth of year (MM)05
date +%dDay of Month (DD)09
date +%YYear (YY)2020
date +%HHour (HH)11
date +%HHour (HH) in 24-hour clock format11
date +%IHour in 12-hour clock format11
date +%plocale's equivalent of AM or PMAM
date +%Psame as %p but in lower caseam

You can get the full list of supported bash date format specifiers using the man date or man gdate commands.

How to add a day to a date?

To get the date in bash shell and add a date, you can use the GNU date command as date -d 'Feb 12 +1 day' +%F which will output 2020-02-13. See above section on GNU date for more example on the -d option.

On macOS with the Unix date command, you would need to use the command as such date -j -v +1d -f "%b %d" "Feb 12" +%F.

How to get yesterday's date?

To get yesterday's date in bash shell you can use the Linux GNU date version with date -d yesterday or the Unix/macOS date version using date -j -v -1d.

How to do date comparison in bash?

To compare two dates in bash, you can use the unix timestamp value of both date using the date command line or the printf method. You can also do a lexicographical comparison of the two dates string by using the double square bracket conditional construct [[..]].

# By comparing Unix Timestamp[me@linux ~]$ firstDate=$(date -d 'Feb 12 UTC' +%s)[me@linux ~]$ secondDate=$(date -d 'Feb 13 UTC' +%s)[me@linux ~]$ if(($secondDate > $firstDate));thenecho"Second Date is more recent than First Date";fi Second Date is more recent than First Date # By doing lexical comparion[me@linux ~]$ firstDate=$(date -d 'Feb 12 UTC' +"%F %T")[me@linux ~]$ secondDate=$(date -d 'Feb 13 UTC' +"%F %T")[me@linux ~]$ if[[$secondDate>$firstDate]];thenecho"Second Date is more recent than First Date";fi Second Date is more recent than First Date 

How to calculate the difference between two dates?

There is a naive way to do this easily with any of the commands above which is to simply subtract both dates from their Unix timestamp value and then convert the seconds to hours or days as needed. For a Math in Bash refresher, see the post Performing Math Calculation In Bash.

⚠️ When using this naive date comparison method, make sure to always use the UTC standard for the date, to avoid troubles with daylight saving. Also, note that this is still a very naive way to diff two dates and it does not consider the more complex scenarios where you may require the support of leap seconds or leap years.

[me@linux ~]$ firstDate=$(date -d 'Feb 12 UTC' +%s)[me@linux ~]$ secondDate=$(date -d 'Feb 13 UTC' +%s)[me@linux ~]$ echo$firstDate$secondDate15814944001581580800[me@linux ~]$ echo"The difference is $(((secondDate-firstDate)/3600)) hours" The difference is 24 hours 

How to calculate the time elapsed in Bash?

The simplest option is to use the special shell variable $SECONDS to get the amount of seconds elapsed. This special variable returns a count of the number of whole seconds since the shell or script has been running. The variable can be reset to zero or any other integer.

[me@linux ~]$ SECONDS=0;sleep2;echo"Time elapsed since \$SECONDS was reset: $SECONDS seconds"; Time elapsed since $SECONDS was reset: 2 seconds 

Alternatively, you can calculate the difference between two dates as described in the section How to calculate the difference between two dates?.

How to convert the Unix Timestamp to UTC? (i.e. Unix to Date format)

The easiest way to convert a Unix Timestamp to UTC is to use GNU date with the @ feature and the -u option.

[me@linux ~]$ date -d @1547092066 -u Thu Jan 10 03:47:46 UTC 2019

Another option would be to use the bash printf builtin and set the TZ environment variable to UTC. The %+ specifier uses the date and time format as in the date command line.

[me@linux ~]$ TZ=UTC printf"%(%+)T\n"1547092066 Thu Jan 10 03:47:46 UTC 2019
Related linux posts that you may like
How to Script Powerful Bash If Statement?
Learn how to script a Bash If statement with the then, else, and else if / elif clauses. This post covers the Bash conditional expressions, and how to avoid common pitfalls when using the Bash If statement.
sudo: no tty present and no askpass program specified
Learn what is askpass and how to solve the 'sudo: no tty present and no askpass program specified' error when using sudo to execute a command.
What is the Bash Null Command?
Learn about the Bash null command, also known as the POSIX shell colon command. This post cover concrete use cases and pitfalls to avoid.
close