-1

We are storing time in like '22-NOV-17 05.33.51.937000000 PM' format with server default timezone CST. We have half an our time comparison in many places. So CST to CDT and CDT to CST are facing issues because on retrieval time for database we can not identify the time zone. So it is breaking our time comparison on CST to CDT and CDT to CST time changes.

We can not change our storing logic like store with timezone and store in UTC timezone because it will breaking our existing logic in many places.

So is there any way to identity date timezone like CST or CDT, stored in database with '22-NOV-17 05.33.51.937000000 PM' format.

Chirag Kathiriya
  • 427
  • 5
  • 14
  • 1
    You're storing that as a string? Or is that just how your client is displaying a timestamp value? Dates and timestamps are stored with an internal format, not anything like you're shown. – Alex Poole Nov 22 '17 at 12:35
  • 2
    What is `CST` and `CDT`? See possible answers by `SELECT TZNAME, TZABBREV, TZ_OFFSET(TZNAME) FROM V$TIMEZONE_NAMES WHERE TZNAME IN ('CST','CDT') OR TZABBREV IN ('CST','CDT');` – Wernfried Domscheit Nov 22 '17 at 12:40
  • 1
    In the fall you set the clock one our backward and therefore repeat the same times again. That means that the times within that hour (times two) are ambiguous, you cannot tell whether they are CST or CDT. For all other times you can find out. – Ole V.V. Nov 22 '17 at 12:51
  • We are storing as a timestamp in database – Chirag Kathiriya Nov 22 '17 at 12:51
  • Agree @OleV.V. But we need also take care those hours. because it is major impacting in our business. – Chirag Kathiriya Nov 22 '17 at 12:55
  • 1
    We are using JDK 7 and ZonedDateTime is in JDK 8. – Chirag Kathiriya Nov 22 '17 at 12:57
  • 1
    The statement from Ole V.V. is quite clear: If you store the information as `TIMESTAMP` then there is no way to find out whether (as example) `2017-11-05 01:30:00` was CST or CDT. – Wernfried Domscheit Nov 22 '17 at 13:02
  • I think you said it clearer than I, @WernfriedDomscheit. Anyway, from [the documentation](https://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm#i1006050): “The `TIMESTAMP` datatype is an extension of the `DATE` datatype. It stores year, month, day, hour, minute, and second values. It also stores fractional seconds, which are not stored by the `DATE` datatype.” No time zone or offset there. – Ole V.V. Nov 22 '17 at 13:21
  • Your need to take care of the ambiguous hours — does that mean you are asking the impossible, or would it be OK to make some assumption about the zone offset for those few times? Or have you got other means that may help you guess? – Ole V.V. Nov 22 '17 at 14:23
  • Yes @OleV.V. I need to take care of the ambiguous hour. Is there any possible way to handle this situation? – Chirag Kathiriya Nov 22 '17 at 16:48
  • Depends on what you require. You may for instance assume they are CDT all of them — I guess you’ll be about half right. If you know the order of the times, you can assume they change to CST when they seem to go backward. As said a couple of times already, with only the database timestamps, there is *no* way whatsoever to know which ambiguous timestamps are CST and which are CDT. – Ole V.V. Nov 22 '17 at 17:35

2 Answers2

3

We are storing time in like '22-NOV-17 05.33.51.937000000 PM' format

does not make sense with your comment

We are storing as a timestamp in database

In Oracle databases, a TIMESTAMP does not have a format - it is stored in the database as 11 bytes representing year (2 bytes), month, day, hours, minutes, seconds (1 byte each) and fractional seconds (4 bytes). It is only when whatever interface you are using (SQL/Plus, SQL Developer, Toad, Java, PHP, etc.) to talk to the database decides to show it to you, the user, that that interface will format it as a string (but the database will just keep it as bytes without any format).

Assuming you are using SQL/Plus or SQL Developer then you can find the default format using:

SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_TIMESTAMP_FORMAT';

And change the default format using:

ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF9';

Or for TIMESTAMP WITH TIME ZONE

ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF9 TZR';

So is there any way to identity date timezone like CST or CDT, stored in database with '22-NOV-17 05.33.51.937000000 PM' format.

No, without any other meta-data that could identify the source of the timestamp and indicate which location it came from (i.e. is there another column that links to the user who entered the data that could be mapped to a physical location and so a time zone) then it is impossible to determine which time zone it is from.

You will either need to:

  • change your database column to TIMESTAMP WITH TIME ZONE and store the time zone; or
  • convert all the values to the same time zone when you are storing them.
MT0
  • 143,790
  • 11
  • 59
  • 117
  • Thanks @MT0, if i store timestamp with timezone then on retrieval time java/hibernate will automatically defect time zone or i need to do some extra operation? – Chirag Kathiriya Nov 23 '17 at 07:07
  • @ChiragKathiriya Sorry, I do not use Hibernate so am not able to answer that. – MT0 Nov 23 '17 at 08:30
  • @ChiragKathiriya See ["How can I map TIMESTAMP WITH TIME ZONE to a Java data type using Hibernate 3.3.2GA?"](https://stackoverflow.com/q/4078520/1509264) or ["How to store date/time and timestamps in UTC time zone with JPA and Hibernate"](https://stackoverflow.com/q/508019/1509264). – MT0 Nov 23 '17 at 08:41
1

I am assuming by CST and CDT you mean North American Central Standard Time and Central Daylight Time such as observed in Rainy River, Chicago and Mexico (the city) among other places. More on this ambiguity later.

For 99.977 % of all times it is fairly easy to know whether they are standard time or daylight saving time. Only times from the two hours around the transition from DST to standard time are ambiguous, and as said in the comments, there is no way to know from the time stamp which is the right way to resolve this ambiguity.

java.time

This answer will take you as far into the future as possible without taking you away from Java 7. You can still use java.time, the modern Java date and time API also known as JSR-310. It has been backported to Java 6 and 7 in the ThreeTen Backport, so it’s a matter of getting this and adding it to your project (just until one day you upgrade to Java 8 or later).

I am taking your word for your date-time string format. What we can do with it:

    DateTimeFormatter storedFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("d-MMM-uu hh.mm.ss.SSSSSSSSS a")
            .toFormatter(Locale.US);
    ZoneId zone = ZoneId.of("America/Mexico_City");

    String storedTime = "22-NOV-17 05.33.51.937000000 PM";
    LocalDateTime dateTime = LocalDateTime.parse(storedTime, storedFormatter);
    // First shot -- will usually be correct
    ZonedDateTime firstShot = ZonedDateTime.of(dateTime, zone);
    System.out.println(firstShot);

This prints:

2017-11-22T17:33:51.937-06:00[America/Mexico_City]

You can see that it picked an offset of -06:00, which means that the time is in standard time (CDT is -05:00).

Since your month abbreviation is in all uppercase, I needed to tell the formatter to parse case insensitively. If America/Mexico_City time zone is not appropriate for you, pick a better one, for example America/Rainy_River or America/Chicago.

Ambiguous times in fall

I once had to parse a log file containing date-times without indication of standard time and summer time (DST). Since we assumed time would always move forward, we failed at the transition to standard time, and one hour of the log file was lost. In this case we might have solved it using the information that times were in summer time until the leap backward by an hour, from there they were in standard time. You may want to think about whether something similar will be possible for you.

Other options include just taking DST time every time — this is what the above code will do — or taking an average and living with the error thus introduced.

We can at least detect the ambiguous times:

    ZoneOffset standardOffset = ZoneOffset.ofHours(-6);
    ZoneOffset dstOffset = ZoneOffset.ofHours(-5);
    // check if in fall overlap
    ZonedDateTime standardDateTime 
            = ZonedDateTime.ofLocal(dateTime, zone, standardOffset);
    ZonedDateTime dstDateTime 
            = ZonedDateTime.ofLocal(dateTime, zone, dstOffset);
    if (! standardDateTime.equals(dstDateTime)) {
        System.out.println("Ambiguous, this could be in CST or CDT: " + dateTime);
    }

Now if the string was 29-OCT-17 01.30.00.000000000 AM, I get the message

Ambiguous, this could be in CST or CDT: 2017-10-29T01:30

ZonedDateTime.ofLocal() will use the provided offset for resolving the ambiguity if it is a valid offset for the date-time and zone.

Non-existing times in the spring

Similarly we can detect if your date-time falls in the gap where the clock is moved forward in the transition to DST:

    // Check if in spring gap
    if (! firstShot.toLocalDateTime().equals(dateTime)) {
        System.out.println("Not a valid date-time, in spring gap: " + dateTime);
    }

This can give a message like

Not a valid date-time, in spring gap: 2018-04-01T02:01

I suggest you can safely reject such values. They cannot be correct.

Avoid the three letter time zone abbreviations

CST may refer to Central Standard Time (in North and Central America), Australian Central Standard Time, Cuba Standard Time and China Standard Time. CDT may mean Central Daylight Time or Cuba Daylight Time. The three and four letter abbreviations are not standardized and are very often ambiguous. Prefer time zone IDs in the region/city format, for example America/Winnipeg.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161