Date and time computations with GAMAP

From Geos-chem
Jump to: navigation, search

On this page we list some tips & tricks for date and time computations with GAMAP. Please also see the following pages:

--Bob Y. 16:10, 26 November 2008 (EST)

NOTE: For all practical purposes, the terms Greenwich Mean Time (GMT), Universal Coordinated Time (UTC), and Zulu Time (Z) are interchangeable. They all refer to the standard time at Greenwich, UK. We shall use GMT in the discussion below.

Julian Day vs. Astronomical Julian Day

The Julian Day (JD) is used to denote the number of days that have elapsed since the start of a year. For example, the Julian Day number of 2008/01/10 is 10 (since it is the 10th day of 2008).

However, another term that is sometimes confused with the Julian Day is the Astronomical Julian Day (AJD). This is the number of days that have elapsed since 12:00 GMT on January 1, 4713 BC. The Astronomical Julian Day at 00:00 GMT on 2008/01/10 is 2454475.5.

The Astronomical Julian Day is mostly used in astrophysics and space sciences to compute long intervals of time that may span many years (e.g. the period of a variable star or of a comet's orbit, etc.). You can find algorithms for computing AJD in several textbooks, including Practical Astronomy with Your Calculator by Peter Duffett-Smith, Cambridge Univ. Press, 1992.

The chief advantage of using the Astronomical Julian Day is that its computation accounts for the end-of-month, end-of-year, end-of-century, and end-of-millenium transitions. This makes it very useful for computing future or past dates in scripts and computer programs.

For example, if you want to compute what the date will be 1000 days from 2008/01/01, then all you have to do is:

  1. compute the Astronomical Juliay Day for 2008/01/01
  2. add 1000
  3. convert back to a calendar date

We'll return to this example in the next section.

TIP: In order to avoid confusing Julian Day with Astronomical Julian Day, we recommend that you use the terminology "Day of Year" rather than "Julian Day". This will remove all ambiguity.

Astronomical Julian Day routines in IDL and GAMAP

NOTE: GEOS-Chem uses these same algorithms (cf. Practical Astronomy on your Calculator by Peter Duffett-Smith). Equivalent functions are found in the GEOS-Chem utility module GeosUtil/julday_mod.f.

IDL has two Astronomical Julian Day functions

JULDAY
Converts a year, month, day (, hour, min, sec) to Astronomical Julian Day
CALDAT
Converts an Astronomical Julian Day back to year, month, day (, hour, min, sec)

NOTE: The year, month, day are mandatory but you may omit the hour, min, sec. If you omit hour, min, sec, then JULDAY will return an long integer value. If you include hour, min, sec, JULDAY wil return a double precision value.

JULDAY and CALDAT are used as follows:

; Compute the Astronomical Julian Day for 2008/01/10
; Note: the day is required, the hours, mins, seconds are optional
IDL> print, julday( 1, 10, 2008, 0, 0, 0 )
       2454475.5

; Convert the Astronomical Julian Day back to a calendar date
IDL> caldat, 2454475.5, y, m, d, h, mi, s
IDL> print, y, m, d, h, mi, s
           1          10        2008           0           0       0.0000000

To compute the date 1000 days after 2008/01/01 (from the example in the preceding section), you would do the following:

IDL> jd = julday( 1, 1, 2008 ) 
IDL> jd = jd + 1000
IDL> caldat, jd, y, m, d
IDL> print, y, m, d
          9          27        2010

We find that gives us the date 2010/09/27. NOTE: This has also accounted for the leap-year-day on Feb 29, 2008.

For your convenience, GAMAP has a function called ADD_DATE which will do this for you in one fell swoop:

IDL> print, add_date( 20080101, 1000 )
    20100927

You can also use ADD_DATE to compute days prior to a given date. Let's compute what the calendar date was 1000 days prior to Jan 1, 2008:

IDL> print, add_date( 20080101, -1000 )
    20050406

Computing the day of year from a calendar date (and vice-versa)

A quick way to compute the day of the year is w/ IDL's JULDAY function:

Day_of_Year = JULDAY( Month, Day, Year ) - JULDAY( 1, 0, Year )

For example:

IDL> print, julday( 1, 10, 2008 ) - julday( 1, 0, 2008 )
      10

JULDAY( 1, 0, 2008 ) is another way of saying 12/31/2007. This is used so that you don't have to add one to the above equation. (This is equivalent to JULDAY( 1, 10, 2008 ) - JULDAY( 1, 1, 2008 ) + 1.)

The GAMAP function DAY_OF_YEAR does the above computation for you. You can replace the statement above with:

IDL> print, day_of_year( 1, 10, 2008 )
      10

To compute the calendar date from the day of the year (the inverse operation), you can use IDL's CALDAT function. However, you must also add the Astronomical Julian day for the first of the year, for example:

IDL> caldat, julday( 1, 0, 2008 )+10, y, m, d
IDL> print, y, m, d
        1          10        2008

But this is made much easier with the GAMAP functions DAY_OF_YEAR and ADD_DATE:

; Convert month/day/year to day of year
IDL> doy = day_of_year( 1, 10, 2008 )
IDL> print, doy
      10

; convert day of year back to month/day/year
IDL> print, add_date( 20080101, doy-1 )
    20080110

The only thing to remember is you have to subtract 1 from the day of year due to the way that ADD_DATE is written.

NOTE: Starting in GAMAP v2-12, you will be able to specify a single YYYYMMDD argument instead of the month, day, year arguments:

; Convert month/day/year to day of year
IDL> doy = day_of_year( 20080110 )

Working with TAU values

Conceptually similar to the Astronomical Julian Day, the TAU value is a monotonically-increasing time index that is used to timestamp output from the GEOS-Chem and GISS models:

  • GEOS-Chem: TAU is the count of hours since 00:00 GMT on Jan 1, 1985
  • GISS: TAU is the count of hours since 00:00 GMT on Jan 1, 1980

However, unlike the Astronomical Julian Day, TAU is given in hours and not days. Also, the reason for the difference in starting date for TAU is that at the time GEOS-Chem was being created, there existed no meteorological data for it prior to Jan 1, 1985. So 1/1/85 was taken as the start date.

GAMAP ships with 2 functions for working with TAU values: NYMD2TAU and TAU2YYMMDD. They are used as follows:

; Convert a date and time to TAU 
; GEOS-Chem style (from 1985) is the default
IDL> tau_gc = nymd2tau( 20080101, 030000 )
IDL> print, tau_gc
      201603.00

; Convert TAU back to date and time (GEOS-Chem style)
IDL> date = tau2yymmdd( tau_gc, /nformat )
IDL> print, date
   20080101       30000

Note that TAU2YYMMDD returns a 2-element vector with the date and time when you use the /NFORMAT keyword. If you omit this keyword then TAU2YYMMDD will return a structure with year, month, day, hour, minute, second tags:

; Convert TAU back to date and time (GEOS-Chem style)
; but this time return as a structure
IDL> date = tau2yymmdd( tau_gc )
IDL> help, date, /structure
** Structure <2061898>, 6 tags, length=24, data length=24, refs=1:
   YEAR            LONG      Array[1]
   MONTH           LONG      Array[1]
   DAY             LONG      Array[1]
   HOUR            LONG      Array[1]
   MINUTE          LONG      Array[1]
   SECOND          LONG      Array[1]

NYMD2TAU and TAU2YYMMDD assume GEOS-Chem style (from 1 Jan 1985) as the default. To compute TAU for GISS style (from 1 Jan 1980), you can call these functions with the /GISS keyword as follows:

; Convert date to TAU value (GISS style)
IDL> tau_giss = nymd2tau( 20080101, 030000, /giss )
IDL> print, tau_giss
      245451.00

; Convert TAU value back to date (GISS style)
IDL> date = tau2yymmdd( tau_giss, /nformat, /giss )
IDL> print, date
    20080101       30000

Separating YYYYMMDD into year, month day

GAMAP ships with 2 routines which make it easy to extract (or combine) the year, month, day from (to) a date in YYYYMMDD format: DATE2YMD and YMD2DATE:

; Split 20080101 into separate year, month, day variables
IDL> date2ymd, 20080101, y, m, d
IDL> print, y, m, d
       2008           1           1

; Combine year, month, day variables into YYYYMMDD format
IDL> date = ymd2date( 2008, 1, 1 )                   
IDL> print, date
    20080101

You can also use DATE2YMD and YMD2DATE with times in HHMMSS format:

; Split the time 12:30:45 into separate hour, min, sec variables
IDL> date2ymd, 123045, hour, min, sec
IDL> print, hour, min, sec
         12          30          45

; Combine 12h, 30min, 45sec into a single time variable
IDL> time = ymd2date( 12, 30, 45 )
IDL> print, time
      123045

Computing local time at a particular location

Local Time (sometimes called Local Solar Time) is the time at your location defined by the position of the Sun in the sky. It is computed as follows:

Local Time = GMT + ( Longitude / 15 )

Where

  • GMT = Greenwich Mean Time
  • Longitude is in the range -180...180

The Earth rotates 360 degrees in 24 hours, or 15 degrees in one hour. Therefore, your Local Time indicates the number of hours that you are east or west of Greenwich.

Your Local Time may differ slightly from your Standard Time Zone Time, depending on where you are located within your Standard Time Zone. In the Continental US, the official Time Zone Times are defined at the Standard Longitude Meridians of 75W (Eastern), 90W (Central), 115W (Mountain), and 130W (Pacific).

For example, if you live in Boston MA (71.0 W), you would notice that the sun sets at approximately 4:30 PM Eastern on a given day in late December. However, if you live in Cleveland OH (81.7 W), then you would notice that the sun sets at approximately 5:00 PM for the same date.
Boston (71.0 W) is located to the east of the Eastern Time Zone Meridian (75 W). Therefore, the sun will set at Boston earlier than it will at the Standard Time Zone Meridian. So we say that Boston's Local Time is running ahead of Eastern Standard Time.
Similarly, Cleveland (81.7 W) is located to the west of the Eastern Time Zone Meridian (75 W), and its local time is behind that of Eastern Standard Time. Therefore, the sun sets in Cleveland later than at the Eastern Time Zone Meridian...and even later than in Boston.
By convention, both Cleveland and Boston set their clocks to Eastern Standard Time, which is the Local Time at 75W longitude. However, even though Boston and Cleveland may have the same Time Zone Time, their Local Times will differ. This is evidenced by the difference in sunset times at both cities.

NOTE: Local Time does not take Daylight Savings into account. Daylight Savings is only applied to Standard Time Zone Time.

You can compute the Local Time with GAMAP's LOCALTIME function. You need to specify the Greenwich Mean Time and the longitude of your location.

; Local time at Boston @ 0 GMT
; NOTE: West longitude is negative!
IDL> lt_bos = localtime( 0, -71.06 )
IDL> print, lt_bos
      19.2627

; Local time at Cleveland @ 0 GMT
IDL> lt_cle = localtime( 0, -81.68 )
IDL> print, lt_cle
      18.5547

; Difference in local times between Boston & Cleveland
; Boston's Local Time is 0.7 hours (42 min) earlier than Cleveland!
IDL> print, lt_bos - lt_cle
     0.708000

Note that LOCALTIME returns time in decimal hours. You can convert that to minutes by multiplying the result by 60.

Testing for leap years

In the Gregorian calendar, a Leap Year is defined as any year that is:

  1. divisible by 4
  2. or if a century year, divisible by 400

The following ARE leap years: 1600, ... 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016 ...

The following ARE NOT leap years: 1700, 1800, 1900

You can use GAMAP's ISLEAP function to test if a given year is a leap year or not. ISLEAP returns 1 if a year is a leap year and 0 if not. ISLEAP will accept both scalars and vector arguments:

; Test if 2000 is a leap year
IDL> print, isleap( 2000 )
   1 

; Test if 1600, 1700, 1800, 1900, 2000 are leap years
; NOTE: Only century years divisible by 400 are leap years!
IDL> print, isleap( [1600, 1700, 1800, 1900, 2000] )
   1   0   0   0   1

--Bob Y. 12:43, 6 August 2008 (EDT)