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:

[code language=”java”] 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
}[/code]

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

[code language=”java”] public static final long SYNODIC_PERIOD = 2551442877L; //29.530588854 days

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

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:

[code language=”java”] public static int getLunarDay(LocalDateTime day) {
var lunarDay = (int) Duration.ofMillis(Duration.between(KNOWN_NEW_MOON, day).toMillis() % SYNODIC_PERIOD).toDays();
return lunarDay;
}[/code]

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:

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

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:

[code language=”java” highlight=”4,5,6″] 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();
}[/code]

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