I believe there should be a lib to check this. I would like to know if there is one for java or javascript.
For example, if the timezone is in America/Los Angeles, 2016/03/13/02:20am would not be a valid datetime due to the daylight saving hour.
I believe there should be a lib to check this. I would like to know if there is one for java or javascript.
For example, if the timezone is in America/Los Angeles, 2016/03/13/02:20am would not be a valid datetime due to the daylight saving hour.
Parse the String with a DateTimeFormatter
object to get a ZonedDateTime
. If the string cannot be parsed, an DateTimeParseException
is thrown. Trap for that exception.
You can obtain a localized formatter by calling one of the DateTimeFormatter.ofLocalized…
methods.
This has been discussed many times already on Stack Overflow. So search for code examples and more discussion.
Your example data is incorrect. The DST cut-over for America/Los_Angeles
was indeed 2016-03-13, but that is a “Spring-ahead” adjustment jumping from 2 AM to 3 AM. So the time-of-day of 01:20 AM is a non-issue. You want an example moment of 2016-03-13T02:20:00
for 2 AM hour. But even this is handled by java.time. The behavior of applying a time zone in this situation is to jump ahead an hour. Read the doc and make sure you understand its behavior.
String input = "2016-03-13T02:20:00" ;
LocalDateTime ldt = LocalDateTime.parse( input );
ZoneId z = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ldt.atZone( z );
System.out.println( "input: " + input + " | " + "zdt: " + zdt );
input: 2016-03-13T02:20:00 | zdt: 2016-03-13T03:20-07:00[America/Los_Angeles]
To change this behavior, use a ResolverStyle
with a DateTimeFormatter
. Already discussed on Stack Overflow; search for code examples and more info.
BIG TIP # 1: Do not exchange date-time data with localized strings. Use ISO 8601 formats. The standard was invented for this very purpose. The java.time classes use these formats by default when parsing/generating strings.
BIG TIP # 2: Stick with UTC values whenever possible, rather than zoned.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
The local timestamp "2016/03/13/01:20am" is indeed valid in Los Angeles, but one hour later we have a gap between standard time and summer time:
String input = "2016/03/13/02:20am";
ZoneId pst = ZoneId.of("America/Los_Angeles");
DateTimeFormatter fmtr =
new DateTimeFormatterBuilder()
.appendPattern("uuuu/MM/dd/hh:mm")
.parseCaseInsensitive()
.appendText(ChronoField.AMPM_OF_DAY)
.toFormatter()
.withLocale(Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(input, fmtr);
System.out.println(ldt); // 2016-03-13T02:20
System.out.println(ldt.atZone(pst)); // 2016-03-13T03:20-07:00[America/Los_Angeles]
System.out.println(
"Valid local timestamp: " + !pst.getRules().getValidOffsets(ldt).isEmpty()); // false
So we see that the new java.time
-package built into Java-8 offers an API how to determine the validitiy of local timestamps in any timezone. It should also be noted that the solution presented does NOT need any way of exception catching.
When converting invalid local timestamps to an instant/moment then only one fixed transition strategy is used, namely to push forward the local timestamp by the size of the gap between winter and summer time, see example above for conversion to an object of type ZonedDateTime
.
Side note: If people are not on Java-8 or if they look for a slightly more elegant alternative then using my library Time4J might be an alternative:
String input = "2016/03/13/02:20am";
PlainTimestamp tsp =
ChronoFormatter.ofTimestampPattern(
"uuuu/MM/dd/hh:mma",
PatternType.CLDR,
Locale.ENGLISH
).parse(input);
System.out.println(tsp); // 2016-03-13T02:20
System.out.println("Valid local timestamp: " + tsp.isValid(AMERICA.LOS_ANGELES)); // false
When converting local timestamps to an instant/moment then the transition strategy is configurable (in contrast to standard Java):
// simulation of JDK-behaviour: 2016-03-13T10:20:00Z (= 2016-03-13T03:20:00-07:00)
System.out.println(tsp.in(Timezone.of(AMERICA.LOS_ANGELES)));
// jump to next valid time: 2016-03-13T10:00:00Z (= 2016-03-13T03:00:00-07:00)
System.out.println(
tsp.in(
Timezone.of(AMERICA.LOS_ANGELES).with(
GapResolver.NEXT_VALID_TIME.and(OverlapResolver.EARLIER_OFFSET)
)
)
);
// throws an exception for invalid timestamp (similar to Joda-Time-library)
// => Invalid local timestamp due to timezone transition:
// => local-date=2016-03-13, local-time=T02:20 [LOS_ANGELES]
tsp.in(
Timezone.of(AMERICA.LOS_ANGELES).with(
GapResolver.ABORT.and(OverlapResolver.EARLIER_OFFSET)
)
);
Foot note: As mentioned above, the popular library Joda-Time always throws an exception if the conversion of an invalid LocalDateTime
to a DateTime
is tried. This effect can be used for determining if the local date-time is valid or not (by exception catching).
I am not an expert for JavaScript, but I have found via Google this function in the library MomentJS. Citation:
Is DST Shifted From 2.3.0, Deprecated 2.14.0
moment('2013-03-10 2:30', 'YYYY-MM-DD HH:mm').isDSTShifted()
Note: As of version 2.14.0 this function is deprecated. It doesn't give the right answer after modifying the moment object. For more information refert to moment/3160
Another important piece of validation is to know if the date has been moved by a DST. For example, in most of the United States: moment('2013-03-10 2:30', 'YYYY-MM-DD HH:mm').format(); //=> '2013-03-10T01:30:00-05:00'
This is because daylight saving time shifts the time from 2:00 to 3:00, so 2:30 isn't a real time. The resulting time is browser-dependent, either adjusting the time forward or backwards. Use moment#isDSTShifted to test for this condition.
Note: before 2.3.0, Moment objects in this condition always returned false for moment#isValid; they now return true.
JavaScript-experts may find better solutions and are kindly asked to present here.
Just to add an answer for JavaScript - Meno correctly picked up on Moment.js. I'll add some detail.
Unfortunately we can't simply ask moment if a date/time falls into the gap, because the JS Date
object under the hood does some funky things with shifting that is not in the spec and is inconsistent across environments. Moment used to have a function isDSTShifted
, but it is deprecated because of this. Ultimately, one really needs the original input as a basis of comparison to tell if the time has been shifted, and since one could have manipulated a moment (add, subtract, set, etc.) we don't have a way to provide a better implementation.
BUT - since you do have specific input, you can do this yourself by comparing the output to the input under the same format and see if it's still the same or not.
var s = "2016/03/13/02:20am";
var f = "YYYY/MM/DD/hh:mma";
var shifted = moment(s, f).format(f) !== s;
If you want to use a named time zone then you will also need to use the moment-timezone addon.
var s = "2016/03/13/02:20am";
var f = "YYYY/MM/DD/hh:mma"
var z = "America/Los_Angeles";
var shifted = moment.tz(s, f, z).format(f) !== s;