Using AS400 date in SSRS Report - date

Running a SELECT against the AS400 using the IBMDA400 OleDb provider appears to return dates as string values, SSRS just laughs at you when you try and apply a date format to the field. I've tried a simple CAST in the SELECT to no avail.
How can I get an actual DBTYPE_DBDATE struct back from the iSeries OleDb provider?
I should mention that the dates in question are all being returned by a UDF with a type of DATE. IBM appears to map DATE type into a DBTYPE_STR OleDb type.

The field(s) in the table(s) are probably not defined as a date type. You will need to convert them using the DATE function as part of the query.
You can use the DSPFFD command, the Navigator, or query the SYSIBM.SQLCOLUMNS table to view the field definitions.
UPDATE
After further testing with the IBMDA400 provider I found the Convert Date Time To Char property hidden away in the OLE DB Technical Reference installed as part of the Programmer's Toolkit with Access. The default value is TRUE. Set Convert Date Time To Char=FALSE in the connection string or properties to disable this 'feature'.
Here's a quick VBA test:
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=IBMDA400;Data Source=...;User ID=...;Password=...;Convert Date Time To Char=FALSE"
Set rs = cn.Execute("SELECT DATE(NOW()) FROM SYSIBM.SYSDUMMY1")
MsgBox "Returned ADO type: " & rs.Fields(0).Type
See MSDN: DataTypeEnum for the possible ADO data types. adDBDate is 133.
IBM i Access for Windows OLE DB Technical Reference
IBM i OLE DB provider functions > Special Properties
Convert Date Time To Char
Specifies conversion of DB2 for IBM i Date, Time, and Timestamp data types to corresponding PC data types and vice versa.
Settings and Return Values
Sets or returns one of the following string values. The default value is "TRUE".
"TRUE"
DB2 for IBM i Date, Time, and Timestamp data types are treated as character strings. When reading IBM i data, these values are converted to character strings. When writing data to the system, character strings are expected as input for these values. The supported character string format for the Date data type is ISO format: yyyy-mm-dd. The supported character string format for the Time data type is the earlier version of the ISO format: hh.mm.ss. The supported character string format for the Timestamp data type is: yyyy-mm-dd-hh.mm.ss.nnnnnn.
"FALSE"
DB2 for IBM i Date, Time, and Timestamp data types will be converted to PC Date, Time, and Timestamp data types. Care should be taken when using this value in an environment that only supports the Variant Date data type (such as Visual Basic). You may encounter unexpected errors due to truncation or overflow caused by the limitations of the Variant Date data type.
Following are additional considerations when Convert Date Time To Char is FALSE.
The variant Date data type, which is actually a timestamp, does not support micro-seconds - the precision of the DB2 for IBM i timestamp. The OLE DB provider will truncate the fractional timestamp without reporting an error. For example, 1990-03-02-08.30.00.100517 will become 1990-03-02-08.30.00.000000. All updated or inserted timestamp values will have 0 micro-seconds.
Leap second overflow error. The OLE DB timestamp allows up to two leap seconds (a value of 60 or 61). DB2 for IBM i supports a maximum value of 59. An overflow error is returned if leap seconds are set.
The variant Date data type does not support the data limits of an ISO date or timestamp. The value "0001-01-01", used as a default date in many databases, including DB2 for IBM i, will cause an overflow.
DB2 for IBM i supports a time value of 24:00:00 for certain older formats of the TIME data type. The OLE DB provider will convert values of 24:00:00 to 00:00:00 without any error message or warning.
Typically for VB variant, a date value of 1899-12-30 (which is a 0 date) is used to imply a Time only variant date. A time of Midnight (00:00:00) is used to imply a Date only variant date.
Remarks
This custom property is available on the ADO connection object. The property is read/write when the connection is closed and read-only when the connection is open.
Delphi example
<connection>.Provider := 'IBMDA400';
<connection>.Properties('Convert Date Time To Char') := "TRUE";
OR
<connection>.Open('Provider=IBMDA400;Data Source=SystemA;Convert Date Time To Char =TRUE', 'Userid', 'Password');
PowerBuilder example
<connection>.Provider = "IBMDA400"
SetProperty(<connection>), "Convert Date Time To Char", "TRUE")
OR
<connection>.Open("Provider=IBMDA400;Data Source=SystemA;Convert Date Time To Char=TRUE", "Userid", "Password")
Visual Basic example
<connection>.Provider = "IBMDA400"
<connection>.Properties("Convert Date Time To Char") = "TRUE"
AND/OR
<connection>.Open "Provider=IBMDA400;Data Source=SystemA;Convert Date Time To Char=TRUE", "Userid", "Password")

