2011-03-14 133 views
2

我目前正在開發一個應用程序,根據用戶的位置(經度和緯度,通常從GPS獲取)和時間顯示太陽系的行星。這些行星(據我所知,它不是我的原始代碼)依賴於UTC時間戳,因此如果用戶在格林威治標準時間(aka UTC),行星顯得很好。但是隨着用戶走遍世界各地,特別是中國和美國,這些行星出現在錯誤的地方(太陽是最明顯的一個 - 忽略它的星星)。我似乎得到的時間傳遞到星球位置計算不正確,我不知道爲什麼。Android時間,時區和GPS位置

我已經有各種版本,但似乎沒有工作到目前爲止,它幾乎不可能分辨出是否有效,直到我發送出去並收到一封電子郵件回來告訴我我錯了。我們認爲這可能是GSM/CDMA衝突,但似乎並非如此。

以下是原始代碼來創建與GMT時間日曆:

public static Calendar convertToGmt(Calendar cal) 
{ 
    Date date = cal.getTime(); 
    TimeZone tz = cal.getTimeZone(); 

    //log.debug("input calendar has date [" + date + "]"); 

    //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 
    long msFromEpochGmt = date.getTime(); 

    //gives you the current offset in ms from GMT at the current date 
    int offsetFromUTC = tz.getOffset(msFromEpochGmt); 
    //log.debug("offset is " + offsetFromUTC); 

    //create a new calendar in GMT timezone, set to this date and add the offset 
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 
    gmtCal.setTime(date); 
    gmtCal.add(Calendar.MILLISECOND, offsetFromUTC); 

    //log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]"); 

    return gmtCal; 
} 

這後來改爲:

public static Calendar convertToGmt(Calendar cal) 
{ 
    Calendar gmtCal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); 
    long time = cal.getTimeInMillis(); 
    long offset = cal.getTimeZone().getRawOffset(); 
    gmtCal.setTimeInMillis(time - offset); 
    return gmtCal; 
} 

而最新的版本:

public static Calendar convertToGmt(Calendar cal) 
{  
    TimeZone timezone = TimeZone.getDefault(); 
    TimeZone utcTimeZone = TimeZone.getTimeZone("UTC"); 

    int currentGMTOffset = timezone.getOffset(cal.getTimeInMillis()); 
    int gmtOffset = utcTimeZone.getOffset(cal.getTimeInMillis()); 

    cal.setTimeInMillis(cal.getTimeInMillis() + (gmtOffset - currentGMTOffset)); 
    return cal; 
} 

在前兩個版本的Calendar實例正在傳回,而第三個版本(爲了優化它)mer ely更新它的一個靜態實例。今天早上修改我想也許使用System.currentTimeInMillis,即:

private static Calendar utc = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 
private static Calendar cal = Calendar.getInstance(); 
public static void convertToGmt() 
{   
    cal.setTimeInMillis(System.currentTimeMillis()); 
    utc.setTimeInMillis(cal.getTimeInMillis()); 
} 

但我不知道這有什麼區別。

我真的很迷茫 - 有人可以向我解釋我哪裏出錯或我該如何解決問題?我希望其他幾雙眼睛可能會有所幫助! :)

回答

0

爲什麼時區問題,如果應用程序接受經度和緯度?我的做法是僅使用日期,時間和經度/緯度來確定行星的位置。 java.util.Date在GMT內部存儲時間,因此手動轉換爲GMT似乎不必要。

+0

那麼在一個點上的代碼只是'日曆gmtCal =新GregorianCalendar的(TimeZone.getTimeZone( 「GMT」));返回gmtCal;'但我們仍然有用戶以錯誤的位置響應,並且由於日期/時間可以在電話設置中進行配置,所以我們假設時區差異會拋出返回的值。 – batterj2

0

嘗試使用谷歌時區API此代碼來獲取當前時間:

String get_xml_server_reponse(String server_url){ 

    URL xml_server = null; 

    String xmltext = ""; 

    InputStream input; 


    try { 
     xml_server = new URL(server_url); 


     try { 
      input = xml_server.openConnection().getInputStream(); 


      final BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 
      final StringBuilder sBuf = new StringBuilder(); 

      String line = null; 
      try { 
       while ((line = reader.readLine()) != null) 
       { 
        sBuf.append(line); 
       } 
       } 
      catch (IOException e) 
       { 
        Log.e(e.getMessage(), "XML parser, stream2string 1"); 
       } 
      finally { 
       try { 
        input.close(); 
        } 
       catch (IOException e) 
       { 
        Log.e(e.getMessage(), "XML parser, stream2string 2"); 
       } 
      } 

      xmltext = sBuf.toString(); 

     } catch (IOException e1) { 

       e1.printStackTrace(); 
      } 


     } catch (MalformedURLException e1) { 

      e1.printStackTrace(); 
     } 

    return xmltext; 

    }  


private String get_UTC_Datetime_from_timestamp(long timeStamp){ 

    try{ 

     Calendar cal = Calendar.getInstance(); 
     TimeZone tz = cal.getTimeZone(); 

     int tzt = tz.getOffset(System.currentTimeMillis()); 

     timeStamp -= tzt; 

     // DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault()); 
     DateFormat sdf = new SimpleDateFormat(); 
     Date netDate = (new Date(timeStamp)); 
     return sdf.format(netDate); 
    } 
    catch(Exception ex){ 
     return ""; 
    } 
    } 

class NTP_UTC_Time 
{ 
    private static final String TAG = "SntpClient"; 

