Summary of answers and other data: (please add yours)
Do:
Whenever you are referring to an exact moment in time, persist the time according to a unified standard that is not affected by daylight savings. (GMT and UTC are equivalent with this regard, but it is preferred to use the term UTC. Notice that UTC is also known as Zulu or Z time.)
If instead you choose to persist a (past) time using a local time value, include the local time offset for this particular time from UTC (this offset may change throughout the year), such that the timestamp can later be interpreted unambiguously.
In some cases, you may need to store both the UTC time and the equivalent local time. Often this is done with two separate fields, but some platforms support a datetimeoffset type that can store both in a single field.
When storing timestamps as a numeric value, use Unix time - which is the number of whole seconds since 1970-01-01T00:00:00Z (excluding leap seconds). If you require higher precision, use milliseconds instead. This value should always be based on UTC, without any time zone adjustment.
If you might later need to modify the timestamp, include the original time zone ID so you can determine if the offset may have changed from the original value recorded.
When scheduling future events, usually local time is preferred instead of UTC, as it is common for the offset to change. See answer, and blog post.
When storing whole dates, such as birthdays and anniversaries, do not convert to UTC or any other time zone.
When possible, store in a date-only data type that does not include a time of day.
If such a type is not available, be sure to always ignore the time-of-day when interpreting the value. If you cannot be assured that the time-of-day will be ignored, choose 12:00 Noon, rather than 00:00 Midnight as a more safe representative time on that day.
Remember that time zone offsets are not always an integer number of hours (for example, Indian Standard Time is UTC+05:30, and Nepal uses UTC+05:45).
If using Java, use java.time for Java 8 and later.
Much of that java.time functionality is back-ported to Java 6 & 7 in the ThreeTen-Backport library.
Further adapted for early Android (< 26) in the ThreeTenABP library.
If using Python 3.9 or later, use the built-in zoneinfo for working with time zones. Otherwise, use dateutil or arrow. The older pytz library can generally be avoided.
If using PHP > 5.2, use the native time zones conversions provided by DateTime, and DateTimeZone classes. Be careful when using DateTimeZone::listAbbreviations() - see answer. To keep PHP with up to date Olson data, install periodically the timezonedb PECL package; see answer.
Do not use Boost for time zone conversions. While its API claims to support standard IANA (aka "zoneinfo") identifiers, it crudely maps them to POSIX-style data, without considering the rich history of changes each zone may have had. (Also, the file has fallen out of maintenance.)
Most business rules use civil time, rather than UTC or GMT. Therefore, plan to convert UTC timestamps to a local time zone before applying application logic.
Remember that time zones and offsets are not fixed and may change. For instance, historically US and UK used the same dates to 'spring forward' and 'fall back'. However, in 2007 the US changed the dates that the clocks get changed on. This now means that for 48 weeks of the year the difference between London time and New York time is 5 hours and for 4 weeks (3 in the spring, 1 in the autumn) it is 4 hours. Be aware of items like this in any calculations that involve multiple zones.
Consider the type of time (actual event time, broadcast time, relative time, historical time, recurring time) what elements (timestamp, time zone offset and time zone name) you need to store for correct retrieval - see "Types of Time" in this answer.
Keep your OS, database and application tzdata files in sync, between themselves and the rest of the world.
On servers, set hardware clocks and OS clocks to UTC rather than a local time zone.
Regardless of the previous bullet point, server-side code, including web sites, should never expect the local time zone of the server to be anything in particular. see answer.
Prefer working with time zones on a case-by-case basis in your application code, rather than globally through config file settings or defaults.
Do not confuse a "time zone", such as America/New_York with a "time zone offset", such as -05:00. They are two different things. See the timezone tag wiki.
Do not use JavaScript's Date object to perform date and time calculations in older web browsers, as ECMAScript 5.1 and lower has a design flaw that may use daylight saving time incorrectly. (This was fixed in ECMAScript 6 / 2015).
Never trust the client's clock. It may very well be incorrect.
Don't tell people to "always use UTC everywhere". This widespread advice is shortsighted of several valid scenarios that are described earlier in this document. Instead, use the appropriate time reference for the data you are working with. (Timestamping can use UTC, but future time scheduling and date-only values should not.)
Testing:
When testing, make sure you test countries in the Western, Eastern, Northern and Southern hemispheres (in fact in each quarter of the globe, so 4 regions), with both DST in progress and not (gives 8), and a country that does not use DST (another 4 to cover all regions, making 12 in total).
Test transition of DST, i.e. when you are currently in summer time, select a time value from winter.
I'm not sure what I can add to the answers above, but here are a few points from me:
Types of times
There are four different times you should consider:
Event time: eg, the time when an international sporting event happens, or a coronation/death/etc. This is dependent on the timezone of the event and not of the viewer.
Television time: eg, a particular TV show is broadcast at 9pm local time all around the world. Important when thinking about publishing the results (of say American Idol) on your website
Relative time: eg: This question has an open bounty closing in 21 hours. This is easy to display
Recurring time: eg: A TV show is on every Monday at 9pm, even when DST changes.
There is also Historic/alternate time. These are annoying because they may not map back to standard time. Eg: Julian dates, dates according to a Lunar calendar on Saturn, The Klingon calendar.
Storing start/end timestamps in UTC works well. For 1, you need an event timezone name + offset stored along with the event. For 2, you need a local time identifier stored with each region and a local timezone name + offset stored for every viewer (it's possible to derive this from the IP if you're in a crunch). For 3, store in UTC seconds and no need for timezones. 4 is a special case of 1 or 2 depending on whether it's a global or a local event, but you also need to store a created at timestamp so you can tell if a timezone definition changed before or after this event was created. This is necessary if you need to show historic data.
Storing times
Always store time in UTC
Convert to local time on display (local being defined by the user looking at the data)
When storing a timezone, you need the name, timestamp and the offset. This is required because governments sometimes change the meanings of their timezones (eg: the US govt changed DST dates), and your application needs to handle things gracefully... eg: The exact timestamp when episodes of LOST showed both before and after DST rules changed.
Offsets and names
An example of the above would be:
The soccer world cup finals game
happened in South Africa (UTC+2--SAST)
on July 11, 2010 at 19:00 UTC.
With this information, we can historically determine the exact time when the 2010 WCS finals took place even if the South African timezone definition changes, and be able to display that to viewers in their local timezone at the time when they query the database.
System Time
You also need to keep your OS, database and application tzdata files in sync, both with each other, and with the rest of the world, and test extensively when you upgrade. It's not unheard of that a third party app that you depend on did not handle a TZ change correctly.
Make sure hardware clocks are set to UTC, and if you're running servers around the world, make sure their OSes are configured to use UTC as well. This becomes apparent when you need to copy hourly rotated apache log files from servers in multiple timezones. Sorting them by filename only works if all files are named with the same timezone. It also means that you don't have to do date math in your head when you ssh from one box to another and need to compare timestamps.
Also, run ntpd on all boxes.
Clients
Never trust the timestamp you get from a client machine as valid. For example, the Date: HTTP headers, or a javascript Date.getTime() call. These are fine when used as opaque identifiers, or when doing date math during a single session on the same client, but don't try to cross-reference these values with something you have on the server. Your clients don't run NTP, and may not necessarily have a working battery for their BIOS clock.
Trivia
Finally, governments will sometimes do very weird things:
Standard time in the Netherlands was
exactly 19 minutes and 32.13 seconds
ahead of UTC by law from 1909-05-01
through 1937-06-30. This time zone
cannot be represented exactly using
the HH:MM format.
或者他的意思是“时间的物理瞬间”,连续体中的一个点
我们宇宙的时间线,比如说,“当下一次日食发生时
在这种情况下,答案 B) 是正确的。
一些日期/时间 API 正确区分了这一点:其中, Jodatime,它是下一个(第三个!)Java DateTime API (JSR 310) 的基础。
This is an important and surprisingly tough issue. The truth is that there is no completely satisfying standard for persisting time. For example, the SQL standard and the ISO format (ISO 8601) are clearly not enough.
From the conceptual point of view, one usually deals with two types of time-date data, and it's convenient to distinguish them (the above standards do not) : "physical time" and "civil time".
A "physical" instant of time is a point in the continuous universal timeline that physics deal with (ignoring relativity, of course). This concept can be adequately coded-persisted in UTC, for example (if you can ignore leap seconds).
A "civil" time is a datetime specification that follows civil norms: a point of time here is fully specified by a set of datetime fields (Y,M,D,H,MM,S,FS) plus a TZ (timezone specification) (also a "calendar", actually; but lets assume we restrict the discussion to Gregorian calendar). A timezone and a calendar jointly allow (in principle) to map from one representation to another. But civil and physical time instants are fundamentally different types of magnitudes, and they should be kept conceptually separated and treated differently (an analogy: arrays of bytes and character strings).
The issue is confusing because we speak of these types events interchangeably, and because the civil times are subject to political changes. The problem (and the need to distinguish these concepts) becomes more evident for events in the future. Example (taken from my discussion here.
John records in his calendar a reminder for some event at datetime 2019-Jul-27, 10:30:00, TZ=Chile/Santiago, (which has offset GMT-4,
hence it corresponds to UTC 2019-Jul-27 14:30:00). But some day
in the future, the country decides to change the TZ offset to GMT-5.
Now, when the day comes... should that reminder trigger at
A) 2019-Jul-27 10:30:00 Chile/Santiago = UTC time 2019-Jul-27 15:30:00 ?
or
B) 2019-Jul-27 9:30:00 Chile/Santiago = UTC time 2019-Jul-27 14:30:00 ?
There is no correct answer, unless one knows what John conceptually meant
when he told the calendar "Please ring me at 2019-Jul-27, 10:30:00
TZ=Chile/Santiago".
Did he mean a "civil date-time" ("when the clocks in my city tell
10:30")? In that case, A) is the correct answer.
Or did he mean a "physical instant of time", a point in the continuus
line of time of our universe, say, "when the next solar eclipse
happens". In that case, answer B) is the correct one.
A few Date/Time APIs get this distinction right: among them, Jodatime, which is the foundation of the next (third!) Java DateTime API (JSR 310).
Make clear architectural separation of concerns - to know exactly which tier interacts with users, and has to change date-time for/from canonical representation (UTC). Non-UTC date-time is presentation (follows users local timezone), UTC time is model (remains unique for back-end and mid tiers).
Also, decide what's your actual audience, what you don't have to serve and where do you draw the line. Don't touch exotic calendars unless you actually have important customers there and then consider separate user-facing server(s) just for that region.
If you can acquire and maintain user's location, use location for systematic date-time conversion (say .NET culture or a SQL table) but provide a way for end-user to choose overrides if date-time is critical for your users.
If there are historical audit obligations involved (like telling exactly when Jo in AZ paid a bill 2 yrs ago in September) then keep both UTC and local time for the record (your conversion tables will change in a course of time).
Define the time referential time zone for data that comes in bulk - like files, web services etc. Say East Coast company has data center in CA - you need to ask and know what they use as a standard instead of assuming one or the other.
Don't trust time-zone offsets embedded in textual representation of the date-time and don't accept to parse and follow them. Instead always request that time zone and/or reference zone have to be explicitly defined. You can easily receive time with PST offset but the time is actually EST since that's the client's reference time and records were just exported at a server which is in PST.
奥尔森数据发生了变化,部分原因是 AD Olson 即将退休,部分原因是维护者因侵犯版权而提起诉讼(现已驳回)。时区数据库现在由互联网号码分配机构 IANA 管理,前面有一个链接页面到“时区数据库”。讨论邮件列表现已[电子邮件受保护];公告列表为[电子邮件受保护]。
You need to know about the Olson tz database, which is available from ftp://elsie.nci.nih.gov/pubhttp://iana.org/time-zones/. It is updated multiple times per year to deal with the often last-minute changes in when (and whether) to switch between winter and summer (standard and daylight saving) time in different countries around the world. In 2009, the last release was 2009s; in 2010, it was 2010n; in 2011, it was 2011n; at the end of May 2012, the release was 2012c. Note that there is a set of code to manage the data and the actual time zone data itself, in two separate archives (tzcode20xxy.tar.gz and tzdata20xxy.tar.gz). Both code and data are in the public domain.
This is the source of time zone names such as America/Los_Angeles (and synonyms such as US/Pacific).
If you need to keep track of different zones, then you need the Olson database. As others have advised, you also want to store the data in a fixed format — UTC is normally the one chosen — along with a record of the time zone in which the data was generated. You may want to distinguish between the offset from UTC at the time and the time zone name; that can make a difference later. Also, knowing that it is currently 2010-03-28T23:47:00-07:00 (US/Pacific) may or may not help you with interpreting the value 2010-11-15T12:30 — which is presumably specified in PST (Pacific Standard Time) rather than PDT (Pacific Daylight Saving Time).
The standard C library interfaces are not dreadfully helpful with this sort of stuff.
The Olson data has moved, in part because A D Olson will be retiring soon, and in part because there was a (now dismissed) law suit against the maintainers for copyright infringement. The time zone database is now managed under the auspices of IANA, the Internet Assigned Numbers Authority, and there's a link on the front page to 'Time Zone Database'. The discussion mailing list is now [email protected]; the announcement list is [email protected].
In general, include the local time offset (including DST offset) in stored timestamps: UTC alone is not enough if you later want to display the timestamp in its original timezone (and DST setting).
Keep in mind that the offset is not always an integer number of hours (e.g. Indian Standard Time is UTC+05:30).
For example, suitable formats are a tuple (unix time, offset in minutes) or ISO 8601.
Crossing the boundary of "computer time" and "people time" is a nightmare. The main one being that there is no sort of standard for the rules governing timezones and daylight saving times. Countries are free to change their timezone and DST rules at any time, and they do.
Some countries e.g. Israel, Brazil, decide each year when to have their daylight saving times, so it is impossible to know in advance when (if) DST will be in effect. Others have fixed(ish) rules as to when DST is in effect. Other countries do not use DST as all.
Timezones do not have to be full hour differences from GMT. Nepal is +5.45. There are even timezones that are +13. That means that:
SUN 23:00 in Howland Island (-12)
MON 11:00 GMT
TUE 00:00 in Tonga (+13)
are all the same time, yet 3 different days!
There is also no clear standard on the abbreviations for timezones, and how they change when in DST so you end up with things like this:
AST Arab Standard Time UTC+03
AST Arabian Standard Time UTC+04
AST Arabic Standard Time UTC+03
The best advice is to stay away from local times as much as possible and stick to UTC where you can. Only convert to local times at the last possible moment.
When testing make sure you test countries in the Western and Eastern hemispheres, with both DST in progress and not and a country that does not use DST (6 in total).
The DateTimeZone class in PHP > 5.2 is already based on the Olson DB which others mention, so if you are doing timezone conversions in PHP and not in the DB, you are exempt of working with (the hard-to-understand) Olson files.
However, PHP is not updated as frequently as the Olson DB, so just using PHPs time zone conversions may leave you with outdated DST information and influence the correctness of your data. While this is not expected to happen frequently, it may happen, and will happen if you have a large base of users worldwide.
To cope with the above issue, use the timezonedb pecl package. Its function is to update PHP's timezone data. Install this package as frequently as it is updated. (I'm not sure if the updates to this package follow Olson updates exactly, but it seems to be updated at a frequency which is at least very close to the frequency of Olson updates.)
If your design can accommodate it, avoid local time conversion all together!
I know to some this might sound insane but think about UX: users process near, relative dates (today, yesterday, next Monday) faster than absolute dates (2010.09.17, Friday Sept 17) on glance. And when you think about it more, the accuracy of timezones (and DST) is more important the closer the date is to now(), so if you can express dates/datetimes in a relative format for +/- 1 or 2 weeks, the rest of the dates can be UTC and it wont matter too much to 95% of users.
This way you can store all dates in UTC and do the relative comparisons in UTC and simply show the user UTC dates outside of your Relative Date Threshold.
This can also apply to user input too (but generally in a more limited fashion). Selecting from a drop down that only has { Yesterday, Today, Tomorrow, Next Monday, Next Thursday } is so much simpler and easier for the user than a date picker. Date pickers are some of the most pain inducing components of form filling. Of course this will not work for all cases but you can see that it only takes a little clever design to make it very powerful.
我从中得到的教训:
除非绝对必要,否则不要在 Web 应用程序中使用 JavaScript 日期和时间计算。
I recently had a problem in a web application where on an Ajax post-back the datetime coming back to my server-side code was different from the datetime served out.
It most likely had to do with my JavaScript code on the client that built up the date for posting back to the client as string, because JavaScript was adjusting for time zone and daylight savings, and in some browsers the calculation for when to apply daylight savings seemed to be different than in others.
In the end I opted to remove date and time calculations on the client entirely, and posted back to my server on an integer key which then got translated to date time on the server, to allow for consistent transformations.
My learning from this:
Do not use JavaScript date and time calculations in web applications unless you ABSOLUTELY have to.
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM TZOffsets
WHERE StartDateTime <= @inputdatetime
AND RegionClassId = @RegionClassId;
While I haven't tried it, an approach to time zone adjustments I would find compelling would be as follows:
Store everything in UTC.
Create a table TZOffsets with three columns: RegionClassId, StartDateTime, and OffsetMinutes (int, in minutes).
In the table, store a list of dates and times when the local time changed, and by how much. The number of regions in the table and the number of dates would depend on what range of dates and areas of the world you need to support. Think of this as if it is "historical" date, even though the dates should include the future to some practical limit.
When you need to compute the local time of any UTC time, just do this:
SELECT DATEADD('m', SUM(OffsetMinutes), @inputdatetime) AS LocalDateTime
FROM TZOffsets
WHERE StartDateTime <= @inputdatetime
AND RegionClassId = @RegionClassId;
You might want to cache this table in your app and use LINQ or some similar means to do the queries rather than hitting the database.
No rules are baked into code, you can adjust the offsets for new regions or date ranges readily.
You don't have to support every range of dates or regions, you can add them as needed.
Regions don't have to correspond directly to geopolitical boundaries, and to avoid duplication of rows (for instance, most states in the US handle DST the same way), you can have broad RegionClass entries that link in another table to more traditional lists of states, countries, etc.
For situations like the US where the start and end date of DST has changed over the past few years, this is pretty easy to deal with.
Since the StartDateTime field can store a time as well, the 2:00 AM standard change-over time is handled easily.
Not everywhere in the world uses a 1-hour DST. This handles those cases easily.
The data table is cross-platform and could be a separate open-source project that could be used by developers who use nearly any database platform or programming language.
This can be used for offsets that have nothing to do with time zones. For instance, the 1-second adjustments that happen from time to time to adjust for the Earth's rotation, historical adjustments to and within the Gregorian calendar, etc.
Since this is in a database table, standard report queries, etc. can take advantage of the data without a trip through business logic code.
This handles time zone offsets as well if you want it to, and can even account for special historical cases where a region is assigned to another time zone. All you need is an initial date that assigns a time zone offset to each region with a minimal start date. This would require creating at least one region for each time zone, but would allow you to ask interesting questions like: "What is the difference in local time between Yuma, Arizona and Seattle, Washington on February 2, 1989 at 5:00am?" (Just subtract one SUM() from the other).
Now, the only disadvantage of this approach or any other is that conversions from local time to GMT are not perfect, since any DST change that has a negative offset to the clock repeats a given local time. No easy way to deal with that one, I'm afraid, which is one reason storing local times is bad news in the first place.
我认为在所有情况下,您都应该以 UTC 格式记录时间,并在存储日期/时间之前与本地时间进行转换。然而,由于夏令时和时区,即使知道给定时间是在哪一个时间也可能很困难。
I have hit this on two types of systems, “shift planning systems (e.g. factory workers)” and “gas depend management systems)…
23 and 25 hour long days are a pain to cope with, so are 8hr shifts that take 7hr or 9hr. The problem is you will find that each customers, or even department of the customer have different rules they have created (often without documenting) on what they do in these special cases.
Some questions are best not asked of the customer’s until after they have paid for your “off the shelf” software. It is very rare to find a customer that thinks about this type of issue up front when buying software.
I think in all cases you should record time in UTC and convert to/from local time before storing the date/time. However even know which take a given time is in can be hard with Daylight saving and time zones.
常见的建议是将服务器的时区设置为 UTC。这确实是一个很好的最佳实践,但对于不遵循其他最佳实践的应用程序来说,它只是一个创可贴。例如,服务可能会使用本地时间戳而不是基于 UTC 的时间戳写入日志文件,从而在夏令时回退过渡期间产生歧义。将服务器的时区设置为 UTC 将修复该应用程序。然而,真正的解决办法是应用程序首先使用 UTC 进行记录。
服务器端代码(包括网站)永远不应该期望服务器的本地时区是任何特定的时区。
在某些语言中,本地时区很容易渗透到应用程序代码中。例如,.NET 中的 DateTime.ToUniversalTime 方法会将本地时区转换为 UTC,DateTime.Now 属性返回当地时区的当前时间。此外,JavaScript 中的 Date 构造函数使用计算机的本地时区。类似这样的例子还有很多。练习防御性编程很重要,避免任何使用计算机本地时区设置的代码。
为客户端代码(例如桌面应用程序、移动应用程序和客户端 JavaScript)保留使用本地时区。
When it comes to applications that run on a server, including web sites and other back-end services, the time zone setting of the server should be ignored by the application.
The common advice is to set the server's time zone to UTC. This is indeed a good best practice, but it's there as a band-aid for applications that do not follow other best practices. For example, a service might be writing to log files with local timestamps instead of UTC-based timestamps, thus creating ambiguities during the daylight saving time fall-back transition. Setting the server's time zone to UTC will fix that application. However the real fix would be for the application to log using UTC to begin with.
Server-side code, including web sites, should never expect the local time zone of the server to be anything in particular.
In some languages, the local time zone can easily creep in to application code. For example, the DateTime.ToUniversalTime method in .NET will convert from the local time zone to UTC, and the DateTime.Now property returns the current time in the local time zone. Also, the Date constructor in JavaScript uses the computer's local time zone. There are many other examples like this. It is important to practice defensive programming, avoiding any code that uses the computer's local time zone setting.
Reserve using the local time zone for client-side code, such as desktop applications, mobile applications, and client-side JavaScript.
Be careful when dealing with timestamps stored in the FAT32 filesystem - it is always persisted in local time coordinates (which include DST - see msdn article). Got burned on that one.
One other thing, make sure the servers have the up to date daylight savings patch applied.
We had a situation last year where our times were consistently out by one hour for a three-week period for North American users, even though we were using a UTC based system.
It turns out in the end it was the servers. They just needed an up-to-date patch applied (Windows Server 2003).
This PHP method returns an associative array containing some 'major' timezones (like CEST), which on their own contain more specific 'geographic' timezones (like Europe/Amsterdam).
If you're using these timezones and their offset/DST information, it's extremely important to realize the following:
It seems like all different offset/DST configurations (including historical configurations) of each timezone are included!
For example, Europe/Amsterdam can be found six times in the output of this function. Two occurrences (offset 1172/4772) are for the Amsterdam time used until 1937; two (1200/4800) are for the time that was used between 1937 and 1940; and two (3600/4800) are for the time used since 1940.
Therefore, you cannot rely on the offset/DST information returned by this function as being currently correct/in use!
If you want to know the current offset/DST of a certain timezone, you'll have to do something like this:
<?php
$now = new DateTime(null, new DateTimeZone('Europe/Amsterdam'));
echo $now->getOffset();
?>
If you happen to maintain database systems that are running with DST active, check carefully whether they need to be shut down during the transition in fall. Mandy DBS (or other systems as well) don't like passing the same point in (local) time twice, which is exactly what happens when you turn back the clock in fall. SAP has solved this with a (IMHO really neat) workaround - instead of turning back the clock, they just let the internal clock run at half the usual speed for two hours...
Are you using the .NET framework?
If so, let me introduce you to the DateTimeOffset type, added with .NET 3.5.
This structure holds both a DateTime and an Offset (TimeSpan), which specifies the difference between the DateTimeOffset instance's date and time and Coordinated Universal Time (UTC).
The DateTimeOffset.Now static method will return a DateTimeOffset
instance consisting of the current (local) time, and the local offset
(as defined in the operating system's regional info).
The DateTimeOffset.UtcNow static method will return a DateTimeOffset instance consisting of the current time in UTC (as
if you were in Greenwich).
For those struggling with this on .NET, see if using DateTimeOffset and/or TimeZoneInfo are worth your while.
If you want to use IANA/Olson time zones, or find the built in types are insufficient for your needs, check out Noda Time, which offers a much smarter date and time API for .NET.
Business rules should always work on civil time (unless there's legislation that says otherwise). Be aware that civil time is a mess, but it's what people use so it's what is important.
Internally, keep timestamps in something like civil-time-seconds-from-epoch. The epoch doesn't matter particularly (I favour the Unix epoch) but it does make things easier than the alternative. Pretend that leap-seconds don't exist unless you're doing something that really needs them (e.g., satellite tracking). The mapping between timestamps and displayed time is the only point where DST rules should be applied; the rules change frequently (on a global level, several times a year; blame politicians) so you should make sure that you do not hard-code the mapping. Olson's TZ database is invaluable.
Just one example to prove that handling time is the huge mess described, and that you can never be complacent. In several spots on this page leap-seconds have been ignored.
Several years ago, the Android operating system used GPS satellites to get a UTC time reference, but ignored the fact that GPS satellites do not use leap-seconds. No one noticed until there was confusion on New Year's Eve, when the Apple phone users and Android phone users did their count-downs about 15 seconds apart.
I think it has since been fixed, but you never know when these 'minor details' will come back to haunt you.
Just wanted to point out two things that seem inaccurate or at least confusing:
Always persist time according to a unified standard that is not
affected by daylight savings. GMT and UTC have been mentioned by
different people, though UTC seems to be mentioned most often.
For (almost) all practical computing purposes, UTC is, in fact, GMT. Unless you see a timestamps with a fractional second, you're dealing with GMT which makes this distinction redundant.
Include the local time offset as is (including DST offset) when
storing timestamps.
A timestamp is always represented in GMT and thus has no offset.
On server-side, store times in UTC format so that all date/time values in database are in a single standard regardless of location of users, servers, timezones or DST.
On the UI layer or in emails sent out to user, you need to show times according to user. For that matter, you need to have user's timezone offset so that you can add this offset to your database's UTC value which will result in user's local time. You can either take user's timezone offset when they are signing up or you can auto-detect them in web and mobile platforms. For websites, JavaScript's function getTimezoneOffset() method is a standard since version 1.0 and compatible with all browsers. (Ref: http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp)
Actually, kernel32.dll does not export SystemTimeToTzSpecificLocation. It does however export the following two: SystemTimeToTzSpecificLocalTime and TzSpecificLocalTimeToSystemTime...
new DateTime(int year, int month, int day, int hour, int minute, TimeZone timezone)
They can throw exceptions when a certain date time does not exist due to DST. Instead, build your own methods for creating such dates. In them, catch any exceptions that occur due to DST, and adjust the time is needed with the transition offset. DST may occur on different dates and at different hours (even at midnight for Brazil) according to the timezone.
Never, ever store local time without its UTC offset (or a reference to the time zone)—examples of how not to do it include FAT32 and struct tm in C.¹
Understand that a time zone is a combination of
a set of UTC offsets (e.g. +0100 in winter, +0200 in summer)
rules for when the switchover happens (which may change over time: for example, in the 1990s the EU harmonized the switchover as being on the last Sundays in March and October, at 02:00 standard time/03:00 DST; previously this differed between member states).
¹ Some implementations of struct tm do store the UTC offset, but this has not made it into the standard.
In dealing with databases (in particular MySQL, but this applies to most databases), I found it hard to store UTC.
Databases usually work with server datetime by default (that is, CURRENT_TIMESTAMP).
You may not be able to change the server timezone.
Even if you are able to change the timezone, you may have third-party code that expects server timezone to be local.
I found it easier to just store server datetime in the database, then let the database convert the stored datetime back to UTC (that is, UNIX_TIMESTAMP()) in the SQL statements. After that you can use the datetime as UTC in your code.
If you have 100% control over the server and all code, it's probably better to change server timezone to UTC.
发布评论
评论(30)
答案和其他数据摘要:(请添加您的)
应做:
datetimeoffset
类型。DateTimeOffset
通常是比DateTime
更好的选择。DateTime
和DateTimeZone
类提供的本机时区转换。使用DateTimeZone::listAbbreviations()
时要小心 - 查看答案。为了使 PHP 保持最新的 Olson 数据,请定期安装 timezonedb PECL 软件包; 查看答案。
库中。>=
、<
)。不要:
America/New_York
)与“时区偏移量”(例如-05:00< /代码>。它们是两个不同的东西。请参阅时区标签 wiki。
Date
对象在旧版 Web 浏览器中执行日期和时间计算,因为 ECMAScript 5.1 及更低版本具有 设计缺陷可能会错误地使用夏令时。 (此问题已在 ECMAScript 6 / 2015 中修复)。测试:
参考:
timezone
标签 wiki 页面dst
其他:
Summary of answers and other data: (please add yours)
Do:
datetimeoffset
type that can store both in a single field.1970-01-01T00:00:00Z
(excluding leap seconds). If you require higher precision, use milliseconds instead. This value should always be based on UTC, without any time zone adjustment.DateTimeOffset
is often a better choice thanDateTime
.DateTime
, andDateTimeZone
classes. Be careful when usingDateTimeZone::listAbbreviations()
- see answer. To keep PHP with up to date Olson data, install periodically the timezonedb PECL package; see answer.<chrono>
library.>=
,<
).Don't:
America/New_York
with a "time zone offset", such as-05:00
. They are two different things. See the timezone tag wiki.Date
object to perform date and time calculations in older web browsers, as ECMAScript 5.1 and lower has a design flaw that may use daylight saving time incorrectly. (This was fixed in ECMAScript 6 / 2015).Testing:
Reference:
timezone
tag wiki page on Stack Overflowdst
timezone
Other:
我不确定我可以在上面的答案中添加什么,但以下是我的几点:
时间类型
您应该考虑四种不同的时间:
还有历史/交替时间。这些很烦人,因为它们可能无法映射回标准时间。例如:儒略日期,根据土星农历的日期,克林贡日历。
以 UTC 格式存储开始/结束时间戳效果很好。对于 1,您需要与事件一起存储的事件时区名称 + 偏移量。对于 2,您需要与每个区域一起存储的本地时间标识符以及为每个观看者存储的本地时区名称 + 偏移量(如果您遇到紧急情况,可以从 IP 中获取此信息)。对于 3,以 UTC 秒存储,不需要时区。 4 是 1 或 2 的特殊情况,具体取决于它是全局事件还是本地事件,但您还需要存储创建于时间戳,以便您可以判断时区定义在此之前或之后是否发生更改事件已创建。如果您需要显示历史数据,这是必要的。
存储时间
偏移量和名称
上面的一个例子是:
有了这些信息,我们就可以从历史上确定 2010 年 WCS 决赛举行的确切时间,即使南非时区定义发生变化,并且能够向观看者显示当时当地时区的时间他们查询数据库。
系统时间
您还需要保持操作系统、数据库和应用程序 tzdata 文件彼此之间以及与世界其他地方的同步,并在升级时进行广泛的测试。您所依赖的第三方应用程序无法正确处理 TZ 更改的情况并非闻所未闻。
确保硬件时钟设置为 UTC,如果您在世界各地运行服务器,请确保它们的操作系统也配置为使用 UTC。当您需要从多个时区的服务器复制每小时轮换的 apache 日志文件时,这一点就变得很明显。仅当所有文件都使用相同时区命名时,按文件名对它们进行排序才有效。这也意味着当您从一个盒子 ssh 到另一个盒子并需要比较时间戳时,您不必在头脑中进行日期数学运算。
另外,在所有机器上运行 ntpd。
客户端
永远不要相信从客户端计算机获得的时间戳是有效的。例如,Date: HTTP 标头或 javascript
Date.getTime()
调用。当用作不透明标识符时,或者在同一客户端上的单个会话期间进行日期数学时,这些值很好,但不要尝试将这些值与服务器上的内容交叉引用。您的客户端不运行 NTP,并且其 BIOS 时钟不一定有可用的电池。琐事
最后,政府有时会做一些非常奇怪的事情:
好吧,我想我已经完成了。
I'm not sure what I can add to the answers above, but here are a few points from me:
Types of times
There are four different times you should consider:
There is also Historic/alternate time. These are annoying because they may not map back to standard time. Eg: Julian dates, dates according to a Lunar calendar on Saturn, The Klingon calendar.
Storing start/end timestamps in UTC works well. For 1, you need an event timezone name + offset stored along with the event. For 2, you need a local time identifier stored with each region and a local timezone name + offset stored for every viewer (it's possible to derive this from the IP if you're in a crunch). For 3, store in UTC seconds and no need for timezones. 4 is a special case of 1 or 2 depending on whether it's a global or a local event, but you also need to store a created at timestamp so you can tell if a timezone definition changed before or after this event was created. This is necessary if you need to show historic data.
Storing times
Offsets and names
An example of the above would be:
With this information, we can historically determine the exact time when the 2010 WCS finals took place even if the South African timezone definition changes, and be able to display that to viewers in their local timezone at the time when they query the database.
System Time
You also need to keep your OS, database and application tzdata files in sync, both with each other, and with the rest of the world, and test extensively when you upgrade. It's not unheard of that a third party app that you depend on did not handle a TZ change correctly.
Make sure hardware clocks are set to UTC, and if you're running servers around the world, make sure their OSes are configured to use UTC as well. This becomes apparent when you need to copy hourly rotated apache log files from servers in multiple timezones. Sorting them by filename only works if all files are named with the same timezone. It also means that you don't have to do date math in your head when you ssh from one box to another and need to compare timestamps.
Also, run ntpd on all boxes.
Clients
Never trust the timestamp you get from a client machine as valid. For example, the Date: HTTP headers, or a javascript
Date.getTime()
call. These are fine when used as opaque identifiers, or when doing date math during a single session on the same client, but don't try to cross-reference these values with something you have on the server. Your clients don't run NTP, and may not necessarily have a working battery for their BIOS clock.Trivia
Finally, governments will sometimes do very weird things:
Ok, I think I'm done.
这是一个重要且异常棘手的问题。事实上,持续时间没有完全令人满意的标准。例如,SQL标准和ISO格式(ISO 8601)显然是不够的。
从概念上看,人们通常处理两种类型的时间日期数据,并且很容易区分它们(上述标准没有):“物理时间”和“民用时间”时间”。
“物理”时刻是物理学所处理的连续宇宙时间轴中的一个点(当然,忽略相对论)。例如,这个概念可以在 UTC 中进行充分编码(如果您可以忽略闰秒)。
“民用”时间是遵循民用规范的日期时间规范:此处的时间点由一组日期时间字段(Y、M、D、H、MM、S、FS)加上 TZ(时区规范)完全指定(实际上也是一个“日历”;但假设我们将讨论限制在公历)。时区和日历共同允许(原则上)从一种表示映射到另一种表示。但民用时刻和物理时刻本质上是不同类型的量值,它们应该在概念上保持分离并区别对待(类比:字节数组和字符串)。
这个问题很令人困惑,因为我们可以互换地谈论这些类型的事件,而且因为文明时代会受到政治变化的影响。对于未来的事件,这个问题(以及区分这些概念的需要)变得更加明显。示例(取自我的讨论此处。John
记录在他的日历提醒了某个日期的某个事件
2019 年 7 月 27 日,10:30:00
,TZ=智利/圣地亚哥
(已偏移 GMT-4,因此它对应于 UTC
2019-Jul-27 14:30:00
)。但有一天未来,该国决定将 TZ 偏移更改为 GMT-5。
触发提醒
现在,当这一天到来时...是否应该在A)
2019-Jul-27 10:30:00 Chile/Santiago
=UTC 时间 2019-Jul-27 15:30 :00 ?
或
B)
2019-Jul-27 9:30:00 智利/圣地亚哥
=UTC 时间 2019-Jul-27 14:30:00
?没有正确的答案,除非人们知道约翰的概念是什么意思
当他告诉日历“请在
2019 年 7 月 27 日 10:30:00 给我打电话时
”。TZ=智利/圣地亚哥
他的意思是“民用日期时间”(“当我城市的时钟告诉
10:30”)?在这种情况下,A)是正确答案。
或者他的意思是“时间的物理瞬间”,连续体中的一个点
我们宇宙的时间线,比如说,“当下一次日食发生时
在这种情况下,答案 B) 是正确的。
一些日期/时间 API 正确区分了这一点:其中, Jodatime,它是下一个(第三个!)Java DateTime API (JSR 310) 的基础。
This is an important and surprisingly tough issue. The truth is that there is no completely satisfying standard for persisting time. For example, the SQL standard and the ISO format (ISO 8601) are clearly not enough.
From the conceptual point of view, one usually deals with two types of time-date data, and it's convenient to distinguish them (the above standards do not) : "physical time" and "civil time".
A "physical" instant of time is a point in the continuous universal timeline that physics deal with (ignoring relativity, of course). This concept can be adequately coded-persisted in UTC, for example (if you can ignore leap seconds).
A "civil" time is a datetime specification that follows civil norms: a point of time here is fully specified by a set of datetime fields (Y,M,D,H,MM,S,FS) plus a TZ (timezone specification) (also a "calendar", actually; but lets assume we restrict the discussion to Gregorian calendar). A timezone and a calendar jointly allow (in principle) to map from one representation to another. But civil and physical time instants are fundamentally different types of magnitudes, and they should be kept conceptually separated and treated differently (an analogy: arrays of bytes and character strings).
The issue is confusing because we speak of these types events interchangeably, and because the civil times are subject to political changes. The problem (and the need to distinguish these concepts) becomes more evident for events in the future. Example (taken from my discussion here.
John records in his calendar a reminder for some event at datetime
2019-Jul-27, 10:30:00
, TZ=Chile/Santiago
, (which has offset GMT-4,hence it corresponds to UTC
2019-Jul-27 14:30:00
). But some dayin the future, the country decides to change the TZ offset to GMT-5.
Now, when the day comes... should that reminder trigger at
A)
2019-Jul-27 10:30:00 Chile/Santiago
=UTC time 2019-Jul-27 15:30:00
?or
B)
2019-Jul-27 9:30:00 Chile/Santiago
=UTC time 2019-Jul-27 14:30:00
?There is no correct answer, unless one knows what John conceptually meant
when he told the calendar "Please ring me at
2019-Jul-27, 10:30:00
".TZ=Chile/Santiago
Did he mean a "civil date-time" ("when the clocks in my city tell
10:30")? In that case, A) is the correct answer.
Or did he mean a "physical instant of time", a point in the continuus
line of time of our universe, say, "when the next solar eclipse
happens". In that case, answer B) is the correct one.
A few Date/Time APIs get this distinction right: among them, Jodatime, which is the foundation of the next (third!) Java DateTime API (JSR 310).
明确架构上的关注点分离 - 准确了解哪一层与用户交互,并且必须更改规范表示 (UTC) 的日期时间或从规范表示 (UTC) 更改日期时间。 非 UTC 日期时间是表示(遵循用户本地时区),UTC 时间是模型(对于后端和中间层仍然是唯一的)。
另外,确定您的实际受众是什么、您不需要服务什么以及您的界限在哪里。不要接触异国情调的日历,除非您在那里确实有重要的客户,然后考虑为该地区单独的面向用户的服务器。
如果您可以获取并维护用户的位置,请使用位置进行系统的日期时间转换(例如 .NET 区域性或 SQL 表),但如果日期时间对您的用户至关重要,则为最终用户提供一种选择覆盖的方法。
如果涉及历史审计义务(例如准确告知亚利桑那州的 Jo 于 2 年前 9 月支付账单的时间)则保留 UTC 和当地时间作为记录(您的转换表会随着时间的推移而改变)。
为批量数据定义时间参考时区 - 例如文件、网络服务等。假设东海岸公司在加利福尼亚州设有数据中心 - 您需要询问并了解他们使用什么作为标准假设其中之一。
不要信任日期时间文本表示中嵌入的时区偏移,并且不接受解析和遵循它们。 相反,始终要求必须明确定义时区和/或参考区。您可以轻松接收带有 PST 偏移量的时间,但该时间实际上是 EST,因为这是客户端的参考时间,并且记录刚刚在 PST 格式的服务器上导出。
Make clear architectural separation of concerns - to know exactly which tier interacts with users, and has to change date-time for/from canonical representation (UTC). Non-UTC date-time is presentation (follows users local timezone), UTC time is model (remains unique for back-end and mid tiers).
Also, decide what's your actual audience, what you don't have to serve and where do you draw the line. Don't touch exotic calendars unless you actually have important customers there and then consider separate user-facing server(s) just for that region.
If you can acquire and maintain user's location, use location for systematic date-time conversion (say .NET culture or a SQL table) but provide a way for end-user to choose overrides if date-time is critical for your users.
If there are historical audit obligations involved (like telling exactly when Jo in AZ paid a bill 2 yrs ago in September) then keep both UTC and local time for the record (your conversion tables will change in a course of time).
Define the time referential time zone for data that comes in bulk - like files, web services etc. Say East Coast company has data center in CA - you need to ask and know what they use as a standard instead of assuming one or the other.
Don't trust time-zone offsets embedded in textual representation of the date-time and don't accept to parse and follow them. Instead always request that time zone and/or reference zone have to be explicitly defined. You can easily receive time with PST offset but the time is actually EST since that's the client's reference time and records were just exported at a server which is in PST.
您需要了解 Olson tz 数据库,该数据库可从
ftp://elsie.nci.nih.gov/pubhttp://iana.org/time-zones/。它每年更新多次,以应对世界各地不同国家何时(以及是否)切换冬季和夏季(标准时间和夏令时)时间的最后一刻的变化。 2009年,最后一个版本是2009s; 2010年,是2010n; 2011年,是2011n; 2012年5月底,发布版本为2012c。请注意,有一组代码用于管理数据和实际时区数据本身,位于两个单独的存档(tzcode20xxy.tar.gz 和 tzdata20xxy.tar.gz)中。代码和数据都属于公共领域。这是时区名称(例如 America/Los_Angeles)(以及同义词(例如 US/Pacific))的来源。
如果您需要跟踪不同的区域,那么您需要 Olson 数据库。正如其他人所建议的,您还希望以固定格式存储数据(通常选择 UTC)以及生成数据的时区记录。您可能想要区分当时与 UTC 的偏移量和时区名称;这可以在以后有所作为。另外,知道当前是 2010-03-28T23:47:00-07:00(美国/太平洋)可能会也可能不会帮助您解释值 2010-11-15T12:30 — 该值可能是在 PST 中指定的(太平洋标准时间)而不是 PDT(太平洋夏令时)。
标准 C 库接口对于这类东西并没有多大帮助。
奥尔森数据发生了变化,部分原因是 AD Olson 即将退休,部分原因是维护者因侵犯版权而提起诉讼(现已驳回)。时区数据库现在由互联网号码分配机构 IANA 管理,前面有一个链接页面到“时区数据库”。讨论邮件列表现已
[电子邮件受保护]
;公告列表为[电子邮件受保护]
。You need to know about the Olson tz database, which is available from
ftp://elsie.nci.nih.gov/pubhttp://iana.org/time-zones/. It is updated multiple times per year to deal with the often last-minute changes in when (and whether) to switch between winter and summer (standard and daylight saving) time in different countries around the world. In 2009, the last release was 2009s; in 2010, it was 2010n; in 2011, it was 2011n; at the end of May 2012, the release was 2012c. Note that there is a set of code to manage the data and the actual time zone data itself, in two separate archives (tzcode20xxy.tar.gz and tzdata20xxy.tar.gz). Both code and data are in the public domain.This is the source of time zone names such as America/Los_Angeles (and synonyms such as US/Pacific).
If you need to keep track of different zones, then you need the Olson database. As others have advised, you also want to store the data in a fixed format — UTC is normally the one chosen — along with a record of the time zone in which the data was generated. You may want to distinguish between the offset from UTC at the time and the time zone name; that can make a difference later. Also, knowing that it is currently 2010-03-28T23:47:00-07:00 (US/Pacific) may or may not help you with interpreting the value 2010-11-15T12:30 — which is presumably specified in PST (Pacific Standard Time) rather than PDT (Pacific Daylight Saving Time).
The standard C library interfaces are not dreadfully helpful with this sort of stuff.
The Olson data has moved, in part because A D Olson will be retiring soon, and in part because there was a (now dismissed) law suit against the maintainers for copyright infringement. The time zone database is now managed under the auspices of IANA, the Internet Assigned Numbers Authority, and there's a link on the front page to 'Time Zone Database'. The discussion mailing list is now
[email protected]
; the announcement list is[email protected]
.一般来说,在存储的时间戳中包含本地时间偏移(包括 DST 偏移):如果您稍后想要在其原始时区(和 DST 设置)中显示时间戳,则单独使用 UTC 是不够的。
请记住,偏移量并不总是整数小时(例如,印度标准时间是 UTC+05:30)。
例如,合适的格式是元组(unix 时间,以分钟为单位的偏移量)或 ISO 8601。
In general, include the local time offset (including DST offset) in stored timestamps: UTC alone is not enough if you later want to display the timestamp in its original timezone (and DST setting).
Keep in mind that the offset is not always an integer number of hours (e.g. Indian Standard Time is UTC+05:30).
For example, suitable formats are a tuple (unix time, offset in minutes) or ISO 8601.
跨越“计算机时间”和“人类时间”的界限是一场噩梦。主要的一个问题是,管理时区和夏令时的规则没有任何标准。各国可以随时更改其时区和夏令时规则,而且他们确实这样做了。
一些国家,例如以色列、巴西,每年决定何时实行夏令时,因此无法提前知道夏令时何时(是否)生效。其他国家对于 DST 何时生效有固定的规则。其他国家并不全部采用夏令时。
时区不必与 GMT 相差整小时。尼泊尔是+5.45。甚至有些时区是+13。这意味着:
都是同一时间,但却是不同的三天!
对于时区的缩写以及它们在夏令时如何变化也没有明确的标准,因此您最终会得到这样的结果:
最好的建议是尽可能远离当地时间并尽可能坚持使用 UTC。仅在最后一刻才转换为当地时间。
测试时,请确保测试西半球和东半球的国家/地区,其中既有正在进行夏令时的国家,也有未实行夏令时的国家,以及未使用夏令时的国家(总共 6 个国家)。
Crossing the boundary of "computer time" and "people time" is a nightmare. The main one being that there is no sort of standard for the rules governing timezones and daylight saving times. Countries are free to change their timezone and DST rules at any time, and they do.
Some countries e.g. Israel, Brazil, decide each year when to have their daylight saving times, so it is impossible to know in advance when (if) DST will be in effect. Others have fixed(ish) rules as to when DST is in effect. Other countries do not use DST as all.
Timezones do not have to be full hour differences from GMT. Nepal is +5.45. There are even timezones that are +13. That means that:
are all the same time, yet 3 different days!
There is also no clear standard on the abbreviations for timezones, and how they change when in DST so you end up with things like this:
The best advice is to stay away from local times as much as possible and stick to UTC where you can. Only convert to local times at the last possible moment.
When testing make sure you test countries in the Western and Eastern hemispheres, with both DST in progress and not and a country that does not use DST (6 in total).
对于 PHP:
PHP 中的 DateTimeZone 类 > 5.2 已经基于其他人提到的 Olson DB,因此如果您在 PHP 中而不是在 DB 中进行时区转换,则可以免除使用(难以理解的)Olson 文件。
然而,PHP 的更新不如 Olson DB 频繁,因此仅使用 PHP 时区转换可能会给您留下过时的 DST 信息并影响数据的正确性。虽然这种情况预计不会经常发生,但它可能会发生,并且如果您在全球拥有大量用户的话,将会发生。
要解决上述问题,请使用 timezonedb pecl 包。它的功能是更新PHP的时区数据。尽可能频繁地更新此软件包。 (我不确定这个包的更新是否完全遵循 Olson 的更新,但它的更新频率似乎至少非常接近 Olson 的更新频率。)
For PHP:
The DateTimeZone class in PHP > 5.2 is already based on the Olson DB which others mention, so if you are doing timezone conversions in PHP and not in the DB, you are exempt of working with (the hard-to-understand) Olson files.
However, PHP is not updated as frequently as the Olson DB, so just using PHPs time zone conversions may leave you with outdated DST information and influence the correctness of your data. While this is not expected to happen frequently, it may happen, and will happen if you have a large base of users worldwide.
To cope with the above issue, use the timezonedb pecl package. Its function is to update PHP's timezone data. Install this package as frequently as it is updated. (I'm not sure if the updates to this package follow Olson updates exactly, but it seems to be updated at a frequency which is at least very close to the frequency of Olson updates.)
如果您的设计可以容纳它,完全避免本地时间转换!
我知道对某些人来说这可能听起来很疯狂,但考虑一下用户体验:用户处理临近的相对日期(今天、昨天、下周一)的速度比绝对日期(2010.09.17,9 月 17 日星期五)一目了然。当您进一步考虑时,日期越接近
now()
,时区(和 DST)的准确性就越重要,因此如果您可以以相对格式表达日期/日期时间 + /- 1 或 2 周,其余日期可以是 UTC,这对 95% 的用户来说不会太重要。通过这种方式,您可以以 UTC 格式存储所有日期,并以 UTC 格式进行相对比较,并简单地向用户显示相对日期阈值之外的 UTC 日期。
这也适用于用户输入(但通常以更有限的方式)。对于用户来说,从只有 { 昨天、今天、明天、下周一、下周四 } 的下拉列表中进行选择比日期选择器简单得多。日期选择器是表单填写过程中最令人痛苦的组件之一。当然,这并不适用于所有情况,但您可以看到,只需要一点巧妙的设计就可以使其非常强大。
If your design can accommodate it, avoid local time conversion all together!
I know to some this might sound insane but think about UX: users process near, relative dates (today, yesterday, next Monday) faster than absolute dates (2010.09.17, Friday Sept 17) on glance. And when you think about it more, the accuracy of timezones (and DST) is more important the closer the date is to
now()
, so if you can express dates/datetimes in a relative format for +/- 1 or 2 weeks, the rest of the dates can be UTC and it wont matter too much to 95% of users.This way you can store all dates in UTC and do the relative comparisons in UTC and simply show the user UTC dates outside of your Relative Date Threshold.
This can also apply to user input too (but generally in a more limited fashion). Selecting from a drop down that only has { Yesterday, Today, Tomorrow, Next Monday, Next Thursday } is so much simpler and easier for the user than a date picker. Date pickers are some of the most pain inducing components of form filling. Of course this will not work for all cases but you can see that it only takes a little clever design to make it very powerful.
我最近在一个 Web 应用程序中遇到了一个问题,在 Ajax 回发中,返回到我的服务器端代码的日期时间与提供的日期时间不同。
这很可能与客户端上的 JavaScript 代码有关,该代码构建了以字符串形式发回客户端的日期,因为 JavaScript 正在调整时区和夏令时,并且在某些浏览器中计算何时应用夏令时似乎与其他人有所不同。
最后,我选择完全删除客户端上的日期和时间计算,并通过整数键发回我的服务器,然后将其转换为服务器上的日期时间,以实现一致的转换。
我从中得到的教训:
除非绝对必要,否则不要在 Web 应用程序中使用 JavaScript 日期和时间计算。
I recently had a problem in a web application where on an Ajax post-back the datetime coming back to my server-side code was different from the datetime served out.
It most likely had to do with my JavaScript code on the client that built up the date for posting back to the client as string, because JavaScript was adjusting for time zone and daylight savings, and in some browsers the calculation for when to apply daylight savings seemed to be different than in others.
In the end I opted to remove date and time calculations on the client entirely, and posted back to my server on an integer key which then got translated to date time on the server, to allow for consistent transformations.
My learning from this:
Do not use JavaScript date and time calculations in web applications unless you ABSOLUTELY have to.
虽然我还没有尝试过,但我认为引人注目的时区调整方法如下:
将所有内容存储在 UTC 中。
创建一个包含三列的表
TZOffsets
:RegionClassId、StartDateTime 和 OffsetMinutes(整数,以分钟为单位)。在表中,存储本地时间更改的日期和时间列表以及更改的幅度。表中的区域数量和日期数量取决于您需要支持的日期范围和世界地区。将此视为“历史”日期,即使这些日期应包括未来的某些实际限制。
当您需要计算任何 UTC 时间的本地时间时,只需执行以下操作:
您可能希望在应用程序中缓存此表,并使用 LINQ 或一些类似的方法来执行查询,而不是访问数据库。
该数据可以从公共领域 tz 数据库中提取。
这种方法的优点和脚注:
现在,此方法或任何其他方法的唯一缺点是从本地时间到 GMT 的转换并不完美,因为任何 DST 更改都会对时钟产生负偏移 em>重复给定的当地时间。恐怕没有简单的方法来处理这个问题,这就是存储当地时间首先是坏消息的原因之一。
While I haven't tried it, an approach to time zone adjustments I would find compelling would be as follows:
Store everything in UTC.
Create a table
TZOffsets
with three columns: RegionClassId, StartDateTime, and OffsetMinutes (int, in minutes).In the table, store a list of dates and times when the local time changed, and by how much. The number of regions in the table and the number of dates would depend on what range of dates and areas of the world you need to support. Think of this as if it is "historical" date, even though the dates should include the future to some practical limit.
When you need to compute the local time of any UTC time, just do this:
You might want to cache this table in your app and use LINQ or some similar means to do the queries rather than hitting the database.
This data can be distilled from the public domain tz database.
Advantages and footnotes of this approach:
Now, the only disadvantage of this approach or any other is that conversions from local time to GMT are not perfect, since any DST change that has a negative offset to the clock repeats a given local time. No easy way to deal with that one, I'm afraid, which is one reason storing local times is bad news in the first place.
我在两种类型的系统上遇到了这个问题,“轮班计划系统(例如工厂工人)”和“天然气依赖管理系统”……
23 和 25 小时的长日处理起来很痛苦,需要 7 小时或 9 小时的 8 小时轮班也是如此。 。问题是您会发现每个客户,甚至客户的部门对于他们在这些特殊情况下的操作都有不同的规则(通常没有记录)。
在客户支付“现成”软件的费用之前,最好不要向客户询问某些问题。很少有客户在购买软件时预先考虑此类问题。
我认为在所有情况下,您都应该以 UTC 格式记录时间,并在存储日期/时间之前与本地时间进行转换。然而,由于夏令时和时区,即使知道给定时间是在哪一个时间也可能很困难。
I have hit this on two types of systems, “shift planning systems (e.g. factory workers)” and “gas depend management systems)…
23 and 25 hour long days are a pain to cope with, so are 8hr shifts that take 7hr or 9hr. The problem is you will find that each customers, or even department of the customer have different rules they have created (often without documenting) on what they do in these special cases.
Some questions are best not asked of the customer’s until after they have paid for your “off the shelf” software. It is very rare to find a customer that thinks about this type of issue up front when buying software.
I think in all cases you should record time in UTC and convert to/from local time before storing the date/time. However even know which take a given time is in can be hard with Daylight saving and time zones.
当涉及到在服务器上运行的应用程序(包括网站和其他后端服务)时,应用程序应忽略服务器的时区设置。
常见的建议是将服务器的时区设置为 UTC。这确实是一个很好的最佳实践,但对于不遵循其他最佳实践的应用程序来说,它只是一个创可贴。例如,服务可能会使用本地时间戳而不是基于 UTC 的时间戳写入日志文件,从而在夏令时回退过渡期间产生歧义。将服务器的时区设置为 UTC 将修复该应用程序。然而,真正的解决办法是应用程序首先使用 UTC 进行记录。
服务器端代码(包括网站)永远不应该期望服务器的本地时区是任何特定的时区。
在某些语言中,本地时区很容易渗透到应用程序代码中。例如,.NET 中的
DateTime.ToUniversalTime
方法会将本地时区转换为 UTC,DateTime.Now
属性返回当地时区的当前时间。此外,JavaScript 中的Date
构造函数使用计算机的本地时区。类似这样的例子还有很多。练习防御性编程很重要,避免任何使用计算机本地时区设置的代码。为客户端代码(例如桌面应用程序、移动应用程序和客户端 JavaScript)保留使用本地时区。
When it comes to applications that run on a server, including web sites and other back-end services, the time zone setting of the server should be ignored by the application.
The common advice is to set the server's time zone to UTC. This is indeed a good best practice, but it's there as a band-aid for applications that do not follow other best practices. For example, a service might be writing to log files with local timestamps instead of UTC-based timestamps, thus creating ambiguities during the daylight saving time fall-back transition. Setting the server's time zone to UTC will fix that application. However the real fix would be for the application to log using UTC to begin with.
Server-side code, including web sites, should never expect the local time zone of the server to be anything in particular.
In some languages, the local time zone can easily creep in to application code. For example, the
DateTime.ToUniversalTime
method in .NET will convert from the local time zone to UTC, and theDateTime.Now
property returns the current time in the local time zone. Also, theDate
constructor in JavaScript uses the computer's local time zone. There are many other examples like this. It is important to practice defensive programming, avoiding any code that uses the computer's local time zone setting.Reserve using the local time zone for client-side code, such as desktop applications, mobile applications, and client-side JavaScript.
对于网络来说,规则并不复杂...
其余的只是使用服务器端日期时间库进行 UTC/本地转换。很好,可以出发了...
For the web, the rules aren't that complicated...
The rest is just UTC/local conversion using your server-side datetime libraries. Good to go...
将您的服务器设置为 UTC,并确保它们全部配置为 ntp 或同等设置。
UTC 避免了夏令时问题,并且不同步的服务器可能会导致不可预测的结果,需要一段时间才能诊断。
Keep your servers set to UTC, and make sure they all are configured for ntp or the equivalent.
UTC avoids daylight savings time issues, and out-of-sync servers can cause unpredictable results that take a while to diagnose.
处理存储在 FAT32 文件系统中的时间戳时要小心 - 它始终保留在本地时间坐标中(包括 DST - 请参阅 msdn 文章)。被那个烧伤了
Be careful when dealing with timestamps stored in the FAT32 filesystem - it is always persisted in local time coordinates (which include DST - see msdn article). Got burned on that one.
另一件事是,确保服务器应用了最新的夏令时补丁。
去年,我们遇到了这样的情况:尽管我们使用的是基于 UTC 的系统,但北美用户的时间在三周内持续落后一小时。
最后发现是服务器的问题。他们只需要应用最新的补丁(Windows Server 2003)。
One other thing, make sure the servers have the up to date daylight savings patch applied.
We had a situation last year where our times were consistently out by one hour for a three-week period for North American users, even though we were using a UTC based system.
It turns out in the end it was the servers. They just needed an up-to-date patch applied (Windows Server 2003).
PHP 的
DateTimeZone::listAbbreviations()
输出此 PHP 方法返回一个包含一些“主要”时区(如 CEST)的关联数组,这些时区本身包含更具体的“地理”时区(如欧洲/阿姆斯特丹)。
如果您正在使用这些时区及其偏移量/DST 信息,那么认识到以下几点非常重要:
似乎每个时区的所有不同偏移量/DST 配置(包括历史配置)都是
例如,Europe/Amsterdam 在此函数的输出中出现了六次。两次出现(偏移量 1172/4772)是 1937 年之前使用的阿姆斯特丹时间;两个 (1200/4800) 是 1937 年至 1940 年间使用的时间;两个 (3600/4800) 表示自 1940 年以来使用的时间。
因此,您不能依赖此函数返回的偏移/DST 信息当前正确/正在使用!如果
你想知道某个时区的当前偏移量/夏令时,你必须这样做:
PHP's
DateTimeZone::listAbbreviations()
outputThis PHP method returns an associative array containing some 'major' timezones (like CEST), which on their own contain more specific 'geographic' timezones (like Europe/Amsterdam).
If you're using these timezones and their offset/DST information, it's extremely important to realize the following:
It seems like all different offset/DST configurations (including historical configurations) of each timezone are included!
For example, Europe/Amsterdam can be found six times in the output of this function. Two occurrences (offset 1172/4772) are for the Amsterdam time used until 1937; two (1200/4800) are for the time that was used between 1937 and 1940; and two (3600/4800) are for the time used since 1940.
Therefore, you cannot rely on the offset/DST information returned by this function as being currently correct/in use!
If you want to know the current offset/DST of a certain timezone, you'll have to do something like this:
如果您碰巧维护在 DST 处于活动状态下运行的数据库系统,请仔细检查它们是否需要在秋季过渡期间关闭。 Mandy DBS(或其他系统)不喜欢在(本地)时间中两次经过同一点,这正是当你在秋天倒转时钟时所发生的情况。 SAP已经用一个(恕我直言,非常巧妙的)解决方法解决了这个问题——他们没有让时钟倒转,而是让内部时钟以平常速度的一半运行两个小时......
If you happen to maintain database systems that are running with DST active, check carefully whether they need to be shut down during the transition in fall. Mandy DBS (or other systems as well) don't like passing the same point in (local) time twice, which is exactly what happens when you turn back the clock in fall. SAP has solved this with a (IMHO really neat) workaround - instead of turning back the clock, they just let the internal clock run at half the usual speed for two hours...
您是否使用.NET 框架?
如果是这样,让我向您介绍 DateTimeOffset 类型,添加与.NET 3.5。
此结构同时包含
DateTime
和 Offset (TimeSpan
),它指定DateTimeOffset
实例的日期和时间与协调世界时 (TimeSpan
) 之间的差异 (世界标准时间)。DateTimeOffset.Now
静态方法将返回DateTimeOffset
由当前(本地)时间和本地偏移量组成的实例
(如操作系统的区域信息中所定义)。
DateTimeOffset.UtcNow
静态方法将返回DateTimeOffset
实例由 UTC 中的当前时间组成(如如果您在格林威治)。
其他有用的类型是 TimeZone 和 TimeZoneInfo 类。
Are you using the .NET framework?
If so, let me introduce you to the DateTimeOffset type, added with .NET 3.5.
This structure holds both a
DateTime
and an Offset (TimeSpan
), which specifies the difference between theDateTimeOffset
instance's date and time and Coordinated Universal Time (UTC).The
DateTimeOffset.Now
static method will return aDateTimeOffset
instance consisting of the current (local) time, and the local offset
(as defined in the operating system's regional info).
The
DateTimeOffset.UtcNow
static method will return aDateTimeOffset
instance consisting of the current time in UTC (asif you were in Greenwich).
Other helpful types are the TimeZone and TimeZoneInfo classes.
对于那些在 .NET 上遇到此问题的人,请查看是否使用
DateTimeOffset
和/或TimeZoneInfo
值得您花时间。如果您想使用 IANA/Olson 时区,或者发现内置类型不足以满足您的需要如果需要,请查看 Noda Time,它为 .NET 提供了更智能的日期和时间 API。
For those struggling with this on .NET, see if using
DateTimeOffset
and/orTimeZoneInfo
are worth your while.If you want to use IANA/Olson time zones, or find the built in types are insufficient for your needs, check out Noda Time, which offers a much smarter date and time API for .NET.
业务规则应始终适用于民事时间(除非有立法另有规定)。请注意,民用时间很混乱,但这是人们使用的时间,所以这才是重要的。
在内部,将时间戳保留为“civil-time-seconds-from-epoch”之类的格式。 纪元并不特别重要(我更喜欢Unix 纪元),但它确实比其他选择更容易。假装闰秒不存在,除非你正在做一些真正的事情 需要它们(例如,卫星跟踪)。时间戳和显示时间之间的映射是唯一应应用 DST 规则的点;规则经常变化(在全球范围内,一年几次;归咎于政客),因此您应该确保不对映射进行硬编码。 Olson 的 TZ 数据库 非常宝贵。
Business rules should always work on civil time (unless there's legislation that says otherwise). Be aware that civil time is a mess, but it's what people use so it's what is important.
Internally, keep timestamps in something like civil-time-seconds-from-epoch. The epoch doesn't matter particularly (I favour the Unix epoch) but it does make things easier than the alternative. Pretend that leap-seconds don't exist unless you're doing something that really needs them (e.g., satellite tracking). The mapping between timestamps and displayed time is the only point where DST rules should be applied; the rules change frequently (on a global level, several times a year; blame politicians) so you should make sure that you do not hard-code the mapping. Olson's TZ database is invaluable.
仅用一个例子来证明处理时间是所描述的巨大混乱,并且您永远不能自满。在本页的几个地方,闰秒被忽略了。
几年前,Android操作系统使用GPS卫星来获取UTC时间参考,但忽略了GPS卫星不使用闰秒的事实。直到除夕夜,苹果手机用户和安卓手机用户倒计时大约相隔 15 秒,才引起了混乱。
我认为它已经被修复了,但你永远不知道这些“小细节”什么时候会回来困扰你。
Just one example to prove that handling time is the huge mess described, and that you can never be complacent. In several spots on this page leap-seconds have been ignored.
Several years ago, the Android operating system used GPS satellites to get a UTC time reference, but ignored the fact that GPS satellites do not use leap-seconds. No one noticed until there was confusion on New Year's Eve, when the Apple phone users and Android phone users did their count-downs about 15 seconds apart.
I think it has since been fixed, but you never know when these 'minor details' will come back to haunt you.
只是想指出两件事似乎不准确或至少令人困惑:
对于(几乎)所有实际计算目的,UTC 实际上就是 GMT。除非您看到带有小数秒的时间戳,否则您正在处理 GMT,这使得这种区别变得多余。
时间戳始终以 GMT 表示,因此没有偏移量。
Just wanted to point out two things that seem inaccurate or at least confusing:
For (almost) all practical computing purposes, UTC is, in fact, GMT. Unless you see a timestamps with a fractional second, you're dealing with GMT which makes this distinction redundant.
A timestamp is always represented in GMT and thus has no offset.
这是我的经验:-
(不需要任何第三方库)
Here is my experience:-
(Does not require any third-party library)
Tom Scott YouTube 上有关时区的视频 Computerphile 频道也对该主题进行了精彩有趣的描述。
示例包括:
日历(发生在20世纪的俄罗斯)。
Tom Scott's video about timezones on YouTube on the Computerphile channel has also a nice and entertaining description of the topic.
Examples include:
calendar (which happened in the 20th century in Russia).
实际上,kernel32.dll 并不导出 SystemTimeToTzSpecificLocation。但是,它确实导出以下两个:SystemTimeToTzSpecificLocalTime 和 TzSpecificLocalTimeToSystemTime...
Actually, kernel32.dll does not export SystemTimeToTzSpecificLocation. It does however export the following two: SystemTimeToTzSpecificLocalTime and TzSpecificLocalTimeToSystemTime...
切勿仅依赖于构造函数,例如,
当由于 DST 导致某个日期时间不存在时,它们可能会抛出异常。相反,构建您自己的方法来创建此类日期。在其中,捕获由于 DST 而发生的任何异常,并通过转换偏移量调整所需的时间。根据时区,夏令时可能会在不同的日期和不同的时间发生(甚至巴西的午夜)。
Never rely only on constructors like
They can throw exceptions when a certain date time does not exist due to DST. Instead, build your own methods for creating such dates. In them, catch any exceptions that occur due to DST, and adjust the time is needed with the transition offset. DST may occur on different dates and at different hours (even at midnight for Brazil) according to the timezone.
¹ struct tm 的某些实现确实存储了 UTC 偏移量,但这尚未纳入标准。
struct tm
in C.¹¹ Some implementations of
struct tm
do store the UTC offset, but this has not made it into the standard.在处理数据库时(特别是 MySQL,但这适用于大多数数据库),我发现很难存储 UTC。
我发现将服务器日期时间存储在数据库中,然后让数据库在 SQL 语句中将存储的日期时间转换回 UTC(即 UNIX_TIMESTAMP())会更容易。之后,您可以在代码中使用日期时间作为 UTC。
如果您对服务器和所有代码有 100% 的控制,最好将服务器时区更改为 UTC。
In dealing with databases (in particular MySQL, but this applies to most databases), I found it hard to store UTC.
I found it easier to just store server datetime in the database, then let the database convert the stored datetime back to UTC (that is, UNIX_TIMESTAMP()) in the SQL statements. After that you can use the datetime as UTC in your code.
If you have 100% control over the server and all code, it's probably better to change server timezone to UTC.