Skip to content

Latest commit

 

History

History
480 lines (425 loc) · 15.9 KB

File metadata and controls

480 lines (425 loc) · 15.9 KB

Formatação de Números e Datas

Objetivo
Format dates, numbers, and currency values for localization with the NumberFormat and DateFormat classes, including number and date format patterns.
-
Formatar datas, números e valores monetários para localização utilizando as classes NumberFormat e DateFormat, incluindo padrões de formato de número e data.

Ainda dentro do assunto de Localização e Internacionalização, é comum a necessidade de apresentar datas, números e valores monetários em diferentes formatos.

O exame de certificação compreende cinco classes principais de formatação:

  • NumberFormat → Formatação geral de números, valores monetários, percentuais e números inteiros com arredondamento, possivelmente baseados em Locale.

  • DecimalFormat → Formatação de números quando há necessidade de definições mais específicas ou personalizadas do formato.

  • DateFormat → Formatação de data e hora antes do Java 8 utilizando formatos predefinidos.

  • SimpleDateFormat → Formatação de data e hora antes do Java 8 utilizando formatos mais específicos ou personalizados.

  • DateTimeFormatter → Formatação de data e hora após o Java 8.

NumberFormat

  1. É possível obter uma instância de NumberFormat a partir de vários métodos estáticos, dependendo da necessidade.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_Instance.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_Instance.java[role=include]

    Lembre-se que, se não for informado o Locale, será utilizado o padrão. O ideal é sempre informar o Locale.

  2. O NumberFormat pode ser utilizado para transformar números em Strings.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_NumberToString.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_NumberToString.java[role=include]
    Saída no console
    pt_BR: 1.000,05
    en_US: 1,000.05
    fr_FR: 1 000,05

    Perceba que a representação do número muda de acordo com o Locale.

  3. O NumberFormat por ser utilizado para transformar Strings em números.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_StringToNumber.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_StringToNumber.java[role=include]
    Saída no console
    pt_BR: 1000.05
    en_US: 100005
    fr_FR: 1000.05

    Perceba que dependendo do Locale estamos representando um número diferente, e isso muda o resultado do parse.

  4. O NumberFormat pode ser utilizado para transformar Strings em valores monetários, e vice-versa.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_Currency.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_Currency.java[role=include]
    Saída no console
    pt_BR: R$ 1.000,05
    en_US: $1,000.05
    fr_FR: 1 000,05 €
    pt_BR: 1000.05
    Unparseable number: "R$ 1000,05"
    Unparseable number: "R$ 1000,05"

    Perceba que novamente o resultado muda de acordo com o Locale. Além disso, não é possível converter a representação da moeda brasileira com um Locale en_US ou fr_FR.

  5. O NumberFormat pode ser utilizado para transformar Strings em percentuais, e vice-versa.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent.java[role=include]
    Saída no console
    pt_BR: 90%
    en_US: 90%
    fr_FR: 90 %
    pt_BR: 0.8
    en_US: 0.8
    Unparseable number: "80%"

    Veja que, ao formatar, 100% é 1, logo 80% é 0,8. Além disso, no Locale fr_FR a representação 80% não é válida.

  6. O NumberFormat pode ficar complicado ao lidar com vírgulas.

    src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent2.java
    link:../../../src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent2.java[role=include]
    Saída no console
    pt_BR: 0.802
    en_US: 8.02

    No Locale pt_BR temos o resultado esperado. Porém, no Locale en_US o 80,2% se torna 802% pois a vírgula não é usada como separador de decimal.

DecimalFormat

Enquanto NumberFormat permite utilizar formatos predefinidos, DecimalFormat permite uma personalização maior. Um exemplo de formato para o DecimalFormat é ###,###.###.

  • # → preenche a posição com um número, ou omite se não houver nada.

  • 0 → preenche a posição com um número, ou 0 se não houver nada.

  • . → indica onde é a posição do separador decimal.

  • , → indica onde é a posição do separador de grupos.

  1. É possível obter uma instância de DecimalFormat utilizando o construtor.

    src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Instance.java
    link:../../../src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Instance.java[role=include]
    Saída no console
    ###,###.###: 12.345,67
    000,000.###: 012.345,67
    ###,###.000: 12.345,670
    000,000.000: 012.345,670
    ###.##: 12345,67
    000000.000: 012345,670
    ###: 12346

    Estou executando o código onde o Locale padrão é pt_BR, por isso a saída no console apresenta vírgulas para separar grupos e pontos para separar os decimais.

  2. Para utilizar um Locale específico é necessário instanciar um NumberFormat e fazer um cast para DecimalFormat.

    src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Locale.java
    link:../../../src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Locale.java[role=include]
    Saída no console
    12,345.67
  3. É possível colocar outros símbolos ou palavras no formato do DecimalFormat.

    src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Strings.java
    link:../../../src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Strings.java[role=include]
    Saída no console
    Número 12.345,67 formatado

DateFormat

Para formatar Data e Hora antes do Java 8, são utilizadas as classes de DateFormat e SimpleDateFormat. Essas classes trabalham em geral com a classe Date e fazem parte do pacote java.text, diferente de DateTimeFormatter que é do novo pacote java.time.format.

  1. Obter instâncias de DateFormat é muito parecido com NumberFormat.

  2. É possível definir o formato das instâncias como SHORT, MEDIUM, LONG ou FULL.

  3. É possível definir o Locale das instâncias.

    src/org/j6toj8/localization/formats/dateformat/DateFormat_Instance.java
    link:../../../src/org/j6toj8/localization/formats/dateformat/DateFormat_Instance.java[role=include]
    Saída no console
    08/09/2001
    22:46:40
    08/09/2001 22:46:40
    8 de Setembro de 2001 22h46min40s BRT
    September 8, 2001 10:46:40 PM BRT
  4. Também é possível transformar String em Date utilizando o método parse.

    src/org/j6toj8/localization/formats/dateformat/DateFormat_Parse.java
    link:../../../src/org/j6toj8/localization/formats/dateformat/DateFormat_Parse.java[role=include]
    Saída no console
    Sat Sep 08 00:00:00 BRT 2001
    Thu Jan 01 22:46:00 BRT 1970
    Sat Sep 08 22:46:00 BRT 2001
    Unparseable date: "08/09/2001"

    Perceba que ocorreu uma exceção ao tentar converter uma data utilizando um formatar que espera Data e Hora.

