Operating with datetimes in SQLite - date

I'm interested in knowing the different possibilities to operate with datetimes in SQLite and understand its pros and cons. I did not find anywhere a detailed explanation of all the alternatives.
So far I have learned that
SQLite doesn't actually have a native storage class for timestamps /
dates. It stores these values as NUMERIC or TEXT typed values
depending on input format. Date manipulation is done using the builtin
date/time functions, which know how to convert inputs from the other
formats.
(quoted from here)
When any operation between datetimes is needed, I have seen two different approaches:
julianday function
SELECT julianday(OneDatetime) - julianday(AnotherDatetime) FROM MyTable;
Number of days is returned, but this can be fractional.
Therefore, you can also get some other measures of time with some extra operations. For instance, to get minutes:
SELECT CAST ((
julianday(OneDatetime) - julianday(AnotherDatetime)
) * 24 * 60 AS INTEGER)
Apparently julianday could cause some problems:
Bear in mind that julianday returns the (fractional) number of 'days'
- i.e. 24hour periods, since noon UTC on the origin date. That's usually not what you need, unless you happen to live 12 hours west of
Greenwich. E.g. if you live in London, this morning is on the same
julianday as yesterday afternoon.
More information in this post.
strftime function
SELECT strftime("%s", OneDatetime)-strftime("%s", AnotherDatetime) FROM MyTable;
Number of seconds is returned. Similarly, you can also get some other measures of time with some extra operations. For instance, to get minutes:
SELECT (strftime("%s", OneDatetime)-strftime("%s", AnotherDatetime))/60 FROM MyTable;
More information here.
My conclusion so far is: julianday seems easier to use, but can cause some problems. strftime seems more verbose, but also safer. Both of them provide only as results a single unit (either days or hours or minutes or seconds), but not a combination of many.
Question
1) Is there any other possibility to operate with datetimes?
2) What would be the best way to get directly the difference of two datetimes in time format (or date or datetime), where datetime would be formatted as 'YYYY-mm-dd HH:MM:SS', and the result would be something in the same format?
I would have imagined that something like the following would work, but it does not:
SELECT DATETIME('2016-11-04 08:05:00') - DATETIME('2016-11-04 07:00:00') FROM MyTable;
> 01:05:00

Julian day numbers are perfectly safe when computing differences.
The only problem would be if you tried to convert them into a date by truncating any fractional digits; this would result in noon, not midnight. (The same could happen if you tried to store them in integer variables.) But that is not what you're doing here.
SQLite has no built-in function to compute date/time differences; you have to convert date/time values into some number first. Whether you use (Julian) days or seconds does not really matter from a technical point of view; use whatever is easier in your program.
If you started with a different format, you might want to convert the resulting difference back into that format, e.g.:
time(difference_value, 'unixepoch') -- from seconds to hh:mm:ss
time(0.5 + difference_value) -- from Julian days to hh:mm:ss

Related

Postgres - Convert Date Range to Individual Month

I have found similar help, but the issue was more complex, I am only good with the basics of SQL and am striking out here. I get a handful of columns a,b,c,startdate,enddate and i need to parse that data out into multiple rows depending on how many months are within the range.
Eg: a,b,c,1/1/2015, 3/15,2015 would become:
a,b,c,1/1/2015,value_here_doesnt_matter
a,b,c,2/1/2015,value_here_doesnt_matter
a,b,c,3/1/2015,value_here_doesnt_matter
Does not matter if the start date or end date is on a specific day, the only thing that matters is month and year. So if the range included any day in a given month, I'd want to output start days for each month in the range, with the 1st as a default day.
Could I have any advice on which direction to begin? I'm attempting generate_series, but am unsure if this is the right approach or how to make it work with keeping the data in the first few arbitrary columns consistent.
I think generate_series is the way to go. Without knowing what the rest of your data looks like, I would start with something like this:
select
a, b, c, generate_series(startdate, enddate, interval '1 month')::date
from
my_table

Represent date (without time) as single integer

