Convert a String Time to/from a Numeric Time
In various places in ERDDAP, a point in time can be represented as either:
- A String -
ERDDAP always uses the
ISO 8601:2004 "extended" format
yyyy-MM-ddTHH:mm:ssZ - for example,
Datasets outside of ERDDAP often use a different format for string time values.
- A Number -
When representing times as numbers, ERDDAP always writes a
- for example, n=473472000 units="seconds since 1970-01-01T00:00:00Z".
ERDDAP uses that exact units string for all datasets.
Data sets outside of ERDDAP often use different units strings.
This web page has converters that can change string time values (with various formats)
to/from numeric time values (with various units strings).
It also has converters to change time strings and units strings that use
other formats into the formats used by ERDDAP.
To avoid time zone and daylight savings confusion,
time values in ERDDAP always use the Zulu (UTC, GMT) time zone, denoted by 'Z'.
The converters below accept string times with other time zones.
Reset the form to the default values.
Or, bypass this web page
and do time conversions from within a computer program, script, or web page.
Notes about the Time Converter
- DISCLAIMER OF LIABILITY -
No person or organization associated with this web site
makes any warranty, express or implied, including warranties of merchantability
and fitness for a particular purpose, or assumes any legal liability for the accuracy,
completeness, or usefulness, of any information at this web site.
- IMPERFECT -
These converters are imperfect. Always check the results from these converters.
These converters accept a large number of string time formats, but there will
always be formats that they don't recognize or interpret differently than you would.
In some cases,
there are two or more common, but different, ways to interpret a given
time format, notably #/#/#: in the U.S. that is usually intended to be month/date/year,
whereas in most of Europe that is usually intended to be date/month/year.
In that particular case, ERDDAP interprets the value as month/date/year.
- UDUNITS Time Units - For example, "seconds since 1970-01-01T00:00:00Z".
The first word can be (upper or lower case):
ms, msec, msecs, millis, millisecond, milliseconds,
s, sec, secs, second, seconds,
m, min, mins, minute, minutes,
h, hr, hrs, hour, hours,
d, day, days,
mon, mons, month, months,
yr, yrs, year, or years.
"since" is required.
So another example is "hours since 0001-01-01".
The time string can be in any format. The date part is required. The time part is optional. ERDDAP will do its best to read the format that you provide.
The recommended format is the ISO 8601:2004 "extended" format yyyy-MM-ddTHH:mm:ss.SSSZ,
where Z is 'Z' or a ±hh or ±hh:mm offset from the Zulu/GMT time zone.
If you omit Z and the offset, the Zulu/GMT time zone is used.
Separately, if you omit .SSS, :ss.SSS, :mm:ss.SSS, or Thh:mm:ss.SSS, the
missing fields are assumed to be 0.
Technically, ERDDAP does NOT follow the UDUNITS standard when converting "years since"
and "months since" time values to "seconds since". The UDUNITS standard defines a
year as a fixed, single value: 3.15569259747e7 seconds. And UDUNITS defines a month
as year/12. Unfortunately, most/all datasets that we have seen that use
"years since" or "months since" clearly intend the values to be calendar years
or calendar months. For example, 3 "months since 1970-01-01" is usually intended
to mean 1970-04-01. So, ERDDAP interprets "years since" and "months since" as
calendar years and months, and does not strictly follow the UDUNITS standard.
- Converter Precision -
When representing times as numbers, this converter always
leaves the numbers at their full precision. When representing times as Strings,
this converter formats times to the (truncated) second (i.e., omitting millis).
- Invalid Input -
ERDDAP tries to deal with improperly formatted input.
If ERDDAP can't deal with the input, ERDDAP will generate an error message.
It the "invalid input" is a string time that you think should be supported,
please email it to bob.simons at noaa.gov
How ERDDAP Deals with Time
- Time is a difficult, complex, messy issue.
- Goal - An underlying goal for ERDDAP's system for dealing with time is
to have a single
system that allows time data from any dataset to be compared directly to time data from
any other dataset. Please keep that in mind when you read the other comments below.
- Time Points -
ERDDAP only deals with time points (a combined
date+time in the
Zulu time zone, each an instant in time).
- Time Zones -
When writing time values, ERDDAP always uses the Zulu (UTC, GMT)
time zone and never uses daylight savings times. Time data from a source with
time zone information (which may incorporate daylight savings time information)
is converted to Zulu time. Time data from a source without time zone information
is assumed to be already in Zulu time.
- Precision -
ERDDAP deals with time internally as
"seconds since 1970-00-00T00:00:00Z",
ignoring leap seconds, stored as double precision floating point numbers. Thus,
most time data can be stored very precisely (roughly to the nearest microsecond,
but progressively less precisely very far in the past or future).
When doing calculations, ERDDAP often works to the nearest millisecond, to
avoid problems with floating point numbers that are slightly more or less
than you expect.
When representing times as numbers, ERDDAP always leaves the numbers at their full
By default, when representing times as Strings, ERDDAP formats times to the
(truncated) second (i.e., omitting millis). However, ERDDAP administrators can
configure specific variables in specific datasets to represent String times at
other precisions (e.g., from millisecond precision up to month precision; see the
<time_precision> variable attribute). Whenever a time field (e.g., milliseconds
or seconds) is not shown, the absent value is assumed to be 0 (except for
day-of-month, which is assumed to be 1).
- Consistent Units - Using the same units for time
("seconds since 1970-00-00T00:00:00Z")
for all datasets allows times from different datasets to be compared easily.
Seconds are the
International System of Units (SI)
base unit of time.
1970-01-01T00:00:00Z is the start of the Unix epoch, which is widely used by computer
operating systems. "seconds since 1970-00-00T00:00:00Z" are widely used and are
or epoch seconds. The use of the units string
"seconds since 1970-00-00T00:00:00Z" makes Unix time compatible with
and the Climate and Forecast Metadata Conventions (CF),
which uses UDUNITS-2 for units
- Text Representation - By default, when formatting times as
text, ERDDAP uses the
ISO 8601:2004 "extended" format String,
truncated to the second
(e.g., 2011-04-26T14:07:12Z). (Related humor:
ERDDAP administrators can configure a given variable to display the time data values
to greater precision (truncated to 0.001 second, 0.01 second, 0.1 second)
or to lesser precision (truncated to second, minute, hour, date, or month)
to indicate the precision of the time data values.
When reading ISO 8601 times with decimal seconds, ERDDAP accepts a period separator
(e.g., 2011-04-26T14:07:12.059Z) or a comma separator (e.g., 2011-04-26T14:07:12,059Z).
Currently, when writing ISO 8601 times ERDDAP always uses a period separator.
- Gregorian Calendar -
ERDDAP uses the Gregorian Calendar for times after the
discontinuity in 1582 and the Julian calendar for times before the discontinuity.
(The ISO 8601:2004 standard doesn't specify how to handle times before 1582.)
Java GregorianCalendar class
(which ERDDAP uses) for details.
For example, 17611992 hours since 0001-01-01T00:00:00Z = 2010-03-01T00:00:00Z
(see for yourself)
Currently, ERDDAP does not support any other calendar (including the noleap, 365_day,
360_day, and Julian calendars defined by the
CF metadata conventions).
However, you can store such data in ERDDAP by storing the numbers or
Strings in a variable that is
named something other than "time" and doesn't include "since" in the units metadata.
Then, ERDDAP won't interpret the values as times and won't convert the data to
"seconds since 1970-01-01T00:00:00Z". Users will be able to access the data in its
- Lenient -
ERDDAP is "lenient" when it parses date/time strings. That means
that date/times with the correct format, but with month, date, hour, minute,
and/or second values that are too large or too small will be rolled to the
appropriate date/times. For example, ERDDAP interprets 2001-12-32 as
2002-01-01, and interprets 2002-01-00 as 2001-12-31.
(It's not a bug, it's a feature! We understand that you may object to this
if you are not familiar with lenient parsing. We understand there are
circumstances where some people would prefer strict parsing, but there are also
circumstances where some people would prefer lenient parsing. ERDDAP can't
have it both ways. This was a conscious choice. Lenient parsing is the default
behavior in Java, the language that ERDDAP is written in and arguably the
most-used computer language. Also, this behavior is consistent with ERDDAP's
conversion of requested grid axis values to the nearest valid grid axis value.
And this is consistent with some other places in ERDDAP that try to repair
invalid input when the intention is clear, instead of just returning an error
- BCE (BC) Years and Astronomical Year Numbering -
As specified in the ISO 8601:2004 standard, ERDDAP just uses the Common
Era (CE, AD) for year numbers, not BCE. For years before 1 CE, historians
use the BCE or BC designation and no year 0, so their sequence of years near
the era transition is 2 BCE, 1 BCE, 1 CE, 2 CE. For years before 1 BCE, the
ISO 8601:2004 standard (according to my reading of it)
encourages the use of a different system:
Astronomical Year Numbering,
in which 1 BC is called year 0000, 2 BC is
called year -0001, etc. Since Astronomical Year Numbering is encouraged by
ISO 8601:2004 and used by many scientific fields, and because oceanographers
and climatologists often use year 0000 for climatologies, ERDDAP uses
Astronomical Year Numbering. Thus, the years near the era transition in
ERDDAP are -0001, 0000, 0001, 0002, respectively. Thus, for BCE years:
AstronomicalYear = 1 - BCEYear (see for yourself:
An advantage of Astronomical Year Numbering is that all leap years are
divisible by 4, e.g., ..., -0008, -0004, 0000, 0004, 0008, ....
If you remember to use Astronomical Year Numbers, ERDDAP will work for
dates far in the past and future.
project stores time as "days since -4713-01-01T00:00:00Z",
which is a reference to midnight at the start of January 1, 4713 BCE, which
is the start time for Chronological Julian Dates (CJD).
So, in the datasets.xml
setup for a SeaDataNet dataset in ERDDAP, in the <addAttributes> section for
all time variables, you need to add
<att name="units">days since -4712-01-01T00:00:00Z</att>
so that 4713 BCE is expressed as the Astronomical Year Number -4712.
As a check, note the start of Chronological Julian day number 2,452,952
2452952 "days since -4712-01-01") is 2003-11-08T00:00:00Z.
- UTC -
As much as is practical, ERDDAP, like Unix time and UDUNITS-2, uses the
Coordinated Universal Time (UTC)
time system. UTC is the modern version of the GMT
time system. UTC uses occasional leap seconds to stay close to the International Atomic
Time (TAI). GPS devices use GPS time (which doesn't use leap seconds) internally,
but display UTC time to users. (See
discussions of Systems of Time.) Strictly speaking, UTC can't be used in some
situations for far future times (e.g., in models), since the occurrence of leap
seconds can't be predicted.
- Leap Seconds -
Like Unix time and UDUNITS-2, ERDDAP's
"seconds since 1970-00-00T00:00:00Z" is a numeric encoding (which doesn't use leap
seconds) of UTC time (which does use leap seconds). When converting string times
to/from numeric times, ERDDAP, like Unix time and UDUNITS-2, ignores leap seconds.
Yet, ERDDAP, Unix time, and UDUNITS-2 numeric times all say they use UTC.
Here's why and how:
Since computer clocks are not good time keepers, they drift from UTC time and have
to be reset to UTC time occasionally. Unix time treats leap seconds as part of the time
deviation caused by the clock's drift. This allows software to do time encoding and
time calculations and ignore leap seconds. With ERDDAP, Unix time, and UDUNITS-2
numeric time values, all minutes are treated as having 60 seconds, even though UTC
minutes with leap seconds have 61 seconds. Note that database programs like
(and so probably MariaDB also),
and languages like
also basically try to ignore leap seconds.
This won't cause problems when converting UTC String date+time+timeZone values into
ERDDAP numeric values if the times are then reconverted back to date+time+Zulu
Strings in a way that also ignores leap seconds. The two errors will cancel each
other out. One unfortunate consequence is that ERDDAP, Unix time, and UDUNITS-2
numeric times have no mechanism to encode actual leap seconds as numbers
unambiguously. For example, both the leap second
and the next second
2009-01-01T00:00:00Z are encoded as
1230768000 seconds since 1970-01-01T00:00:00Z. This is a flaw, but only affects
actual leap seconds. Similarly, if you need to calculate the exact number of UTC
seconds between two points in time, you can use ERDDAP to handle the time data,
but subtracting one numeric time value from the other is just the first step in
the calculation. You need manually adjust that value by consulting a
table of leap seconds.
- Low Resolution Times and Time Spans (Time Ranges) -
If time data was recorded to
a lower resolution (e.g., a minute, hour, day, month, or year) or represents a time
span (e.g., a specific hour, day, 7-day period, month, year), ERDDAP requires that
a time point (we call it the "nominal time") be used to represent that time or time
span. Using a nominal time allows time points, low resolution time points, and time
spans to be interoperable. (Imagine visualizing different datasets in a program like
Google Earth and using a time slider to control which subset of data is visible.)
The most commonly selected time points for the nominal time are the start time and
the centered time (less common, but has advantages for use with time sliders). We
recommend that you use the long_name metadata to identify this (e.g., "Start Time" or
"Centered Time"). For low resolution times, consider e.g., "Date", "Month", and "Year".
And/or, put the details in the variable's "comment" metadata.
Or, to more accurately represent time spans, you can set up a dataset to have both
a beginTime and an endTime variable (and even a centeredTime, if you want
to cover all possibilities).
- These are imperfect answers. The current setup deals reasonably
well with most
situations and matches most people's expectations about how time works (even if they
haven't thought about it in detail), but not all. But that's the way ERDDAP works
If you change the extension of this web page's URL from .html to .txt,
ERDDAP will respond with just the text result.
- An example which converts a string time to a numeric time is:
Or, you can use some other format, e.g.,
- An example which converts a numeric time into a string time is:
The recommended format for the base time in the units is the ISO 8601 format, but you can specify the base time in any common format, e.g.,
Note that the "slash" format is interpreted the U.S. way (as month/date/year)
not as the European way (as date/month/year).
- In some previous versions of ERDDAP,
the String to numeric converter required an ISO 8601 formatted
String and used the parameter name isoTime. You can still use that, e.g.,
If you specify isoTime, "&units=..." parameter is optional. The default value is "seconds since 1970-01-01T00:00:00Z".
- An example which converts any common string time into an ISO 8601
String time is:
You must specify a date portion. The time portion is optional.
If you just specify a date, the converter will return value will just be a date.
If you specify a date portion and a time portion, the converter will return
a date and a time.
Note that the "slash" format is interpreted the U.S. way (as month/date/year)
not as the European way (as date/month/year).
- An example which converts a units string into a units string with an
ISO string time is:
Percent Encoding - The parameter values in the URL (the parts after '=' signs) must be properly
all characters other than A-Za-z0-9_-!.~'()* must be encoded as %HH,
where HH is the 2 digit hexadecimal value of the character, for example, a space becomes
%20. Characters above #127 must be converted to UTF-8 bytes, then each UTF-8 byte must
be percent encoded (ask a programmer for help). There are
web sites that percent encode and decode for you.