It appears the correct answer is, you can't. No way, no how, does the IBMDA400 provider map any type into a DBTYPE_DBDATE.
What you can do is use the DateValue() SSRS function to convert the returned DBTYPE_STR value to a date/time serial. From there the format functions will work on it.
I didn't have a problem, here in North America, with the DateValue() function directly interpreting the returned DBTYPE_STR value, however, this could be an issue in other locales due to date format differences.

Related

Converting a string contain date and time using to_date function

How to use to_date function in oracle-sqldeveloper to convert a string
May 1 2019 12:00 to date datatype? Does Date in SQL store time too,
or it only stores date? I tried using the to_date function with some
format but it always removes the time part.
If the time is not possible in Date datatype what could be a good alternative?
You can convert your date to a string with (assuming 24-hour values, which seems likely as you don't have an AM/PM marker):
to_date('May 1 2019 12:00', 'Mon DD YYYY HH24:MI', 'nls_date_language=English')
The format elements are in the documentation. I've included the optional third argument to to_date() because your month name has to be interpreted in English, regardless of your session settings.
it always removes the time part
Oracle dates always have both date and time parts, even if the time is set to midnight. You're probably seeing the result of that query as '01-MAY-19'.
Dates don't have any intrinsic human-readable format; Oracle uses its own internal representation, which you generally don't need to worry about.
In most clients and IDEs the session NLS_DATE_FORMAT setting is used to display native dates as strings. For historic reasons that still defaults to DD-MON-YY, despite Y2K, during database creation. it can be changed at database level, and sessions will then inherit that. But each session can override it, e.g. by issuing:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
You can also explicitly convert a date value back to a string, and specify which format elements you want to include, via a to_char() call. Only do that when displaying a value - if you're storing dates or passing them around to functions, always do that as the proper date data type, not as strings. (If you have to pass them outside the database as strings, e.g. to a remote API, you'd usually want to use an ISO-8601 format).
db<>fiddle showing the default output, explicitly formatted as a string (again, for display only - do not store or manipulate dates as string), and with the session format modified.
In SQL Developer you can also go to Tools->Preferences->Database->NLS and change the 'Date format' there - that setting will then apply when you create new sessions, without having to issue alter session each time.

Clojure: creating a date-time object for the JDBC

