Sorry if this question has been asked before. I can't seem to find an answer.
MySQL has these types of date/time types:
DATE: 0000-00-00
TIME: 00:00:00
DATETIME: 0000-00-00 00:00:00
TIMESTAMP: 0000-00-00 00:00:00
YEAR: 0000
I know one can use each of these datatypes to store each of them.
How can I store TIME only such as business opening/closing hours,YEAR only such as company year founded and DATE only such as date of birth within a DATETIME field? Thanks.
PS: Am using PHP, if that helps, though am looking for a pure MySQL solution.
I would argue that you should store all of these as datetime. Hours of business can vary based on the day, and you may also need the day or month when a company was founded.
By storing everything as datetime it is possible that you would be persisting extra information. But it would always be possible to easily retrieve the information of interest. For example, to get the year a company was founded you could use either YEAR(date_founded), or if you are feeling more sophisticated you could use DATE_FORMAT('date_founded', '%Y').
What you can do is store timestamp and then use timestamp to get whatever information you want to retrieve from timestamp
Based on language you are using there are available functions to help you with your implementation.
Share the language you are using so we can help you better
Related
For instance, I have a blog where users can comment and I want everyone can see how long ago the comment was posted, for example: 5 minutes ago OR 3 hours ago.
So if a guy in London posts a comment and a guy in India visits the page, they both should see "1 minute ago" and on hover should see the time relative to their timezone. (10pm in London, 3.30am in India).
My current solution in mind is to use varchar(25) data type and store the time as ISO-8601 (e.g. 2019-12-12T21:46:42+00:00)
Using this I can get the timezone of the commenter and convert the time to the current user's timezone. It works perfectly.
But I wonder if there is a better / more elegant way to do it?
So far I tried using DATETIME and TIMESTAMP data types but they do not seem to be useful in this scenario. I read online that TIMESTAMP is supposed to store time in UTC timezone and send it back in user's timezone but that did not happen for me, it got saved in my local time instead. And yes, I did not specify any time while saving data, MySQL used the CURRENT_TIMESTAMP.
Any thoughts or ideas?
I'd recommend storing all your dates/times in one universal format in your database and UTC would be the best candidate for this.
That way, regardless of their location, it's easy for you to say 1 minute ago...
If you need to display the full date/time on the front-end, you'd need to convert the time from UTC to that user's location, which you can do via PHP's handy DateTime functions:
https://www.php.net/manual/en/datetime.settimezone.php
Which type I should use to store current date + time in UTC?
Then to be able to convert UTC date to specific timezone?
Now I use TIMESTAMP type and CURRENT_TIMESTAMP.
It stores data like: 2019-08-19 20:44:11
But minutes are different that real UTC time, I dont know why.
My server time is local. It is correct under Windows Server
It is up to you to decide the best way to solve timezone problem when users and server has different locale.
No matter the case and the app (mobile, web, etc.) the problem is the same. You should find the best and easiest in your case way to handle time zones.
Here are few options that you can use:
MySQL
From MySQL Date and Time Types - you can create table fields that will hold your date and time values.
"The date and time types for representing temporal values are DATE, TIME, DATETIME, TIMESTAMP, and YEAR. Each temporal type has a range of valid values, as well as a “zero” value that may be used when you specify an invalid value that MySQL cannot represent. The TIMESTAMP type has special automatic updating behavior, described later."
In respect to MySQL Data Type Storage Requirements read the link and make sure you satisfy the table storage engine and type requirements in your project.
Setting the timezone in MySQL by:
SET time_zone = '+8:00'
To me this is a bit more work to handle, but the data is fully loaded, managed and updated by MySQL. No PHP here!
Using MySQL might seem like a better idea (that's what I'd like to think), but there's a lot more to it.
To be able to choose, you will have to make an educated decision. There's a lot to cover in regards to using MySQL. Here's a practical article that goes into the rabbit hole of using MySQL to manage date, time and timezone.
Since you didn't specify how you interface the database, here's a PHP example and functions to handle the date, time and time zones.
PHP
1. Save date, time and time zone
E.g. Chicago (USA - Illinois) - UTC Offset UTC -5 hours
You can save the date time
2015-11-01 00:00:00
and the time zone
America/Chicago
You will have to work out DST transitions and months having different numbers of days.
Here's a reference to the DateTime to work out any timezone and DST differences:
DateTime Aritmetic
2. Unix Timestamp and Time Zone
Before we go into the details of this option we should be aware of the following:
The unix time stamp is a way to track time as a running total of seconds. This count starts at the Unix Epoch on January 1st, 1970 at UTC. Therefore, the unix time stamp is merely the number of seconds between a particular date and the Unix Epoch. It should also be pointed out (thanks to the comments from visitors to this site) that this point in time technically does not change no matter where you are located on the globe. This is very useful to computer systems for tracking and sorting dated information in dynamic and distributed applications both online and client side.
What happens on January 19, 2038?
On this date the Unix Time Stamp will cease to work due to a 32-bit overflow. Before this moment millions of applications will need to either adopt a new convention for time stamps or be migrated to 64-bit systems which will buy the time stamp a "bit" more time.
Here's how the timestamp works:
08/19/2019 # 8:59pm (UTC) translates to 1566248380 seconds since Jan 01 1970. (UTC)
Using the PHP date() function you can format to anything you want like:
echo date('l jS \of F Y h:i:s A', 1566248380);
Monday 19th of August 2019 08:59:40 PM
or MySQL:
SELECT from_unixtime(2147483647);
+--------------------------------------+
| from_unixtime(2147483647) |
+--------------------------------------+
| 2038-01-19 03:14:07 |
+--------------------------------------+
More example formats that you can convert to:
08/19/2019 # 8:59pm (UTC)
2019-08-19T20:59:40+00:00 in ISO 8601
Mon, 19 Aug 2019 20:59:40 +0000 in RFC 822, 1036, 1123, 2822
Monday, 19-Aug-19 20:59:40 UTC in RFC 2822
2019-08-19T20:59:40+00:00 in RFC 3339
The PHP Date() function can be used as a reference.
Again you will have to save the time zone:
America/Chicago
Set the PHP script time zone for your users by using date_default_timezone_set() function:
// set the default timezone to use. Available since PHP 5.1
date_default_timezone_set('UTC');
date_default_timezone_set('America/Chicago');
You can't store a date/time with time zone information.
MySQL does not store the time zone information on either DATETIME or TIMESTAMP. They are assumed to be on the server time zone.
The only ugly work around is to set the whole MySQL server/vm/docker container to UTC.
I'm thinking about making a project in a database with a large amount of objects / people / animals / buildings, etc.
The application would let the user select two candidates and see which came first. The comparison would be made by date, or course.
MySQL only allow dates after 01/01/1000.
If one user were to compare which came first: Michael Jackson or Fred Mercury, the answer would be easy since they came after this year.
But if they were to compare which came first: Tyranosaurus Rex or Dog, they both came before the accepted date.
How could I make those comparisons considering the SQL limit?
I didn't do anything yet, but this is something I'd like to know before I start doing something that will never work.
THIS IS NOT A DUPLICATE OF OTHER QUESTIONS ABOUT OLD DATES.
In other questions, people are asking about how to store. It would be extremely easy, just make a string out of it. But in my case, I'd need to compare such dates, which they didn't ask for, yet.
I could store the dates as a string, using A for after and B for before, as people answered in other questions. There would be no problem. But how could I compare those dates? What part of the string I'd need to break?
You could take a signed BIGINT field and use it as a UNIX timestamp.
A UNIX timestamp is the number of seconds that passed since January 1, 1970, at 0:00 UTC.
Any point in time would simply be a negative timestamp.
If my amateurish calculation is correct, a BIGINT would be enough to take you 292471208678 years into the past (from 1970) and the same number of years into the future. That ought to be enough for pretty much anything.
That would make dates very easy to compare - you'd simply have to see whether one date is bigger than the other.
The conversion from calendar date to timestamp you'd have to do outside mySQL, though.
Depending on what platform you are using there may be a date library to help you with the task.
Why deal with static age at time of entry and offset?
User is going to want to see a date as a date anyway
Complex data entry
Three fields
year smallint (good for up to -32,768 BC)
month tinyint
day tinyint
if ( (y1*10000 + m1*100 + d1) > (y2*10000 + m2*100 + d2) )
OK I had an idea.
Store the age in days, since the hours/seconds are irrelevant for this case.
Christ's age in days: -2015 * 365.
Dog's age in days: -40000 * 365.
In order to make precise calculations, I'd only need an extra field with the date I have added the values. Then add to the "age in days" the difference in days from the day I have added the register, from the day the user is making the comparison.
For example:
Dog's age has been added in 29/12/2015 and the age in days is -40000 * 365.
User is making a comparison on day 29/01/2016.
The difference in days between the two dates is 31 days.
So dog's age in days should be -40000 * 365 - 31.
Using an unsigned big int can do the trick.
Thanks to Pekka for suggesting using negative numbers for any date before the current date.
We've been working on implementing timezone support for our Web app.
This great SO post has helped us a bunch: Daylight saving time and time zone best practices
We've implelmented the OLSON TZ database in MYSQL and are using that for TZ conversions.
We're building a scheduling app so:
We are storing all our bookings which occur on a specific date at a specific time in UTC time in DateTime fields and converting them using CONVERT_TZ(). This is working great.
What we aren't so sure about is stuff like vacations and breaks:
Vacations are just Date references and don't include a time portion. Because CONVERT_TZ() doesn't work on date objects we are guessing that we are best to just store the date value as per the user's timezone?
id1 id3 startDate endDate
-----------------------------
3 6 2010-12-25 2011-01-03
4 3 2010-09-22 2010-09-26
Same thing with recurring breaks during stored for each day of the week. We currently store their breaks indexed 0-6 for each day of the week. Because these are just time objects we can't use CONVERT_TZ() and assume we should just store them as time values in the user's time zone?
bID sID dayID startTime endTime
--------------------------------
1 4 1 12:00:00 14:00:00
2 4 4 13:30:00 13:30:00
In this case with vacations and breaks we would only compare them to booking times AFTER the booking times have been converted to the user's local time.
Is this the correct way to handle things, or should we be storing both vacations and breaks in some other way so that we can convert them to UTC (not sure how this would work for breaks).
Thanks for your assistance!
The two storage formats look fine. You just need to convert them to the user's local time when you pull them out of the table.
Actually, for the breaks table I presume they're already nominally in local time, so you just compare directly against the local time of the appointment.
I don't understand your question well enough to say my answer is 100% correct for you. But I think what you need to do is store the DateTime in "local" time and also store the timezone. This way you have it correct even if daylight savings time shifts (which happens).
Good article at http://blogs.windwardreports.com/davidt/2009/11/what-every-developer-should-know-about-time.html (yes by me).
I can't see any info about that. Where can I find the oldest date Mysql can support ?
For the specific example you used on your question (year 1200), technically things will work.
In general, however, timestamps are unadvisable for this uses.
First, the range limitation is arbitrary: in MySQL it's Jan 1st, 1000. If you are working with 12-13th century stuff, things go fine... but if at some moment you need to add something older (10th century or earlier), the date will miserably break, and fixing the issue will require re-formatting all your historic dates into something more adequate.
Timestamps are normally represented as raw integers, with a given "tick interval" and "epoch point", so the number is indeed the number of ticks elapsed since the epoch to the represented date (or viceversa for negative dates). This means that, as with any fixed-with integer data-type, the set of representable values is finite. Most timestamp formats I know about sacrifice range in favor of precision, mostly because applications that need to perform time arithmetic often need to do so with a decent precision; while applications that need to work with historical dates very rarely need to perform serious arithmetic.
In other words, timestamps are meant for precise representation of dates. Second (or even fraction of second) precission makes no sense for historical dates: could you tell me, down to the milliseconds, when was Henry the 8th crowned as King of England?
In the case of MySQL, the format is inherently defined as "4-digit years", so any related optimization can rely on the assumption that the year will have 4 digits, or that the entire string will have exactly 10 chars ("yyyy-mm-dd"), etc. It's just a matter of luck that the date you mentioned on your title still fits, but even relying on that is still dangerous: besides what the DB itself can store, you need to be aware of what the rest of your server stack can manipulate. For example, if you are using PHP to interact with your database, trying to handle historical dates is very likely to crash at some point or another (on a 32-bit environment, the range for UNIX-style timestamps is December 13, 1901 through January 19, 2038).
In summary: MySQL will store properly any date with a 4-digit year; but in general using timestamps for historical dates is almost guaranteed to trigger issues and headaches more often than not. I strongly advise against such usage.
Hope this helps.
Edit/addition:
Thank you for this very insteresting
answer. Should I create my own algo
for historical date or choose another
db but which one ? – user284523
I don't think any DB has too much support for this kind of dates: applications using it most often have enough with string-/text- representation. Actually, for dates on year 1 and later, a textual representation will even yield correct sorting / comparisons (as long as the date is represented by order of magnitude: y,m,d order). Comparisons will break, however, if "negative" dates are also involved (they would still compare as earlier than any positive one, but comparing two negative dates would yield a reversed result).
If you only need Year 1 and later dates, or if you don't need sorting, then you can make your life a lot easier by using strings.
Otherwise, the best approach is to use some kind of number, and define your own "tick interval" and "epoch point". A good interval could be days (unless you really need further precission, but even then you can rely on "real" (floating-point) numbers instead of integers); and a reasonable epoch could be Jan 1, 1. The main problem will be turning these values to their text representation, and viceversa. You need to keep in mind the following details:
Leap years have one extra day.
The rule for leap years was "any multiple of 4" until 1582, when it changed from the Julian to the Gregorian calendar and became "multiple of 4 except those that are multiples of 100 unless they are also multiples of 400".
The last day of the Julian calendar was Oct 4th, 1582. The next day, first of the Gregorian calendar, was Oct 15th, 1582. 10 days were skipped to make the new calendar match again with the seasons.
As stated in the comments, the two rules above vary by country: Papal states and some catholic countries did adopt the new calendar on the stated dates, but many other countries took longer to do so (the last being Turkey in 1926). This means that any date between the papal bull in 1582 and the last adoption in 1926 will be ambiguous without geographical context, and even more complex to process.
There is no "year 0": the year before year 1 was year -1, or year 1 BCE.
All of this requires quite elaborate parser and formater functions, but beyond the many case-by-case breakings there isn't really too much complexity (it'd be tedious to code, but quite straight-forward). The use of numbers as the underlying representation ensures correct sorting/comparing for any pair of values.
Knowing this, now it's your choice to take the approach that better fits your needs.
From the documentation:
DATE
A date. The supported range is '1000-01-01' to
'9999-12-31'.
Yes. MySQL dates start in year 1000.
For whatever it's worth, I found that the MySQL DATE field does support dates < 1000 in practice, though the documentation says otherwise. E.g., I was able to enter 325 and it stores as 0325-00-00. A search WHERE table.date < 1000 also gave correct results.
But I am hesitant to rely on the < 1000 dates when they are not officially supported, plus I sometimes need BCE years with more than 4 digits anyway (e.g. 10000 BCE). So separate INT fields for year, month and day (as suggested above) do seem the only choice.
I do wish the DATE type (or perhaps a new HISTDATE type) supported a full range of historical dates - it would be nice to combine three fields into one and simply sort by date instead of having to sort by year, month, day.
Use SMALLINT for year, so the year will accept from -32768 (BC) to 32768 (AD)
As for months and days, use TINYINT UNSIGNED
Most historical events dont have months and days, so you could query like this :
SELECT events FROM history WHERE year='-4990'
Result : 'Noah Ark'
Or : SELECT events FROM history WHERE year='570' AND month='4' AND day='20'
return : "Muhammad pbuh was born"
Depending on requirements, you could also add DATETIME column and make it NULL for date before 1000 and vice versa (thus saving some bytes)
This is an important and interesting problem which has another solution.
Instead of relying on the database platform to support a potentially infinite number of dates with millisecond precision, rely on an object-oriented programming language compiler and runtime to correctly handle date and time arithmetic.
It is possible to do this using the Java Virtual Machine (JVM), where time is measured in milliseconds relative to midnight, January 1, 1970 UTC (Epoch), by persisting the required value as a long in the database (including negative values), and performing the required conversion/calculation in the component layer after retrieval.
For example:
Date d = new Date(Long.MIN_VALUE);
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy G HH:mm:ss Z");
System.out.println(df.format(d));
Should show:
Sun, 2 Dec 292269055 BC 16:47:04 +0000
This also enables independence of database versions and platforms as it abstracts all date and time arithmetic to the JVM runtime, i.e. changes in database versions and platforms will be much less likely to require re-implementation, if at all.
I had the similar problem and I wanted to continue relay on date fields in the DB to allow me use date range search with accuracy of up-to a day for historic values.
(My DB includes date of birth and dates of roman emperors...)
The solution was to add a constant year (example: 3000) to all the dates before adding them to the DB and subtracting the same number before displaying the query results to the users.
If you DB has already some dates value in it, remember to update the exiting value with the new const number.