SimpleDateFormat

A classe SimpleDateFormat permite criar formatos mais personalizados para apresentar Data e Hora, como dd/MM/yyyy HH:mm:ss. A seguir, as letras mais importantes utilizadas na formatação para o exame:

  • y → Ano (2019, 19)

  • M → Mês (8, 08, Ago, Agosto)

  • d → Dia (06)

  • h → Hora em formato AM/PM

  • H → Hora em formato 24H

  • m → Minutos

  • s → Segundos

Em geral (existem exceções), quanto mais letras forem utilizadas, mais extenso é o formato apresentado. Por exemplo:

  • M → 8

  • MM → 08

  • MMM → Ago

  • MMMM → Agosto

  1. É possível criar formatos personalizados para formatar um Date utilizando o construtor de SimpleDateFormat.

    src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Instance.java
    link:../../../src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Instance.java[role=include]
    Saída no console
    08 09 01 - 22 46 40
    08 09 01
    22 46 40
  2. Também é possível criar Date a partir de String utilizando o método parse.

    src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Parse.java
    link:../../../src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Parse.java[role=include]
    Saída no console
    Sat Sep 08 22:46:40 BRT 2001
    Sat Sep 08 00:00:00 BRT 2001
    Thu Jan 01 22:46:40 BRT 1970
    Unparseable date: "22 46 40"

    Perceba a exceção ao tentar utilizar o formato incorreto.

DateTimeFormatter

O Java 8 traz a classe DateTimeFormatter que possui várias formas de formatar e transformar Data/Hora em String, e vice-versa.

  1. É possível obter instâncias predefinidas de DateTimeFormatter, que representam formatos ISO ou RFC.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Predefined.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Predefined.java[role=include]
    Saída no console
    2019-08-06
    11:40:00
    2019-08-06T11:40:00
    2019-218
    2019-W32-2
  2. É possível obter instâncias predefinidas de DateTimeFormatter, que representam formatos localizados, através da classe FormatStyle.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_FormatStyle.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_FormatStyle.java[role=include]
    Saída no console
    06/08/19 11:40
    06/08/2019 11:40:00
    06/08/19
    11:40

    O resultado depende de onde o código está sendo executado. Este código foi executado no Locale padrão pt_BR.

  3. É possível alterar o Locale utilizado pelo DateTimeFormatter.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Locale.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Locale.java[role=include]
    Saída no console
    8/6/19 11:40 AM
    Aug 6, 2019 11:40:00 AM
  4. É possível obter instâncias personalizadas de DateTimeFormatter.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Custom.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Custom.java[role=include]
    Saída no console
    06 08 19 - 11 40 00

    Para criar um DateTimeFormatter personalizado é necessário conhecer o que cada letra do formato representa. Volte na seção de SimpleDateFormat caso não se lembre.

  5. Não é possível formatar uma Data/Hora caso o objeto fornecido não tenha os campos necessários. Um exemplo seria tentar apresentar a Data e fornecer um LocalTime.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Error.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Error.java[role=include]
    Saída no console
    2019-08-06
    2019-08-06
    Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year
    	at java.time.LocalTime.get0(LocalTime.java:679)
    	at java.time.LocalTime.getLong(LocalTime.java:656)
    	at java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
    	at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    	at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2551)
    	at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2190)
    	at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
    	at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
    	at java.time.LocalTime.format(LocalTime.java:1413)
    	at org.j6toj8.localization.formats.datetimeformatter.DateTimeFormatter_Error.main(DateTimeFormatter_Error.java:18)
  6. O mesmo erro pode ocorrer ao utilizar um formato personalizado.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_ErrorCustom.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_ErrorCustom.java[role=include]
    Saída no console
    11 40 00
    11 40 00
    Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    	at java.time.LocalDate.get0(LocalDate.java:680)
    	at java.time.LocalDate.getLong(LocalDate.java:659)
    	at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    	at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2551)
    	at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2190)
    	at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
    	at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
    	at java.time.LocalDate.format(LocalDate.java:1691)
    	at org.j6toj8.localization.formats.datetimeformatter.DateTimeFormatter_ErrorCustom.main(DateTimeFormatter_ErrorCustom.java:20)

    Nesse caso é lançada exceção porque LocalDate não possui hora, minuto ou segundo.

  7. Também é possível fazer o oposto: converter uma String em uma classe de Data/Hora. Para isso existem os métodos parse.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Parse.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Parse.java[role=include]
    Saída no console
    2019-08-06T11:40
    2019-08-06T11:40
    2019-08-06T11:40
  8. Todos os métodos utilizados para format e parse também podem ser invocados diretamente na instância do DateTimeFormatter.

    src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Inverted.java
    link:../../../src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Inverted.java[role=include]
    Saída no console
    2019-08-06T11:40
    2019-08-06T11:40:00
    {},ISO resolved to 2019-08-06T11:40
    2019-08-06T11:40

    Porém, veja que ao utilizar o método parse diretamente no DateTimeFormatter, é necessário converter o resultado para um LocalDateTime.

Referências