Is there a standard way to represent a date as a single integer? (I only need to store dates, not full timestamps.)
If it matters, the reason I want to do this is because I'm storing the dates in a SQLite database on Android and would like to store them as numbers so they can be compared/sorted efficiently to return results from queries.
One good option might be YYYYMMDD, for example encoding today (Jan 13, 2014) as the integer 20140113.
Advantages:
It works for comparisons, as long as you only care about <, ==, and >;
It's reasonably human-readable;
It's compatible with the ISO 8601 standard.
Disadvantages:
It's not as easy to compute differences between dates;
SQLite won't recognize it as a date.
On the last point: The SQLite3 documentation says that SQLite3 has no specific storage types for dates and/or times. Instead, it recommends using one of:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
all of which apparently can be processed using SQLite's built-in date and time functions.
The latter argues in favor of the solution in bdf's answer. Picking an arbitrary time within the specified day is admittedly problematic, but I suggest picking noon UTC is unlikely to cause too many problems as long as you're careful to use it consistently. (Noon UTC can be on a different day if your time zone offset is 12 hours or more, but that's not an issue for most of the world.)
just set the time for every day to an arbitrary time of your choosing, such as 2 am.
Storing them as timestamps anyway might still be a good idea, since you'd have a lot more date formatting options.
An important detail to keep in mind are time zones. If e.g. your time falls in the gap between your time zone offset and GMT you might get unexpected results. So at first I propose we discuss date as the one visible to the user which is usually the one of the local time zone.
So if we assume local time, and we want to make use of the Date objects, there are 2 possible solutions, which I will present as JavaScript unit test style. First one is the one presented by Keith Thompson previously:
let date = new Date('1987-12-31T01:02:03')
let simpleDateInteger = (
date.getFullYear() * 10000 +
(date.getMonth() + 1) * 100 +
date.getDate()
)
expect(simpleDateInteger).toBe(19871231)
let fromSimpleDateInteger = new Date(
simpleDateInteger / 10000, // year
simpleDateInteger / 100 % 100 - 1, // month
simpleDateInteger % 100 // day
)
expect(fromSimpleDateInteger.toDateString()).toEqual(date.toDateString())
If you need more compact integers and each integer +1 representing the next day, i.e. a continuous representation you can go with this one:
let date = new Date('1987-12-31T00:01:02')
const DAY_IN_MILLISECONDS = 86400 * 1000
let timeZoneInMilliSeconds = date.getTimezoneOffset() * 60 * 1000
let continuousDateInteger = Math.floor(
(date.getTime() - timeZoneInMilliSeconds) / DAY_IN_MILLISECONDS
)
expect(continuousDateInteger).toBe(6573)
let fromContinuousDateInteger = new Date(
continuousDateInteger * DAY_IN_MILLISECONDS + timeZoneInMilliSeconds
)
expect(fromContinuousDateInteger.toDateString()).toEqual(date.toDateString())

UniVerse native date format

I am in the process of optimizing some UniVerse data access code we have which uses UniObjects. After some experimentation, it seems that using a UniSession.OConv call to parse certain things such as decimal numbers (most we have a MR4 or MR2 or MR2$) and dates (almost all are D2/) is extremely slow (I think it might make a call back to the server to parse it).
I have already built a parser for the MR*[$] codes, but I was wondering about the dates as they are stored so I can build one for D2/. Usually they seem to be stored as a 5 digit number. I thought it could be number of days since the Unix Epoch since our UniVerse server runs on HP-UX, but after finding '15766' as a last modified date and multiplying it by 86400 (seconds per day), I got March 02, 2013 which doesn't make sense as a last modified date since as far as I know that is still in the future.
Does anyone know what the time base of these date numbers are?
It is stored as a number of days. Just do a conversion on 0 and you will get the start date.
Edit:
As noted by Los, the Epoch used in UniVerse (and UniData) is 31st Dec 1967.
In Universe and any other Pick database, Dates and Times are stored as separate values.
The internal date is the number of days before of after 31/12/1967, which is day zero.
The internal time is the number of seconds after midnight. It can be stored as a decimal but is not normally.
In TCL there is a CDT command (stands for Convert Date) that converts dates from human readable to numeric and and vice versa:
CDT 9/28/2017
* Result: 18169
CDT 18169
* Result: 09/28/2017

Selecting records between two timestamps

I am converting an Unix script with a SQL transact command to a PostgreSQL command.
I have a table with records that have a field last_update_time(xtime) and I want to select every record in the table that has been updated within a selected period.
Say, the current time it 05/01/2012 10:00:00 and the selected time is 04/01/2012 23:55:00. How do I select all the records from a table that have been updated between these dates. I have converted the 2 times to seconds in the Unix script prior to issuing the psql command, and have calculated the interval in seconds between the 2 periods.
I thought something like
SELECT A,B,C FROM table
WHERE xtime BETWEEN now() - interval '$selectedtimeParm(in secs)' AND now();
I am having trouble evaluating the Parm for the selectedtimeParm - it doesn't resolve properly.
Editor's note: I did not change the inaccurate use of the terms period, time frame, time and date for the datetime type timestamp because I discuss that in my answer.
What's wrong with:
SELECT a,b,c
FROM table
WHERE xtime BETWEEN '2012-04-01 23:55:00'::timestamp
AND now()::timestamp;
If you want to operate with a count of seconds as interval:
...
WHERE xtime BETWEEN now()::timestamp - (interval '1s') * $selectedtimeParm
AND now()::timestamp;
Note the standard ISO 8601 date format YYYY-MM-DD h24:mi:ss which is unambiguous with any locale or DateStyle setting.
The first value for the BETWEEN construct must be the smaller one. If you don't know which value is smaller use BETWEEN SYMMETRIC instead.
In your question you refer to the datetime type timestamp as "date", "time" and "period". In the title you used the term "time frames", which I changed to "timestamps". All of these terms are wrong. Freely interchanging them makes the question even harder to understand.
That, and the fact that you only tagged the question psql (the problem hardly concerns the command line terminal) might help to explain why nobody answered for days. Normally, it's a matter of minutes around here. I had a hard time understanding your question.
Understand the data types date, interval, time and timestamp - with or without time zone. Start by reading the chapter "Date/Time Types" in the manual.
Error message would have gone a long way, too.
For anyone who is looking for the fix to this. You need to remove timestamp from the where clause and use BETWEEN!
TABLENAME.COL-NAME-FOR-TIMESTAMP BETWEEN '2020-01-29 04:18:00-06' AND CURRENT_TIMESTAMP

Set one date from another date

I am using Objective-C within X-code.
I am iterating through a dictionary which contains a date value as one of it's keys. All I want to do is get an array of all the distinct dates so I can use them in a table, as headers. I just plan on
iterating the dictionary and adding dates to a mutable array each time I encounter a new date.
I must set previous date to new date for comparisons to work and I am having a very difficult time figuring out how to set one date equal to another date.
This seems like it should be such a simple thing to do and I am trying to avoid converting the dates to strings first - but if that's what I have to do, then so be it.
Any help would be appreciated.
Thanks,
Gerry O.
If you know the time offset is same as GMT, you could do it by dividing the date's timestamp by 86400 seconds (or three more 0s in milliseconds) and comparing those. If a time offset is there, add or subtract by 3600 seconds per hour before you divide them. But then again, leap years and seconds would break that...
Most languages have libraries support extracting the year, date, month, etc. They take everything into account, usually.
In Objc, you can get a NSDateComponents from NSCalendar's components:fromDate: method. After this, you can call the components to see exactly what each component (I suggest year, month, day) is.
I think you want code to compare dates and You need two loops nested.... where in outer loops iterates with conditional inner loop... In inner loop you just check that previously you had the same date or not...
please go through the below post on same site...
How to compare two dates in Objective-C
hope you will get solution... else clarify your question....