    private static final int RECEIVE_TIME_OFFSET = 32; 
    private static final int TRANSMIT_TIME_OFFSET = 40; 
    private static final int NTP_PACKET_SIZE = 48; 

    private static final int NTP_PORT = 123; 
    private static final int NTP_MODE_CLIENT = 3; 
    private static final int NTP_VERSION = 3; 

    // Number of seconds between Jan 1, 1900 and Jan 1, 1970 
    // 70 years plus 17 leap days 
    private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L; 

    private long mNtpTime; 

    public boolean requestTime(String host, int timeout) { 
     try { 
      DatagramSocket socket = new DatagramSocket(); 
      socket.setSoTimeout(timeout); 
      InetAddress address = InetAddress.getByName(host); 
      byte[] buffer = new byte[NTP_PACKET_SIZE]; 
      DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT); 

      buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); 

      writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET); 

      socket.send(request); 

      // read the response 
      DatagramPacket response = new DatagramPacket(buffer, buffer.length); 
      socket.receive(response);   
      socket.close(); 

      mNtpTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);    
     } catch (Exception e) { 
      // if (Config.LOGD) Log.d(TAG, "request time failed: " + e); 
      return false; 
     } 

     return true; 
    } 


    public long getNtpTime() { 
     return mNtpTime; 
    } 


    /** 
     * Reads an unsigned 32 bit big endian number from the given offset in the buffer. 
     */ 
    private long read32(byte[] buffer, int offset) { 
     byte b0 = buffer[offset]; 
     byte b1 = buffer[offset+1]; 
     byte b2 = buffer[offset+2]; 
     byte b3 = buffer[offset+3]; 

     // convert signed bytes to unsigned values 
     int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0); 
     int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1); 
     int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2); 
     int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3); 

     return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3; 
    } 

    /** 
     * Reads the NTP time stamp at the given offset in the buffer and returns 
     * it as a system time (milliseconds since January 1, 1970). 
     */  
    private long readTimeStamp(byte[] buffer, int offset) { 
     long seconds = read32(buffer, offset); 
     long fraction = read32(buffer, offset + 4); 
     return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L)/0x100000000L);   
    } 

    /** 
     * Writes 0 as NTP starttime stamp in the buffer. --> Then NTP returns Time OFFSET since 1900 
     */  
    private void writeTimeStamp(byte[] buffer, int offset) {   
     int ofs = offset++; 

     for (int i=ofs;i<(ofs+8);i++) 
      buffer[i] = (byte)(0);    
    } 

} 

String get_time_zone_time(GeoPoint gp){ 

     String erg = ""; 
     String raw_offset = ""; 
     String dst_offset = ""; 

     double Longitude = gp.getLongitudeE6()/1E6; 
     double Latitude = gp.getLatitudeE6()/1E6; 

     // String request = "http://ws.geonames.org/timezone?lat="+Latitude+"&lng="+ Longitude+ "&style=full"; 


     long tsLong = 0; // System.currentTimeMillis()/1000; 

     NTP_UTC_Time client = new NTP_UTC_Time(); 

     if (client.requestTime("pool.ntp.org", 2000)) {    
      tsLong = client.getNtpTime(); 
     } 

     if (tsLong != 0) 
     { 

     tsLong = tsLong/1000; 

     // https://maps.googleapis.com/maps/api/timezone/xml?location=39.6034810,-119.6822510&timestamp=1331161200&sensor=true 

     String request = "https://maps.googleapis.com/maps/api/timezone/xml?location="+Latitude+","+ Longitude+ "&timestamp="+tsLong +"&sensor=true"; 

     String xmltext = get_xml_server_reponse(request); 

     if(xmltext.compareTo("")!= 0) 
     { 

     int startpos = xmltext.indexOf("<TimeZoneResponse"); 
     xmltext = xmltext.substring(startpos); 



     XmlPullParser parser; 
     try { 
      parser = XmlPullParserFactory.newInstance().newPullParser(); 


      parser.setInput(new StringReader (xmltext)); 

      int eventType = parser.getEventType(); 

      String tagName = ""; 


      while(eventType != XmlPullParser.END_DOCUMENT) { 
       switch(eventType) { 

        case XmlPullParser.START_TAG: 

          tagName = parser.getName(); 

         break; 


        case XmlPullParser.TEXT : 


         if (tagName.equalsIgnoreCase("raw_offset")) 
          if(raw_offset.compareTo("")== 0)        
          raw_offset = parser.getText(); 

         if (tagName.equalsIgnoreCase("dst_offset")) 
          if(dst_offset.compareTo("")== 0) 
          dst_offset = parser.getText(); 


         break; 

       } 

       try { 
         eventType = parser.next(); 
        } catch (IOException e) { 

         e.printStackTrace(); 
        } 

       } 

       } catch (XmlPullParserException e) { 

        e.printStackTrace(); 
        erg += e.toString(); 
       } 

     }  

     int ro = 0; 
     if(raw_offset.compareTo("")!= 0) 
     { 
      float rof = str_to_float(raw_offset); 
      ro = (int)rof; 
     } 

     int dof = 0; 
     if(dst_offset.compareTo("")!= 0) 
     { 
      float doff = str_to_float(dst_offset); 
      dof = (int)doff; 
     } 

     tsLong = (tsLong + ro + dof) * 1000; 



     erg = get_UTC_Datetime_from_timestamp(tsLong); 
     } 


    return erg; 

} 

而且隨着使用它:

GeoPoint gp = new GeoPoint(39.6034810,-119.6822510); 
String Current_TimeZone_Time = get_time_zone_time(gp);