However, my dreams were ruined. When I delved into the task, I realized that in this language it is really difficult to work with time zones. Implementing something more complicated than simply formatting the time display and calculating the date using complex operations (calendar functions) was extremely difficult. I gained valuable experience solving this problem, and this entailed new difficulties.
What is the time zone?
The time zone is a geographic region in which the uniform local time is used, set by the government of the country. Many countries entirely belong to some specific time zones, and in the territories of large states, like Russia and the United States, several time zones are used. It is curious that although China is also quite large, however, it adopted only one time zone. Sometimes this leads to strange situations when sunrise begins in the western part of the country around 10 am.
GMT, UTC and offset
Local South Korean time is denoted as
GMT+09:00. GMT stands for Greenwich Mean Time (Greenwich Mean Time), that is, the time on the Royal Observatory clock in Greenwich, UK. It is located on the zero meridian. The GMT-system's radio signal began broadcasting on February 5, 1924, and it itself became a world standard on January 1, 1972.
Many people believe that GMT and UTC are the same thing, often using them as interchangeable systems. But this is a mistake. The UTC system appeared in 1972 as a way to compensate for the effect of the rotation of the Earth. The system is based on International Atomic Time (International Atomic Time), calculated from the frequency of electromagnetic oscillations of cesium atoms. In other words, UTC is a more accurate replacement for GMT. Although the real time difference between the two systems is very small, it is still better for software developers to rely on UTC.
An interesting fact: when UTC was still being developed, in English-speaking countries it was suggested to call it CUT (Coordinated Universal Time), and in French-speaking countries - TUC (Temps Universal Coordonn). However, none of the camps could not win, and the system agreed to call UTC, by letter of both the proposed options (C, T and U).
UTC+09:00mean that the local time is 9 hours ahead of UTC-time standard. That is, when it is 9 pm in South Korea, it is noon in the UTC region. The difference between standard time and UTC-local called "offset", which is expressed as a positive or negative values:
In many countries it is accepted to give their time zones unique names. For example, the time zone of South Korea is called KST (Korea Standard Time), its offset is expressed as
KST = UTC+09:00. However, the displacement is
+09:00used not only by South Korea, but also by Japan, Indonesia and many other countries, therefore the connection between displacements and belts is expressed not as 1: 1, but as 1: N. The list of countries with offset
+09:00is presented here .
Some offsets operate not only on the clock. For example, in North Korea, the standard time is
+08:30, and in Australia in some regions are used
The full list of UTC offsets is here .
Time zone! == offset?
As I already said, we use time zone names (KST, JST) with displacements interchangeably, not distinguishing them. But it will be wrong to consider the same time and the shift of a particular region. There are several reasons:
Summer time (DST)
In some countries, this term is unknown, but in many states there is a daylight saving time, mainly in Europe. For this, the international term DST is adopted - Daylight Saving Time. It means the transfer of hours in the summer period one hour ahead of the relative standard time.
For example, in California in the winter PST (Pacific Standard Time, Pacific Standard Time) is used, and in the summer PDT (Pacific Daylight Time,
UTC-07:00) is used. In the United States and Canada, the term Pacific Time (PT, Pacific Time) applies to regions that use two time zones.
When does summer time begin and end? It all depends on the country. For example, in the United States and Canada until 2006, DST was used from 2 am on the first Sunday of April to 12 am on the last Sunday of October. And from 2007, summertime began to count from 2 am on the second Sunday of March to 2 am on the first Sunday of November. In Europe, in different countries, the progressive use of DST is practiced depending on each time zone.
Do time zones change?
Each country itself determines which time zones to use it, so the local time may vary for any political and / or economic reasons. For example, in the USA, the DST borders were changed in 2007, because George Bush initiated an energy policy in 2005. Egypt and Russia used to switch to summer time, but they refused from 2011.
In some cases, the government can change not only the order of daylight saving time, but also the standard time. For example, earlier on Samoa offset was used
UTC-10:00, but then they switched
UTC+14:00to reduce losses in trade due to the time difference with Australia and New Zealand. This decision led to the loss of the whole day from the life of the country - December 30, 2011, about which newspapers around the world wrote .
In the Netherlands, the shift has been applied since 1909
+0:19:32.13, since 1937 the country has moved to
+00:20, and from 1940 to
+01:00, since then, most of the time has not changed there.
Time Zone 1: N Offset
So, the time zone can have one or more offsets. What time is taken as the standard depends on the current political and / or economic reasons in a particular country.
In everyday life, this does not cause difficulties until you try to systematize this data on the basis of some rules. Imagine that you want to set a standard time on your smartphone using an offset. If you live in a region where daylight saving time is practiced, then the smartphone should know exactly when to go back and forth. That is, you need to establish the relationship between standard and summer time in the same time zone (for example, Pacific Time).
But this can not be done with a couple of simple rules. For example, when the beginning and the end of DST were changed in the USA in 2007, May 31, 2006 was supposed to use PDT (
-07:00) as the standard time , and March 31, 2007 - PST (
-08:00). It turns out that in order to refer to a specific time zone, you need to know the whole history of changing time zones or the date of changing the rules for daylight saving time.
You can say: "The time zone in New York is PST (
-08:00)." However, it is necessary to clarify: "The current time zone in New York is PST." At the same time for the exact implementation of the system you need to use an even more accurate expression. Forget the term "time zone". You have to say: “PST is now used as a standard time in New York.”
So what should we use instead of offset to determine the time zone of a particular region? The name of this region. More precisely, you should group into a single time zone those regions in which the same time goes to daylight saving time and standard time. Names like PT (Pacific Time) can be used, but they only combine current standard and summer time, and do not necessarily take into account all historical changes. Moreover, since PT is only available in the United States and Canada, you need to rely on established standards from reputable organizations to ensure the versatility of your software.
IANA Time Zone Database
I must confess that time zone information is rather a database, not a set of rules, because this information should contain all relevant historical changes. There are several standard databases designed for solving problems related to time zones. The most commonly used IANA Time Zone Database , and usually called its tz database (or tzdata). The database contains historical data on changes in standard time and DST across the globe. Moreover, it is organized so that you can check all the historical data and verify the accuracy of time since Unix time (
1970.01/01 00:00:00). Although you can find information in the database until 1970, their accuracy is not guaranteed.
The naming convention uses the “region / place” rule. As the region is usually used the name of the continent or ocean (Asia, America, Pacific), and as a place - the names of major cities (Seoul, New York). The reason is that cities usually exist longer than countries. For example, the time zone of South Korea is
Asia/Seoul, and Japan is
Asia/Tokyo. Although both countries use the same offset
UTC+09:00, their local time varied in different ways, so they were divided into different time zones.
The IANA database is run by many communities of developers and historians. Freshly-available historical data is immediately entered into it and current policies are updated, so that today the base can be considered the most reliable source. Moreover, it is used under the hood of many Unix-systems, including Linux and MacOS, as well as a number of popular programming languages, including Java and PHP.
Please note that Windows uses a Microsoft database . However, it is inaccurate in terms of historical data and is supported only by Microsoft itself. Therefore, the base is less reliable than the IANA base.
This is an implementation-dependent algorithm that uses the best available time zone information to determine the Daylight SavingTA (t) setting for local time, which is calculated in milliseconds. The ECMAScript implementation should help best define the local summer time setting.
Looks like they just say: "Dude, try to make it work." Among other things, you have to solve the problem of compatibility with different browsers. You say, “What a mess!”, And then read the following line:
Note: We recommend that you use information from the IANA time zone database http://www.iana.org/time-zones/ in your implementations .
Intl.DateTimeFormat. But this option is much less reliable than analogs in other programming languages.
Time Zone in Server-Client Environment
Consider a simple scenario: we need to define a time zone. Suppose we create a calendar that will process time information. When a user in the client environment enters the date and time in the registration window, the date is transferred to the server and stored in the database. Then the client receives from the server the date registered in the schedule for display on the screen.
Here you need to decide on something. What if some of the clients accessing the server are in different time zones? The event in the schedule, which is registered in Seoul on March 10, 2017 at 23.30, in New York should be displayed as March 10, 2017 at 9.30. In order for the server to serve clients from different time zones, the schedule stored on it must contain absolute values that do not depend on the belt in any way. Each server has its own way of storing such values, this question is beyond the scope of the article, since everything depends on the particular server or database. In general, the date and time transmitted from the client to the server should be presented either as values based on a single offset (usually UTC), or as values containing information about the time zone of the client environment.
Typically, such data is transmitted in the form of Unix-time in UTC format or according to the ISO-8601 standard with offset information. If in our example we convert Seoul to Unix-time at 21.30 on March 10, 2017, we get an integer value
1489113000. And in the ISO-8601 format, a string value will be obtained
Even when you work with JS in a server environment using Node.js, you may need to parse the data received from the client. But since the time zones of servers and databases are usually synchronized, and formatting is imposed on clients, in a browser environment you need to determine several factors. Further I will explain with reference to the browser environment.
Tasks involving work with a given or time are solved using an object
Date. This is a native object defined in ECMAScript, like
Function. That is, it is, for the most part, implemented using native code like C ++. The API is well described in the MDN documentation . The object was greatly influenced by the java.util.Date class from Java, so it inherited some undesirable properties, such as the characteristics of changeable data and the month starting at zero (
Under the hood, an object
setHour()and others affect the client's time zone (more precisely, the belt specified in the OS in which the browser is running). So if you create an object
Datedirectly using the data entered by the user, then the local time zone of the client will be reflected in this data.
Creating a Date object using user-entered data
Let's return to the first example. Suppose a user entered Seoul time at 11:30 am on March 11, 2017. This data is saved as five numbers: 2017, 2, 11, 11, and 30 — year, month, day, hour, and minute, respectively (since the month starts at 0, its the value should be 3–1 = 2). Using the constructor, you can easily create an object
const d1 = newDate(2017, 2, 11, 11, 30);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)
If you look at the value returned
d1.toString(), you will see that the absolute value of the created object is 11.00 on March 11, 2017, it is calculated using confusion
You can use in the constructor and string data. If you apply them to
Date, the object will internally call
Date.parse()and calculate the correct value. This feature supports the specifications of RFC2888 and ISO-8601 . But the MDN documentation for Date.parse () says that the value returned by this method depends on the browser, and the format of the string type can affect the exact final value. Therefore, it is better not to use this method. For example, in Safari and Internet Explorer a string value seems to
NaN, and in Chrome and Firefox returns the local time zone. In some situations, a UTC-based value is returned.
Creating a Date object using server data
Suppose you want to get data from a server. If they are in the form of numeric Unix time, then
Dateyou can simply use a constructor to create an object . I have not mentioned that when the constructor
Dategets one value as a single parameter, it calculates the Unix time value in milliseconds (note: JS processes Unix time in milliseconds. This means that the second value needs to be multiplied by 1000). When executing the following code, we will get the same value as in the previous example:
const d1 = newDate(1489199400000);
d1.toString(); // Sat Mar 11 2017 11:30:00 GMT+0900 (KST)
And if instead of Unix-time to use the string type ISO-8601? As I explained above, then the method
Date.parse()becomes unreliable and it is better not to use it. However, starting with ECMAScript 5, you can
Dateuse strings in the ISO-8601 format in the designer in Internet Explorer 9.0 and higher.
If you are not using the most recent browser, then make sure that there is a letter
Zat the end of the values. Without it, your old browser can interpret the value based on local time, not UTC. Here is an example of using Internet Explorer 10:
const d1 = newDate('2017-03-11T11:30:00');
const d2 = newDate('2017-03-11T11:30:00Z');
d1.toString(); // "Sat Mar 11 11:30:00 UTC+0900 2017"
d2.toString(); // "Sat Mar 11 20:30:00 UTC+0900 2017"
According to the specification, in both cases the same value should be obtained. But they are different. In a later browser, the values will be the same. To avoid this problem, always add
Zat the end of the line if there is no information about the time zone.
Creating a date to send to the server
Now you can use the previously created
Date, freely retrieve or add time based on local time zones. Only at the end of processing do not forget to convert the data to the previous format before returning to the server.
If this is Unix time, you can use the method
getTime()(do not forget about milliseconds).
const d1 = newDate(2017, 2, 11, 11, 30);
d1.getTime(); // 1489199400000
What about the string value in ISO-8601 format? As I said, Internet Explorer 9.0 and above support ECMAScript 5, and later versions support ISO-8601. Therefore, using the methods
toJSON()you can create a string in ISO-8601 (
toJSON()can be used for recursive calls with
JSON.stringify()and others). Both methods give the same result, except for the cases of processing incorrect data:
const d1 = newDate(2017, 2, 11, 11, 30);
d1.toISOString(); // "2017-03-11T02:30:00.000Z"
d1.toJSON(); // "2017-03-11T02:30:00.000Z"const d2 = newDate('Hello');
d2.toISOString(); // Error: Invalid Date
d2.toJSON(); // null
To create a string in UTC format, you can use the
toUTCString(). This will result in a value that meets the requirements of RFC-1123 .
toLocaleString()and their extension methods. But there is little benefit from them, since they are mainly used to return rows based on the local time zone, and the return values depend on the browser and OS.
Changing the local time zone
As you can see, JS has some support for time zones. And if you need in the application to change the local belt without taking into account the value specified in the OS? Or if you need to display several time zones simultaneously in one application? As I have said several times, JS does not allow you to manually change the local time zone. The only solution would be to add or remove an offset from the date for which you already know the offset value of the desired belt. But don't be upset. Perhaps there is another way.
Let's return to our example with Seoul. Let this time zone be specified in the browser. The user enters Seoul time at 11.30 on March 11, 2017 and wants to see it as the local New York time. The server sends the date in Unix-time in milliseconds and notifies that the New York offset is
-05:00. Now you can convert the data if you know the local time zone offset.
In this scenario, you can use the method
const seoul = newDate(1489199400000);
seoul.getTimeZoneOffset(); // -540
-540means that the time zone is 540 minutes ahead of the target. Note the minus, although the Seoul offset contains a plus (
+09:00). I do not know why, but it is displayed that way. If we use this method to calculate the offset for New York, we get
60 * 5 = 300. Transform the difference
840in milliseconds and create a new object
Date. Now we will use its method
getXXto convert the value to the format you need. Create a simple formatting function to compare the results:
return date.getFullYear() + '/' +
(date.getMonth() + 1) + '/' +
date.getDate() + ' ' +
date.getHours() + ':' +
const seoul = newDate(1489199400000);
const ny = newDate(1489199400000 - (840 * 60 * 1000));
formatDate(seoul); // 2017/3/11 11:30
formatDate(ny); // 2017/3/10 21:30
formatDate()shows the correct date and time according to the time zone difference between Seoul and New York. Looks like we found a simple solution. Is it possible to convert to the local time zone if we know the offset of the desired region? Unfortunately not. I have already said that time zone data is a database that stores all historical changes in offsets. So, to get the correct time zone value, you need to know the offset to the desired date (not the current one).
Local time zone conversion problem
If you work a little more with the example above, you will soon run into a problem. Suppose a user wants to check the local time in New York, and then change the date from 11 to 15. If you use the method
setDate()from the object
Date, you can change the date without affecting the other values.
formatDate(ny); // 2017/3/15 21:30
It looks simple, but there is a hidden trap. What happens if you need to send data back to the server? They have changed, so you can not use methods
getISOString(). Therefore, it is necessary to do the inverse transformation before sending data to the server.
const time = ny.getTime() + (840 * 60 * 1000); // 1489631400000
Some may wonder why I added the use of transformed data, because you still have to convert them back before returning. After all, you can process the data without conversion and temporarily create the converted object
Dateonly during formatting? Not. If the object is
Date based on the Seoul time to change the date from the 11th to the 15th, then 4 days are added (
24 * 4 * 60 * 60 * 1000). But in the case of the local New York time, the date changed from 10th to 15th, that is, 5 days was added (
24* 5 * 60 * 60 * 1000). So to get an accurate result, you need to calculate data based on local offset.
The problems do not end there. You will not get the desired value simply by adding or subtracting the offset. Since for New York, summer time begins on March 12, the offset for March 15, 2017 is equal to
-05:00. And when you do the inverse transformation, you need to add 780 minutes, which is 60 minutes less than before.
const time = ny.getTime() + (780 * 60 * 1000); // 1489627800000
On the other hand, if the user's local time zone refers to New York and you need to know the time in Seoul, then the unnecessary use of summer time will lead to another problem.
Simply put, you cannot use the offset alone to perform accurate operations based on the selected time zone. If we put together everything that we have already discussed, it will become clear that there are still difficulties with summer time. To get the exact value, you need a database that contains the entire history of changing offsets, like the same IANA database .
To solve this problem, you will have to store the entire database, and when extracting the date or time from the object
Datefind this date and the corresponding offset, and then convert the value according to the process described above. In theory, all this is realizable. But in fact, you have to spend too much energy, not to mention testing the reliability of the converted data. Although not in a hurry to get upset. So far, we have discussed only some of the problems of JS and how to solve them. Now you can use a good library.
I will not talk in detail about the use of the library or its structure. Just show you how it can be used to easily solve the problems described above. For details, send the documentation .
So, use the Moment Timezone:
const seoul = moment(1489199400000).tz('Asia/Seoul');
const ny = moment(1489199400000).tz('America/New_York');
seoul.format(); // 2017-03-11T11:30:00+09:00
ny.format(); // 2017-03-10T21:30:00-05:00
seoul.date(15).format(); // 2017-03-15T11:30:00+09:00
ny.date(15).format(); // 2017-03-15T21:30:00-04:00
seoulremains the same, and the offset
nyhas changed from
-04:00. And if you use the function
format(), you can get a string in ISO-8601 format, in which the offset is neatly applied. It turns out much simpler than the solution given by me above.
getTimezoneOffset(). But if you need full time zone support, don't do it from scratch. Better use a library like the Moment Timezone.