3

I want to print the current date in this format 2017/06/05 > Year/Month/Day and next to it, the current timezone in this format +3

I used this code

String DateToday  = DateFormat.getDateInstance().format(new Date());
String TZtoday = DateFormat.getTimeInstance().getTimeZone().getDisplayName();
txt.setText(DateToday + " | "+ TZtoday );

But, it shows like this:

Jun 5, 2017 | Arabia Standard Time

I want it like this:

2017/06/05 | +3

Alaa AbuZarifa
  • 1,171
  • 20
  • 39
  • 1
    What do you want to happen if the offset is not a whole number of hours? – Ole V.V. Jun 05 '17 at 10:12
  • @OleV.V. not getting what mean by that? – Alaa AbuZarifa Jun 05 '17 at 10:34
  • 1
    For example, say that the zone offset for “Asia/Kathmandu” is +05:45. How should this be printed? If the answer is “don’t care”, I suggest you throw an exception in this case. – Ole V.V. Jun 05 '17 at 10:41
  • hmm, you are right I think..! – Alaa AbuZarifa Jun 05 '17 at 11:43
  • Practically speaking, I suggest always printing your offset-from-UTC with both hours and minutes, and also the optional colon in between. So, `+03:00` rather than `+3`. That seems to be the canonical formatting. I have seen multiple libraries and protocols that expect only that format. Also, that format is more recognizable to the human reader. – Basil Bourque Jun 05 '17 at 14:32
  • @BasilBourque I used on this format because I'm using API that requires this type of format.! – Alaa AbuZarifa Jun 06 '17 at 07:29
  • @SamZar But does your API also support the full style of `±HH:MM`? Besides my practical advice in comment above, another reason to use the full format is because that is the default used by the `ZoneOffset` class in java.time. – Basil Bourque Jun 06 '17 at 07:59
  • the api I'm using, takes the current date and the time zone in the format I requested in the question, I know it's not the best format, but I had to comply with it.! – Alaa AbuZarifa Jun 06 '17 at 09:21

4 Answers4

3
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd | X");
System.out.println(sdf.format(new Date()));

gets close, but the time zone is printed with a leading zero:

2017/06/05 | +03

I suppose you could remove leading zeros from the time zone, if you need to:

SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat zone = new SimpleDateFormat("ZZZZZ"); // = +03:00
String tz = zone.format(new Date()).split(":")[0]
    .replaceAll("^(\\+|-)0", "$1"); // = +3
System.out.println(sdf.format(new Date()) + " | " + tz);

which gives:

2017/06/05 | +3
David Conrad
  • 15,432
  • 2
  • 42
  • 54
  • the date showed right :D , but only when I remove the X inside the format because it crashes the app with error `java.lang.IllegalArgumentException: Unknown pattern character 'X'` – Alaa AbuZarifa Jun 05 '17 at 07:34
  • @Sam You're using `java.text.SimpleDateFormat`? Maybe `X` was added in Java 8. Nope, I just checked, it was in Java 7, too. – David Conrad Jun 05 '17 at 07:36
  • @Sam It's also supported on Android, according to the [Android documentation](https://developer.android.com/reference/java/text/SimpleDateFormat.html). What are you using? – David Conrad Jun 05 '17 at 07:40
  • I'm using android, but I did some digging, `X` can be replaced with `ZZZZZ` but it shows like this `+03:00` – Alaa AbuZarifa Jun 05 '17 at 07:47
  • @Sam Right, because `ZZZZZ` does something different than `X` – David Conrad Jun 05 '17 at 07:49
  • @Sam I mean, the other possibility if you really can't use `X` is to split that time zone on the `:` and then remove the leading zero from the first half... – David Conrad Jun 05 '17 at 07:50
  • 1
    I did that, and it works fine now.. I'll edit your answer with the changes I made to the result I want.. thanks a lot man for the help. – Alaa AbuZarifa Jun 05 '17 at 08:04
  • 2
    @Sam You don't want to use StringTokenizer for anything, it's old and broken, just use `String.split()`, and you shouldn't use `replaceAll("0", "")` because the time zone might be `+10` or `-10` -- you need to make sure you're only replacing a ***leading*** zero. – David Conrad Jun 05 '17 at 08:20
  • oh, I see.. ok thanx for the tip – Alaa AbuZarifa Jun 05 '17 at 11:43
3

I know you are on Android, and I know that what is offered built-in on Android are the long outdated classes like DateFormat, SimpleDateFormat, Date and Calendar. Still I say, if you are doing something with dates and/or times and/or time zones, you should seriously consider skipping these classes and moving on to their modern replacements. This will require you to get ThreeTenABP. Then I suggest this way:

    ZonedDateTime date = ZonedDateTime.now(ZoneId.systemDefault());
    String tzToday = date.format(DateTimeFormatter.ofPattern("uuuu/MM/dd | x"));
    tzToday = tzToday.replaceFirst("(?<=\\+|-)0", "");

Result on my computer:

2017/06/05 | +2

Result in Asia/Kathmandu time zone may not be exactly what you wanted:

2017/06/05 | +545

You may think my code snippet isn’t very advantageous over using the old classes. You may take my word when I say that there are so many cases where the old classes give you negative surprises, so the sooner you can start using the new ones, the better. Or you may make a typo in the format pattern string, for example, and notice how you get a surprising result with the old classes with no sign that anything is wrong, while the newer ones will attempt to put together a meaningful error message.

