Cálculo do dia lunar e fases da lua com Java 10

Dia 30 de março iniciou o período de Pessach (ou páscoa) judaica. O interessante é que as festas judaicas são calculadas de acordo com as fases da lua e dias lunares, baseado num calendário luni-solar (períodos da lua e sol). Aproveitei o período para aprender um pouco de Java 10 com o uso da declaração var, lambdas e a API de data e hora que foi introduzida no Java 8. Para isso criei um aplicativo que calcula as fases lunares.

A lua passa por todas as suas fases em um período com mais ou menos 29,5 dias, também chamado de lunação. Esse período é conhecido como mês sinódico. É diferente do período que ela circunda a terra (27,3 dias), que é conhecido como mês sideral. Essa diferença é referente ao movimento orbital da Terra em torno do sol.

Para calcular as fases da lua, a ideia é bem simples. É preciso conhecer uma data com lua nova. No exemplo utilizei dia 07 de janeiro de 1970. Como o ciclo é de 29,5 dias, é necessário calcular a diferença entre essas datas e o resto da divisão pelo período sinódico e temos o número de dias lunares que passou à partir de uma lua nova. Pois a cada 29,5 dias temos uma outra lua nova. Ou seja:

DiaLunar = (DiaVerificar - DataDaLuaNova) mod PeriodoSinótico

Para saber o tipo de luas, é só verificar a quantidade de dias do período sinótico (29,5 dias) e dividir pela quantidade de luas (8 no caso), que dá aproximadamente 3,7. Então a cada 3,7 dias temos um novo tipo de lua.

Criei um enum para as fases da lua para ficar melhor organizado o código:

    static enum MoonPhase {
        NEW_MOON, // Nova
        WAXING_CRESCENT, // Crescente
        FIRST_QUARTER, // Quarto crescente
        WAXING_GIBBOUS, // Crescente gibosa
        FULL_MOON, // Cheia
        WANING_GIBBOUS, // Minguante gibosa
        THIRD_QUARTER, // Quarto minguante
        WANING_CRESCENT; // Minguante
    }

Criei duas constantes para serem utilizadas posteriormente em outros códigos:

    public static final long SYNODIC_PERIOD = 2551442877L; //29.530588854 days

    public static final LocalDateTime KNOWN_NEW_MOON = LocalDate.of(1970, Month.JANUARY, 7).atStartOfDay();

A lua nova é início do mês para os judeus e é chamada de ראש חודש ou Rosh Chodesh, traduzido como cabeça do mês. Nesse dia se comemora a Festa da Lua Nova, 14 dias após esse dia é lua cheia, e no primeiro mês judaico (Nisan ou Abibe) é onde inicia o Pessach. Para se recuperar o dia lunar, que começa a contar à partir da primeira lua nova do mês, é necessário calcular a diferença entre uma lua nova conhecida e a data a ser verificada e ver o resto da divisão pelo período sinódico:

    public static int getLunarDay(LocalDateTime day) {
        var lunarDay = (int) Duration.ofMillis(Duration.between(KNOWN_NEW_MOON, day).toMillis() % SYNODIC_PERIOD).toDays();
        return lunarDay;
    }

Com isso, para saber a fase atual da lua é só dividir pela diferença de cada lua, que, como dito anteriormente, é de aproximadamente 3,7 dias:

    public static MoonPhase getMoonPhase(int lunarDay) {
        var phaseNum = (int)Math.floor(lunarDay/3.7);
        return MoonPhase.values()[phaseNum];
    }

E para usar os dados utilizei o método main para imprimir a data, o dia lunar e a fase da lua de agora até 1 mês para frente:

    public static void main(String[] args) {
        var now = LocalDateTime.now();
        var end = now.plusMonths(1);

        Stream.iterate(now, date -> date.plusDays(1))
            .limit(ChronoUnit.DAYS.between(now, end))
            .forEach(MoonUtils::printLunarAnalysis);
      
    }

    private static void printLunarAnalysis(LocalDateTime ldt) {
        var df = DateTimeFormatter
            .ofLocalizedDate(FormatStyle.SHORT)
            .withLocale(new Locale("pt", "br"));

        var lunarDay = getLunarDay(ldt);
        var moonPhase = getMoonPhase(lunarDay);
        System.out.println("Data: " + ldt.format(df));
        System.out.println("Dia lunar: " + lunarDay);
        System.out.println("Fase da lua (provável): " + moonPhase);
        System.out.println();
    }

Nas linhas 4 a 6 estou utilizando expressões lambda junto com a interação do Java 8 e method reference. A inferência da declaração var do Java 10, que está em todo código, também funciona adequadamente. Com isso, aprendemos utilizar alguns dos novos recursos do Java e, caso você necessite em alguma aplicação ou calendário, conseguimos descobrir qual fase da lua estamos.

Sobre: Thiago Galbiatti Vespa

Thiago Galbiatti Vespa é mestre em Ciências da Computação e Matemática Computacional pela USP e bacharel em Ciências da Computação pela UNESP. Coordenador de projetos do JavaNoroeste, membro do JCP (Java Community Process), consultor Oracle, arquiteto de software de empresas de médio e grande porte, palestrante de vários eventos e colaborador de projetos open source. Possui as certificações: Oracle Certified Master, Java EE 5 Enterprise Architect – Step 1, 2 and 3; Oracle WebCenter Portal 11g Certified Implementation Specialist; Oracle Service Oriented Architecture Infrastructure Implementation Certified Expert; Oracle Certified Professional, Java EE 5 Web Services Developer; Oracle Certified Expert, NetBeans Integrated Development Environment 6.1 Programmer; Oracle Certified Professional, Java Programmer; Oracle Certified Associate, Java SE 5/SE 6