This format is defined by ISO 8601 standard. Basically, it defines the order of the fields (always "year-month-day-hour-minute-second-offset"), beyond their values and sizes:
- year with at least 4 digits (ie year 1 should be written as
0001
)
- month, day, hour, minute and second always with 2 digits, and completed with a zero to the left if it is less than 10 (ie, February should be
02
instead of just 2
)
- month between
01
and 12
- day between
01
and 31
- time between
00
and 24
(although the value 24
means "the end of the day", which is the same as midnight the next day - that is to say, 2019-08-05T24:00
is the same as 2019-08-06T00:00
)
- minute between
00
and 59
- second between
00
and 60
(where the value 60
is used to indicate a Leap Second)
- fractions of a second are also allowed (in fact, hours and minutes also allow fractions, provided that this is the last present element, i.e.,
14:30.5
and 14:30:30
would be equivalent, but 14:30.5:01
would be invalid)
Between date and time, always put the letter "T" capital letters, although it may be replaced by a space, provided that there is a "mutual agreement" between the parties that will handle the date (there are also other standards such as RFC 3339, which allows a space in place of the T
, among other differences).
The ISO 8601 standard also defines the separators (hyphens between year, month and day; two-point between hour, minute and second), although it also admits a basic format without separators. I mean, both 2015-02-12T15:20:16-02:00
how much 20150212T152016-0200
would be valid* (the last hyphen before the 02
is not a separator, but the negative sign, as we will see below).
* When I say that both formats are valid, I am referring to the definitions of ISO 8601. This does not necessarily mean that your application will accept both (always refer to the respective documentation).
Finally, the standard also defines the Time zone Signatory, which despite the name, is not in fact a Timezone.
Timezones are a set of rules that define the local time of a given region, including the changes it undergoes over time. The most common and used are the timezones of the TZDB of the IANA, defining identifiers as America/Sao_Paulo
and Europe/London
. Each identifier has the history of changes that the local time of your respective region has suffered over time. The Timezone America/Sao_Paulo
, for example, it has all the daylight saving time changes corresponding to the Brasilia Daylight Saving Time (ie the history of all the exact dates and times the clock was put forward or put back an hour).
The name of Timezone is not America/Brasilia
because IANA criteria is to use the name of the largest city in each region
Anyway, what the ISO 8601 standard uses in its format is actually a offset, which is basically the difference with respect to UTC. For example, the Timezone America/Sao_Paulo
is, for most of the year, 3 hours behind the UTC, which is represented by the offset -03:00
. But during daylight saving time, the clocks are advanced by an hour and it passes to be 2 hours behind UTC (i.e., the offset mute to -02:00
).
In 2019 Brazil will not have daylight saving time, but Timezone America/Sao_Paulo
keeps the history of previous years, so when working with dates in the past, you can know the correct offset of that time. And anyway, these rules change all the time, and there’s no guarantee that the government won’t change its mind in the future and adopt summer time again.
Note that the offset (the difference with respect to UTC) changed from -03:00
for -02:00
, but the Timezone remains the same (America/Sao_Paulo
). That’s why this field with the value -02:00
, although it is called "time zone signator", it actually corresponds to the offset. You can see more details about this on tag wiki timezone
, in the section "Differences between Timezone and offset".
OBS: the another answer says this format is the "UTC standard", but that’s not quite it. The UTC pattern is something else: it sets a standard base time, from which all time zones in the world are based. But UTC by itself nay defines a specific format for dates and times. ISO 8601 defines the format in question.
Another detail is that ISO 8601 admits "incomplete" values, such as only the date (2019-08-22
), only the hour (14:30
- inclusive, minutes and seconds may be omitted), or only the year (2019
), or still only the month and day (--08-22
).
How to obtain this format
In PHP, you can use the constant DATE_ATOM
, together with the method DateTime::format
, or with the function date
:
$d = new DateTime("2015-02-12T15:20:16-02:00");
echo $d->format(DATE_ATOM); // 2015-02-12T15:20:16-02:00
echo date(DATE_ATOM, strtotime('12-02-2015 15:20:16 -02:00')); //2015-02-12T15:20:16-02:00
You could also use "Y-m-d\TH:i:sP"
instead of DATE_ATOM
, as you suggested another answer. The result is the same as the code above. Using this form would be interesting if you need the "incomplete" formats, for example only the date (which would be "Y-m-d"
) or just the time ("H:i:s"
), or the date and time without the offset ("Y-m-d\TH:i:s"
). But for full format, like date, time and offset, use DATE_ATOM
already resolves.
If you want, you can change the Timezone (and consequently the offset), using the class DateTimeZone
:
// data no offset -02:00
$d = new DateTime("2015-02-12T15:20:16-02:00");
// converte para outro timezone
$d->setTimeZone(new DateTimeZone('Asia/Tokyo'));
echo $d->format(DATE_ATOM); // 2015-02-13T02:20:16+09:00
In this case, I created the date referring to offset -02:00
and then converted to the Timezone Asia/Tokyo
. The result was the corresponding date and time in Japan’s time zone. Note that because of this, offset moved to +09:00
(9 hours ahead of UTC), and the date and time became 13 to 2 in the morning, because this is the date and time corresponding to this same instant in Japan (that is, while it was 12 to 15h in Brazil, in Japan it was 13 to 2h).
If you do not specify any Timezone, will be used the default that is configured in your environment. In my case, for example, the default is America/Sao_Paulo
, but if I change it to any other, this will be used (consequently, the offset will also change):
echo date_default_timezone_get(); // America/Sao_Paulo (pode não ser o mesmo no seu ambiente)
$d = new DateTime();
echo $d->format(DATE_ATOM); // 2019-08-28T14:18:58-03:00
// mudar o timezone default para o fuso da Alemanha
date_default_timezone_set('Europe/Berlin');
$d = new DateTime();
echo $d->format(DATE_ATOM); // 2019-08-28T19:18:58+02:00
How the question is tagged javascript, follows a solution in this language (with reservations, read until the end):
let d = new Date("2015-02-12T15:20:16-02:00");
console.log(d.toISOString()); // 2015-02-12T17:20:16.000Z
The detail is that the method toISOString()
converts the date/time to UTC. That’s why the result was 17h (not 15h), since 15h no offset -02:00
(i.e., two hours behind UTC) equals 17h in UTC - and note the "Z" at the end, which is the form used to designate who is in UTC (another way is to use the offset zero: +00:00
).
If you want the date/time in a offset specific, it is a little more complicated. There is the method getTimezoneOffset()
, that returns the offset of Timezone which is configured in the browser (which usually it gets from the operating system), and this value is returned in minutes (but with the detail that it returns with "the reversed signal"; for example, if the offset for -03:00
, he returns 180
, and if it is +03:00
, he returns -180
).
It’s not a very pretty code, but anyway:
let d = new Date("2015-02-12T15:20:16-02:00");
let offset = d.getTimezoneOffset();
let output = d.getFullYear() + '-' + (d.getMonth() + 1).toString().padStart(2, '0') + '-' +
d.getDate().toString().padStart(2, '0') + 'T' +
d.getHours().toString().padStart(2, '0') + ':' +
d.getMinutes().toString().padStart(2, '0') + ':' +
d.getSeconds().toString().padStart(2, '0') +
`${ offset > 0 ? '-' : '+' }${ Math.floor(offset / 60).toString().padStart(2, '0') }:${ (offset % 60).toString().padStart(2, '0') }`;
console.log(output); // 2015-02-12T15:20:16-02:00
The problem is that this solution is limited to Timezone browser. If you want to use other timezones, an alternative is to use toLocaleString
, but it is a very limited solution:
let d = new Date("2015-02-12T15:20:16-02:00");
console.log(d.toLocaleString('sv', { timeZone: 'America/Sao_Paulo' })); // 2015-02-12 15:20:16
console.log(d.toLocaleString('sv', { timeZone: 'Asia/Tokyo' })); // 2015-02-13 02:20:16
I used the locale sv
(Swede), which uses a format close to ISO 8601 (replace the space with the letter "T"), but still it does not show the offset (which would have to be calculated separately). But this is not a portable solution as it depends on the locale sv
be available/installed on the system, and in addition those definitions dependent on the locale can change (not so common, but can happen, as explained at the end of this reply).
Another option would be to use the library Moment Timezone. With it it is possible to convert the date to any other Timezone:
let d = moment.tz('2015-02-12T15:20:16-02:00', 'America/Sao_Paulo');
console.log(d.format('YYYY-MM-DD[T]HH:mm:ssZ')); // 2015-02-12T15:20:16-02:00
// usar outro timezone
d = moment.tz('2015-02-12T15:20:16-02:00', 'Asia/Tokyo');
console.log(d.format('YYYY-MM-DD[T]HH:mm:ssZ')); // 2015-02-13T02:20:16+09:00
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data.min.js"></script>
The T in the middle there is a separator only?
– Henrique Silva
Yes, the T is fixed.
– Jéf Bueno
Thanks friend, helped me a lot.
– Henrique Silva