I created my PostgreSQL table with a column:
updated_at timestamp(0) with time zone
I'm using clojure.java-time but I can't create a string (or should be an object?) to set the current date-time. I tried with:
(time/format "yyyy-MM-dd HH:ss" (time/local-date-time))
and with that string I tried:
(db/update-answer! {:updated_at "2019-12-25 14:08", :id 102, :answer "Foo"}
but the JDBC tells me that the field "updated_at" doesn't have a valid type. As far as I understand the JDBC is still using the old java API for handle the dates and time zones and not the new in JDK 8. So, in summary I don't know how to create a string or an object valid for Postgresql time-stamp column using clojure.java-time.
Someone advises me about extending the JDBC protocol, I'm reading about it but for now I'm just looking for a way to create a valid object and finish this update.
UPDATED
After followed this page I could give format to Instant with:
(ns zentaur.hiccup.helpers-view
(:require [clojure.tools.logging :as log]
[java-time :as jt])
(:import [java.time ZoneId]))
(defn format-date [date]
(log/info (str ">>> DATE >>>>> " date "und type >>> " (type date)))
(jt/format "yyyy-MM-dd HH:mm" (.atZone date (ZoneId/systemDefault))))
(Java syntax, not Clojure)
tl;dr
Use objects, not Strings. Use java.time classes in Java, never the legacy date-time classes.
myPreparedStatement
.setObject(
… ,
OffsetDateTime.now()
)
Even better would be a trigger to do this automatically.
in summary I don't know how to create … an object valid for Postgresql time-stamp column using clojure.java-time.
Call OffsetDateTime.now() to get a java.time.OffsetDateTime object holding the current moment.
Use a database column of type TIMESTAMP WITH TIME ZONE rather than TIMESTAMP (short for TIMESTAMP WITHOUT TIME ZONE).
Moments
set the current date-time
If you want to track moments, specific points on the timeline, you are using the wrong data type.
You are using TIMESTAMP which is short for TIMESTAMP WITHOUT TIME ZONE. This type lacks any concept of time zone or offset-from-UTC. So this type cannot track moments. I suggest you avoid the short version of this type name in the future, to make your SQL clear. See Postgres doc.
You should be defining your column as TIMESTAMP WITH TIME ZONE. Postgres handles this type by always saving the moment as seen in UTC. Any provided time zone or offset in the input is used to adjust to UTC. Likewise, when retrieved, your value is always in UTC, an offset of zero hours-minutes-seconds.
Beware of middleware and tools that dynamically apply some time zone to the retrieved value. This clouds matters, creating the illusion that the value was stored in that time zone. The value was actually stored in UTC, always in UTC in Postgres.
Smart objects, not dumb strings
but I can't create a string (or should be an object?) to set the current date-time.
Don’t.
You should be exchanging objects between Java and Postgres, rather than mere strings. This avoids the time-zone injection problem mentioned above.
As of JDBC 4.2, you can exchange modern java.time objects with the database. Never use the legacy types such as Calendar, GregorianCalendar, java.util.Date, java.sql.Date, java.sql.Timestamp, and so on. These classes were terribly flawed, designed by people who did not understand date-time handling. They were supplanted as of the adoption of JSR 310.
Capture the current moment as an OffsetDateTime. Your JDBC driver might support Instant & ZonedDateTime but those types are optional, as seen in graphic table above. In contrast, JDBC requires support for OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
The explicit use of ZoneOffset.UTC in code above is not strictly required. If omitted, your JVM’s current default offset will be applied implicitly. Your JDBC driver and/or Postgres will adjust to UTC as discussed earlier above. My own preference for the sake of debugging/logging is to specify UTC so I can see the value as it will eventually be stored in the database.
And retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Default value
If you are simply recording the moment whenever a row is updated, no need to do that in your SQL. I suggest you write a trigger to be called whenever a row in that table is updated. Then you are guaranteed the value will be written no matter the mechanism by which the row was updated. And less to worry about when writing your SQL statements.

Data stored as DD/MM/YYYY in table, but querying in DD/MM/YYYY doesn't work for all dates MS Access 2016

There are probably many questions that are asking about date formats, but I haven't found anything like this.
I have a table, called t_birthday. t_birthday has a field called "DayOfMonth" which currently stores the data in a dd/mm/yyyy format. Lets say the record I have has the Date of 01/12/2016 (Dec 1, 2016).
Now, if I create a query using the "Query Design" option in the Create tab, I select my table t_birthday. For the field option, I select DayOfMonth. In the criteria option, I put =#01/12/2016#. When I click Run, it queries the database and returns the record with that date successfully.
However.. If I check the SQL generated from this Query Design, it is this:
SELECT t_birthday.DayOfMonth
FROM t_birthday
WHERE (((t_birthday.DayOfMonth)=#12/1/2016#));
If I try copy and pasting the DayOfMonth value from the table into that query, it wouldn't work. Notice how the format in the query is mm/dd/yyyy, but in my table it's still dd/mm/yyyy. I never touched any of the date formatting options in my table, or even on my computer. When I actually create this record using a form, I have a date picker which is in the form of dd/mm/yyyy as well.
Questions:
In the query design, when I specify criteria in dd/mm/yyyy, why does it generate sql in the form of mm/dd/yyyy?
I can only query dates using dd/mm/yyyy format if the day number (1-31) is 13 or above, OR if the month value and the day value are the same (October 17, Jan 1, March 3, November 11, December 12, etc). mm/dd/yyyy still works for those dates previously mentioned. I can't query dates like November 7th, Feb 3rd, August 4th, etc using dd/mm/yyyy though. How do I get around this problem? I store the dates, and I use the values directly from the table as conditionals in my queries. I shouldn't have to alter my date value in order to use them.
Why can I write an SQL statement for dates with the day number above 13 in dd/mm/yyyy format or mm/dd/yyyy format? E.g., the WHERE clause can look like: WHERE DayOfMonth=#13/06/2018 or WHERE DayOfMonth=#06/13/2018 and it still returns the same record? Why does access not enforce a specific format?
EDIT:
Currently I run my query in VBA and return it into a recordset using the following:
Dim bdayRecords As RecordSet
Dim sql As String
sql = "SELECT t_birthday.DayOfMonth"
sql = sql & " FROM t_birthday"
sql = sql & " WHERE (((t_birthday.DayOfMonth)=#" & rs("DayOfMonth") & "#));"
bdayRecords = CurrentDb.OpenRecordset(sql)
Where rs in the where clause was a previous recordset with a date value stored in "DayOfMonth". The rs recordset retrieved the date value from a different table in the exact same way bdayRecords was populated.
bdayRecords won't find the records with the date values matching the criteria explained before.
Use a properly formatted string expression for the date value retrieved:
sql = sql & " WHERE t_birthday.DayOfMonth = #" & Format(rs("DayOfMonth").Value, "yyyy\/mm\/dd") & "#;"
The ISO sequence yyyy-mm-dd works everywhere, so make it a habit to use that.
SQL always uses mm/dd/yyyy. That's not dependent on how you format it.
You never actually store a date in a certain format. You display a date in a certain format. All dates in Access are stored as a double-precision floating number containing the number of days elapsed since 30-12-1899, with fractions as time. How dates are formatted has no influence whatsoever on your SQL statement
Always use either mm/dd/yyyy or yyyy-mm-dd in your SQL. VBA only takes mm/dd/yyyy.
However, Access is opportunistic when working with clearly invalid dates, such as 13/1/2018. Because no 13th month exists, it parses it as the 13th of january, even though it's not a valid date.
If you're using values from other queries, there shouldn't be any problems, since the values never get cast back and forth to strings. You only get in trouble when casting a date to a string and then back to a date, which is not something you should do in queries, ever.
To avoid casting back and forth between strings, you can either refactor your code to a single query instead of retrieving a value from a recordset and inserting that value in a string SQL statement, or use parameters, which allows you to use the date value directly in an SQL statement.
For explanations why these design choices are made, ask Microsoft, they wrote the program. This is just how it works.

Postgresql Ethiopian Date Format

Is there a way to store a date in a PostgreSQL db using the Ethiopian date format? I'm trying to store 29th or 30th of February but it throws an error, because in the Julian calendar there's no such thing. Any inputs?
I am not sure that I'll tell you something new but...
Databases are used by programs or by interfaces, I never saw databases that are used by end-user in console with psql.
If you are develop an application, that must display dates in specific calendar, you can store date in PostgreSQL in TIMESTAMP. All operations with dates will work correct in database. But you have to implement conversion from TIMESTAMP into string representation and vice versa in your application manually. If this is most important thing for your application, you will do this.
All queries that must return date you will write with conversion into DOUBLE PRECISION e.g.
SELECT EXTRACT(EPOCH FROM timestamp_field)
This returns DOUBLE PRECISION value that represents timestamp in numerical format.
All date parameters in queries you have convert from numerical presentation in TIMESTAMP using built-in function to_timestamp:
update table_name set
timestamp_fileld = to_timestamp(1384852375.46666)
The other solution is to write psql functions that do this for you directly in queries, but anyway you need to handle each input/output of date fields in queries.

date conversion in SQL parameter field in Crystal reports

I have a Crystal report (version XI r3) that uses a SQL command object to retrieve its data. In the command object I have a parameter for a date. My database uses "date" fields stored as numeric values in YYYYMMDD format, so I've specified the parameter as numeric and added a prompt to say "enter date in YYYYMMDD format".
My users don't much care for that; they want to be able to use the date-picker and/or to be able to enter the date in MM/DD/YYYY format.
My investigations so far have led me to believe that if I convert the parameter to a true date datatype, I won't be able to make use of it in the SQL command object because I can't convert it from a date to a number in the SQL statement, so I'd have to do my date-range control in the Crystal Select Wizard rather than in my SQL statement, which could slow my report down by an order of magnitude or two (since I'm hitting a table that is indexed by this date field, and that has a lot of records per day).
Am I wrong? Is there a way to let a user enter a date in MM/DD/YYYY format and still be able to use it as a numeric YYYYMMDD parameter in my SQL command object?
I'm afraid you would have to modify the original Command's to replace the numeric parameter with a date parameter, and do the conversion from date to number within the Command itself.
So, within the Command:
WHERE MyDate = {?MyNumberParam)
would become:
WHERE MyDate = (YEAR({?MyDateParam})*10000) + (MONTH({?MyDateParam})*100) + DAY({?MyDateParam})
The last part will convert 20th April 2012 to (2012*10000 + 4*100 + 20) = 20120420, which I believe is what you'd want.