Artigo em boa hora, estava esta semana estudando sobre o java.time, isto sobre fuso-horários acabou me tomando boa parcela do meu tempo, mas acho que consegui compreender as nuances (não completamente, óbvio) da abordagem da linguagem Java para as datas, segue as minhas anotações e contribuições para complementar às suas de Javascript:
- TemporalAcessor: Uma interface implementada por todas abaixo referente a Date-Time, permitindo tratar dados de forma genérica sem precisar converter numa ou outra específica para atender a algum parâmetro, etc.
- Temporal: Implementa TemporalAcessor e fornece os métodos básicos implado por todas abaixo referente a Date-Time
- LocalDate: Apenas as datas, dispensando as horas;
- LocalTime: Apenas o horário, dispensando as datas;
- LocalDateTime: Utiliza ambos, tanto a hora quanto a data. Mas não tem fuso-horário;
- OffsetDateTime: Utiliza ambos com fuso-horário, mas sem sensibilidade ou mudanças a longo prazo (horário de verão);
- ZonedDateTime: Utiliza ambos com fuso-horário sensível à mudanças como horário de verão, mas maior complexidade;
- ZoneOffset: Apenas o fuso-horário sem nome ou sensibilidade a mudanças sazonais (usado por OffsetDateTime);
- ZoneId: Um identificador de fuso-horário completo e todas as regras aplicadas a estes fusos por cada região;
- Instant: Um momento específico da linha do tempo em milisegundos (UTC+0), mas sem considerar datas;
- Clock: Similar ao System.currentTimeMillis(), mas permite importar relógios externos como API;
- ChronoField: Enumeração que implementa TemporalField;
- ChronoUnit: Enumeração que implementa TemporalUnit;
- Chronology: Implementa diferentes tipos de calendários junto do ChronoLocalDate (menos utilizado).
Observação: Enumeração, para quem não manja de Java é uma lista truncada de opções que DEVEM ser selecionadas.
O ideal para contornar a situação é o ZonedDateTime, pois nele você consegue passar não só o idioma, mas o local que a data será baseada, sensíveis à estas alterações. Entretanto, num sistema internacional o Instant é melhor, pois sempre estará junto ao UTC e consegue converter ZonedDateTime para o Instant. Entretanto, ele não é uma data (pode ser convertido para uma, no entanto), é um momento no tempo contado a partir do epoch (1 de janeiro de 1970, às 00:00), podendo ser tanto negativo quanto positivo.
Ou seja, na minha cabeça o ideal seria armazenar ZonedDateTime -> Converta para Instant -> Faz as operações -> Retorna a resposta convertendo de volta para ZonedDateTime (se algum veterano tiver experiência na utilização, feedback seria bom)
Por último, acho que discordo do seu ponto do "não há uma medida universal de datas", pois o UTC veio justamente para fazer isso, é como se fosse um contrato inerente e consensual da humanidade entre si para: "independente da sua opinião ou localização que você utiliza na sua casa, aqui usamos X", é como se fosse... "independente do seu comportamento na sua casa, o ideal é X!" e isso é o que define algo universal, de comum acordo entre as partes.
A dificuldade técnica apontada seria em nos adaptarmos à "casa" de todos, mas é muito mais fácil fazer a "casa" de todos se adaptarem a nós, na medida universal da coisa.