Hey guys.. as an offshoot of another thread I have here, im trying to build some global awareness into an app..
I'm comparing local files with remote files on an ftp server. If the remote file is not present, or if the file size is different, or if the remote file is older, then the local file is uploaded.
Currently, all the local files are being uploaded because the ftp server is in a country 8 hours away, so every file appears 8 hours older than the local files.
The local file's age is referenced by the lastModified() method of java.io.File. The remote file's age is calculated after parsing the response from the ftp server. When the response comes in, part of it is extracted and SimpleDateFormatted into a Date, which is then stored in a small object designed to hold information about the remote file. the date is stored as a Date object.
How and when would I use timezones, in order to correct the 8 hour difference? here is the code for the factory that manufactures the meta-information objects (they hold a date, a size and a name only):
class FtpFileMetaFactory{
public static FtpFileMeta manufacture(String ftpResponse) throws InstantiationException{
//valid response?
if(bits.length<8){
throw new InstantiationException("The information supplied does not break down to at least 8 chunks!");
}
//date calculations - sometimes the response is "Jun 17 10:24" others it is "Sep 12 2003"
//if it's a time then year==this year, else year info is conveyed (no time)
String theDate;
if(bits[7].matches("[0-9]{1,2}:[0-9]{2}")){ //it is a time
theDate = bits[7]+","+bits[6]+","+bits[5]+","+new GregorianCalendar().get(GregorianCalendar.YEAR);
}else if(bits[7].matches("[12][0-9]{3}")){ //it is a year
theDate = "23:59,"+bits[6]+","+bits[5]+","+bits[7];
}else{
throw new InstantiationException("The value "+bits[7]+" (for the numeric argument) is not acceptable."+
"It must appear to be a year in YYYY format, or a time in hH:MM format");
}
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm,dd,MMM,yyyy");
//yank the name
String name = ftpResponse.substring(ftpResponse.lastIndexOf(bits[8]));
//assemble the metafile
try{
//attempting to set the timezone, parse the time, then move the zone
//but the time does not change...
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeZone(java.util.TimeZone.getTimeZone("GMT-08:00"));
gc.setTime(sdf.parse(theDate));
gc.setTimeZone(java.util.TimeZone.getTimeZone("GMT+00:00"));
//debug printlns
System.out.println("date is: "+theDate);
System.out.println("with TZ adjust is: "+gc.getTime());
return new FtpFileMeta(name, gc.getTime(), Integer.parseInt(bits[4]));
}catch(java.text.ParseException pe){
throw new InstantiationException("The date format provided, was not understood");
}
}
}
dlorde
June 22nd, 2004, 07:35 PM
Not all the useful stuff gets into the JavaDocs - sometimes it helps to look at the source comments to see what is going on. In the Calendar class it says:
" // Data flow in Calendar
// ---------------------
// The current time is represented in two ways by Calendar: as UTC
// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
// fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
// millis from the fields, and vice versa. The data needed to do this
// conversion is encapsulated by a TimeZone object owned by the Calendar.
// The data provided by the TimeZone object may also be overridden if the
// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
// keeps track of what information was most recently set by the caller, and
// uses that to compute any other information as needed.
// If the user sets the fields using set(), the data flow is as follows.
// This is implemented by the Calendar subclass's computeTime() method.
// During this process, certain fields may be ignored. The disambiguation
// algorithm for resolving which fields to pay attention to is described
// above.
// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
// |
// | Using Calendar-specific algorithm
// V
// local standard millis
// |
// | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
// V
// UTC millis (in time data member)
// If the user sets the UTC millis using setTime(), the data flow is as
// follows. This is implemented by the Calendar subclass's computeFields()
// method.
// UTC millis (in time data member)
// |
// | Using TimeZone getOffset()
// V
// local standard millis
// |
// | Using Calendar-specific algorithm
// V
// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
// In general, a round trip from fields, through local and UTC millis, and
// back out to fields is made when necessary. This is implemented by the
// complete() method. Resolving a partial set of fields into a UTC millis
// value allows all remaining fields to be generated from that value. If
// the Calendar is lenient, the fields are also renormalized to standard
// ranges when they are regenerated."
If you change the timezone, then get the individual fields, you can create a new Calendar with the converted values (and vice-versa). for example:Date date1 = new Date(); // current time
System.out.println("Date 1 = " + date1);
// Create calendar and set timezone
GregorianCalendar gc1 = new GregorianCalendar();
gc1.setTime(date1);
gc1.setTimeZone(java.util.TimeZone.getTimeZone("GMT-08:00"));
// Create new calendar from the fields offset by the new timezone
GregorianCalendar gc2 = new GregorianCalendar(gc1.get(GregorianCalendar.YEAR),
gc1.get(GregorianCalendar.MONTH),
gc1.get(GregorianCalendar.DAY_OF_MONTH),
gc1.get(GregorianCalendar.HOUR_OF_DAY),
gc1.get(GregorianCalendar.MINUTE));
Date date2 = gc2.getTime();
System.out.println("Date 2 = " + date2);
// On my PC, this outputs:
//
// Date 1 = Wed Jun 23 00:28:46 BST 2004
// Date 2 = Tue Jun 22 15:28:00 BST 2004
Suddenly he lost his footing on the slippery tiles and, scrabbling for grip, slid, with ever increasing speed, towards the edge of the roof...
cjard
June 23rd, 2004, 07:17 AM
Hmm. It appears then, that setting the timezone causes the internal fields of the calendar to be updated, but does not manipulate the Date object that the Calendar got its time from.. I would then surmise that calling getTime() returns you the object you passed in, rather than a new Date, remanufactured from the fields within the calendar.
Did the javadoc tout Calendar as a device for manipulating Dates? (i.e. Calendar is to Date what StringBuffer is to String)
because if it did.. then it told lies! :(
import java.util.*;
public class TimeZoneTest{
public static void main(String[] argv){
Date date1 = new Date(); // current time
// Create calendar and set timezone
GregorianCalendar gc1 =
new GregorianCalendar(java.util.TimeZone.getTimeZone("America/Los_Angeles"));
gc1.setTime(date1);
System.out.println("OLD TZ TIME = " + gc1.getTime());
note that the date doesnt change, even when the fields do.. rather disappointing. i added some code to getTimeInMillis() too, and that is not affected by timezones either.
did i miss the point of a calendar, or should this be changed?
cjard
June 23rd, 2004, 07:43 AM
out of curiousity, i used the add() method to manipulate the Calendar, and the Date provided by getTime() does indeed change as a result of this..
Does this mean that TimeZones will never change the time returned froma calendar, ind in this case, to affect the time properly, i should manipulate the hour myself, something like this:
interacting with an ftp server 8 hours away, i want to bring the remote file dates into local time
make a calendar, set the time to the remote time
store the zone offset and dst offset as a single summation, call it OFS
set the timezone to local
affect OFS by the summation of the new zone offset (subtract the new offset to the old offset)
because OFS represents the time difference from local to remote, and i want to bring remote up to local, * -1 to reverse the direction of the offset
add() OFS to the hour_of_day on the calendar
scenario:
remote file date 12:00 01 jun 2000
local file date: 20:00 01 jun 2000
in calendar: 12, 01, 06, 00, zone is -8, DST offset is +1
store -8+1 in OFS = -7
set timezone to local, zone is 0, DST is +1
OFS - (0+1) = -8
OFS * -1 = +8
claendar.add(hour of day, OFS)
then, gettime will return a time that the remote file was made, converted to local time.. which will be properly comparable with my local timing..
what a pain in the backside..
public class GregorianCalendar2 extends GregorianCalendar ...
dlorde
June 23rd, 2004, 07:46 AM
The date classes have always been a dog's breakfast, and reworking it all into calendars was a reasonable move, but poorly implemented - I think the committee must have been torn between two opposing factions and ended up with this crazy compromise where the core time in milliseconds is always UTC and the individual field values are offset to local time. It must have sounded reasonable at the time, but the implementation makes it a nightmare to use.
And lo, even time itself was confused, and the Calendar did cry out, saying :"I know not which time I should tell, thus shall I tell both UTC and local time, and so shalt thou also be heavy with doubt..."
The Book of Dlorde, Classes 8:17
cjard
June 23rd, 2004, 04:38 PM
I believe you:
static int ZONE_OFFSET
Field number for get and set indicating the raw offset from GMT in milliseconds.
(74) gc1.add(gc1.ZONE_OFFSET,8*60*60*1000);
(75) System.out.println("8 timezones later = " + gc1.getTime());
Exception in thread "main" java.lang.IllegalArgumentException
at java.util.GregorianCalendar.add(Unknown Source)
at TimeZoneTest.main(TimeZoneTest.java:74)
add(int field, int amount) ...
... Throws:
IllegalArgumentException - if an unknown field is given.
codeguru.com
Copyright WebMediaBrands Inc., All Rights Reserved.