1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.mapclient; 18 19 import java.util.Calendar; 20 import java.util.Date; 21 import java.util.Locale; 22 import java.util.TimeZone; 23 import java.util.regex.Matcher; 24 import java.util.regex.Pattern; 25 26 public final class ObexTime { 27 28 private Date mDate; 29 ObexTime(String time)30 public ObexTime(String time) { 31 /* 32 * Match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset +/-hhmm 33 * 34 * Matched groups are numberes as follows: 35 * 36 * YYYY MM DD T HH MM SS + hh mm 37 * ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^ 38 * 1 2 3 4 5 6 8 9 10 39 * |---7---| 40 * 41 * All groups are guaranteed to be numeric so conversion will always succeed (except group 8 42 * which is either + or -) 43 */ 44 Pattern p = Pattern.compile( 45 "(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(([+-])(\\d{2})(\\d{2})" + ")?"); 46 Matcher m = p.matcher(time); 47 48 if (m.matches()) { 49 50 /* 51 * MAP spec says to default to "Local Time basis" for a message listing timestamp. We'll 52 * use the system default timezone and assume it knows best what our local timezone is. 53 * The builder defaults to the default locale and timezone if none is provided. 54 */ 55 Calendar.Builder builder = new Calendar.Builder(); 56 57 /* Note that Calendar months are zero-based */ 58 builder.setDate(Integer.parseInt(m.group(1)), /* year */ 59 Integer.parseInt(m.group(2)) - 1, /* month */ 60 Integer.parseInt(m.group(3))); /* day of month */ 61 62 /* Note the MAP timestamp doesn't have milliseconds and we're explicitly setting to 0 */ 63 builder.setTimeOfDay(Integer.parseInt(m.group(4)), /* hours */ 64 Integer.parseInt(m.group(5)), /* minutes */ 65 Integer.parseInt(m.group(6)), /* seconds */ 66 0); /* milliseconds */ 67 68 /* 69 * If 7th group is matched then we're no longer using "Local Time basis" and instead 70 * have a UTC based timestamp and offset information included 71 */ 72 if (m.group(7) != null) { 73 int ohh = Integer.parseInt(m.group(9)); 74 int omm = Integer.parseInt(m.group(10)); 75 76 /* time zone offset is specified in miliseconds */ 77 int offset = (ohh * 60 + omm) * 60 * 1000; 78 79 if (m.group(8).equals("-")) { 80 offset = -offset; 81 } 82 83 TimeZone tz = TimeZone.getTimeZone("UTC"); 84 tz.setRawOffset(offset); 85 86 builder.setTimeZone(tz); 87 } 88 89 mDate = builder.build().getTime(); 90 } 91 } 92 ObexTime(Date date)93 public ObexTime(Date date) { 94 mDate = date; 95 } 96 getTime()97 public Date getTime() { 98 return mDate; 99 } 100 101 @Override toString()102 public String toString() { 103 if (mDate == null) { 104 return null; 105 } 106 107 Calendar cal = Calendar.getInstance(); 108 cal.setTime(mDate); 109 110 /* note that months are numbered stating from 0 */ 111 return String.format(Locale.US, "%04d%02d%02dT%02d%02d%02d", cal.get(Calendar.YEAR), 112 cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE), cal.get(Calendar.HOUR_OF_DAY), 113 cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)); 114 } 115 } 116