Another detail to note is I am reading the system clock and JVM’s time zone only once (passing the time zone back to now() to make sure it is used for the time). Your code in the question formats the date first, then reads the time zone. If someone changes the time zone between the two, the result will be inconsistent. Very unlikely, but this also means it will be very unlikely anyone will be able to figure out what happened.

The (?<=\\+|-) in the regular expression is a lookbehind: I am replacing a 0 preceeded by either + or - with the empty string. I searched in vain for a way to format the zone offset like +3 as you desired, so I resorted to the replaceFirst method.

Further link: How to use ThreeTenABP in Android Project.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 2
    well, I've been an java developer before moving to Android, so I'll make sure my next implementation of Times/Dates will be with ThreeTenABP, thanks a lot of the advice.! – Alaa AbuZarifa Jun 06 '17 at 07:47
1

Just complementing @Ole V.V.'s answer, I've found another way of doing it using ThreeTenABP, but without needing a regex replacement (although I don't think it's much simpler, more on that below).

Using the DateTimeFormatterBuilder you can use a HashMap that maps the values of the offsets to a String. So, for whole offset hours (like +03:00), you can map the respective value to the string +3 and so on.

The only problem is that the ZoneOffset has seconds precision. And the minimum and maximum values are, respectively, UTC-18:00:00 and UTC+18:00:00. So all possible values are UTC-18:00:00, UTC-17:59:59, UTC-17:59:58 and so on. And the formatter requires that all possible values are mapped (otherwise it'll display the offset's seconds value), so the map will have more than 120K entries!

To build this map, I've made 2 loops:

  • The first loop maps the whole hour offsets (+01:00 to +1, -02:00 to -2 and so on)
  • The second loop maps all the other values (they remain unchanged):
    • whole hours >= 10 (like +10:00)
    • not-whole hours (like +05:30)

The code to create the formatter:

// builds a hashmap to map all offset values
Map<Long, String> map = new HashMap<>();

// First loop: Map whole hours from 1 to 9 and from -9 to -1
// So a "+01:00" offset is displayed as "+1"
for (int i = 1; i <= 9; i++) {
    long seconds = ZoneOffset.ofHours(i).getTotalSeconds();
    // 1 hour offset maps to "+1" and so on
    map.put(seconds, "+" + i);
    // -1 hour offset maps to "-1" and so on
    map.put(-seconds, "-" + i);
}

// second loop: Need to map all other possible values for not-whole hours
// offsets like "+10:00" and "+01:30" are not changed
int minOffset = ZoneOffset.MIN.getTotalSeconds();
int maxOffset = ZoneOffset.MAX.getTotalSeconds();
for (long i = minOffset; i <= maxOffset; i++) {
    // the map already contains whole hours, don't need to overwrite the values
    if (!map.containsKey(i)) {
        // uses default String representation (like "+05:30")
        map.put(i, ZoneOffset.ofTotalSeconds((int) i).getId());
    }
}

// create the formatter
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                // year/month/day and the "|"
                .appendPattern("uuuu/MM/dd | ")
                // use the map of custom values (offset will use the values in the map)
                .appendText(ChronoField.OFFSET_SECONDS, map)
                // create formatter
                .toFormatter();

Some tests:

LocalDateTime dt = LocalDateTime.of(2017, 5, 1, 10, 0);
ZonedDateTime z = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo")); // UTC-03:00
System.out.println(formatter.format(z)); // 2017/05/01 | -3

// just picking some timezone in Arabia Stardard Time
// (there are more than one timezone in AST: https://en.wikipedia.org/wiki/UTC%2B03:00#Arabia_Standard_Time)
// I don't know in which one you are
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Qatar")); // UTC+03:00
System.out.println(formatter.format(z)); // 2017/05/01 | +3

// 2-digit offset
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Vladivostok")); // UTC+10:00
System.out.println(formatter.format(z)); // 2017/05/01 | +10:00

// not whole hour
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Kathmandu")); // UTC+05:45
System.out.println(formatter.format(z)); // 2017/05/01 | +05:45

The output is:

2017/05/01 | -3
2017/05/01 | +3
2017/05/01 | +10:00
2017/05/01 | +05:45


Notes:

  • I don't know if having a 120K-entries map is better than using regular expressions (it's up to you to decide). This approach creates a big map, but at least doesn't require output post-processing (not sure if that's a reasonable trade-off)
  • If you want whole-hour offsets >= 10 to be displayed as +10, +11 and so on, just change the first for loop to for (int i = 1; i <= maxOffsetYouWant; i++) - just reminding that the maximum possible value for maxOffsetYouWant is 18.
  • 1
    wow, that's a really interesting approach to solve the problem, thanks for that, although it's more complicated for me to understand fully. so for now I'll stick for the old java classes solution since my audience for the app won't have more than +3 timezone..! but thanks again for the help – Alaa AbuZarifa Jun 06 '17 at 07:51
  • Indeed, I must admit that the solution for this specific case is more complicated than I wish. But apart from that, you should really spend some time learning the new API. It's much better than `Date` and `Calendar` and it's really worth learning it. –  Jun 06 '17 at 11:48
  • 1
    I'll for sure ;) – Alaa AbuZarifa Jun 06 '17 at 12:29
0

USe the following logic to get the desired date format.

    public static void main(String[] args) {

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, 1);
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(cal.getTime());
// Output "Wed Sep 26 14:23:28 EST 2012"

        String formatted = format1.format(cal.getTime());
        String formattedmain=formatted.replace("-","/");
        System.out.println(formatted);
        // Output "2012-09-26"
        System.out.println(formattedmain);

//Output :- 2017/06/06


    }
Amit Gujarathi
  • 1,090
  • 1
  • 12
  • 25