2

I am developing a race timing system, and for several instances, I need to retrieve a time object from H2DB. Like its bretheren (or sisteren), the time data type is relative to 1st January 1970 and is expressed in SQL in the 'hh:mm:ss' format, with the date being by set by default to 01-01-1970. It is by default mapped to the a 'java.sql.Time' object. Being a trusting padawan, I coded the following to separate hours from minutes for display purposes.

 if(race.getCutOffTime()!=null){
    long cutOffHour=(race.getCutOffTime().getTime())/(3600000);
    int cutOffMinute=(int)(race.getCutOffTime().getTime()%(60*60*1000));
    System.out.println(cutOffHour+":"+cutOffMinute);
    }

Java's time handling stink arises however in that these functions output unexpected values, for example, the following statement from my db, gives an output of 3:30.

INSERT INTO MagEye.Races(RaceName, EventID,CutOffTime) 
VALUES ('TEST', SELECT EventID FROM MagEye.Events 
WHERE EventName='Sabrina Love',TIME '5:50:00');

Changing this statement to reflect a time of '0:0:0', gives me a value of "-2:00" What am I doing wrong? Thank you (in advance).

Edit As requested, here is my code for the database:

Table Creation Statement:

CREATE TABLE IF NOT EXISTS MagEye.Races (RaceID INT PRIMARY KEY AUTO_INCREMENT ,  RaceName VARCHAR(100) ,EventID INT, Description TEXT, MaxEntrants INT, MinAge INT, MaxAge INT, RacePrefix VARCHAR (5), TimingMethod CHAR(1), CutOffTime TIME, RaceEnd TIMESTAMP,Finished BOOLEAN DEFAULT FALSE, Autostart BOOLEAN, FOREIGN KEY(EventID) REFERENCES MagEye.Events(EventID));

Insertion Statement:

INSERT INTO MagEye.Races(RaceName, EventID,CutOffTime) VALUES ('TEST', SELECT EventID FROM MagEye.Events WHERE EventName='Sabrina Love',TIME '5:50:00');

Retrieval:

raceDB.result = raceDB.state.executeQuery("SELECT * FROM MagEye.Races WHERE EventID=" + eventID + " ORDER BY RaceName");
            java.util.ArrayList<Race> races = new java.util.ArrayList<>();


            while (raceDB.result.next()) {
                Race thisRace;
                String timingMethodString = raceDB.result.getString("TimingMethod");
                Race.TimingMethod timingMethod = null;
                if (timingMethodString != null) {
                    timingMethod = Race.TimingMethod.valueOf(timingMethodString);
                } else {
                    timingMethod = Race.TimingMethod.MANUAL;
                }
                thisRace = new Race(raceDB.result.getInt("RaceID"), event, raceDB.result.getString("RaceName"), raceDB.result.getString("Description"), raceDB.result.getInt("MaxEntrants"), raceDB.result.getInt("MinAge"), raceDB.result.getInt("MaxAge"), raceDB.result.getString("RacePrefix"), timingMethod,(raceDB.result.getTime("CutOffTime")), raceDB.result.getBoolean("Autostart"));
thisRace = new Race(raceDB.result.getInt("RaceID"), event, raceDB.result.getString("RaceName"), raceDB.result.getString("Description"), raceDB.result.getInt("MaxEntrants"), raceDB.result.getInt("MinAge"), raceDB.result.getInt("MaxAge"), raceDB.result.getString("RacePrefix"), timingMethod,(raceDB.result.getTime("CutOffTime")), raceDB.result.getBoolean("Autostart"));

Display:

if(race.getCutOffTime()!=null){
    long cutOffHour=(int)(race.getCutOffTime().getTime())/(3600000);
    RacesCutOffLength.setText(cutOffHour+"");      
    int cutOffMinute=(int)(race.getCutOffTime().getTime()%(60*60*1000));
    this.RacesMinutes.setText(cutOffMinute+"");
    }
    else{
        RacesCutOffLength.setText("0");      
        RacesMinutes.setText("0");      
    }

Edit: I've decided to replace the Time object with a long primative

RabtFt
  • 153
  • 15
  • "stink"? Good craftsmen do not blame the tools... – Thorbjørn Ravn Andersen Sep 24 '12 at 21:11
  • The two hour discrepancy is most likely because you are in a GMT+2 time zone. – Thorbjørn Ravn Andersen Sep 24 '12 at 21:12
  • Why is it then, that every mention of Java time in Stack-Overflow has a t least one or more references to Joda time?? :P – RabtFt Sep 25 '12 at 19:08
  • 1
    that is because the abstraction of dates as specific milliseconds does not lend well _in general_ to dealing with periods of time. Given sufficient assumptions it can work good enough. Your problem seems to be an incorrect understanding of how to map between your Java and SQL representations of dates. – Thorbjørn Ravn Andersen Sep 25 '12 at 20:29
  • @ThorbjørnRavnAndersen Yip. I was being an idiot. But it was also a problem with time zones. I've decided to use long instead for this instant and for the others, I'm having to map it to my locale's timezone. formatter.applyLocalisedFormat("HH:mm"); – RabtFt Sep 26 '12 at 05:51

1 Answers1

2

java.util.Date is always the UTC time. So, depending of your locale, there will be probably an offset.

I have heard of Joda Time as a better Java API for dealing with times.

Anyway, the problem comes from mixing dates treated by java.util.Date and others directly passed as part of the SQL.

As long as you keep using java.util/sql.Date for everything (and you don't peak inside the DB)(and you don't change the locale) the results will be coherent. The trouble will begin when your SQL tries to pass the values directly as text. So either use Date everywhere or, as trashgod says, everytime you use a Date you take care of setting the Locale to "GMT" (so the internal representation and the output of the Date are the same). Note that this does include Date returned from the DB

SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • 1
    Set the formatter's time zone, a shown [here](http://stackoverflow.com/a/2705674/230513). – trashgod Sep 24 '12 at 19:56
  • I have heard of Joda Time, however due to the restrictions placed upon this project, using external libraries, especially ones which are >3 MB in size is frowned upon. – RabtFt Sep 24 '12 at 19:57
  • 3
    As long as you keep using `java.util/sql.Date` for **everything** (and you don't peak inside the DB)(and you don't change the locale) the results will be coherent. The trouble will begin when your SQL tries to pass the values directly as text. So either use `Date` everything or, as trashgod says, everytime you use a `Date` you take care of setting the Locale to "GMT" (so the internal representation and the output of the `Date` are the same). Note that this does include `Date` returned from the DB. – SJuan76 Sep 24 '12 at 20:08
  • Another alternative would be change how you manage the `Date` in the database; either set their values in the SQL as used by the DB `#dd/MM/yyyy#` for Access, etc.) or just directly store them as Strings. But you will lose a lot of flexibility. – SJuan76 Sep 24 '12 at 20:11
  • @SJuan76, If you post your comment with three upvotes, I'll accept it – RabtFt Sep 24 '12 at 21:10
  • Done, (filling so I can post). – SJuan76 Sep 24 '12 at 21:29