Uploaded image for project: 'ZK'
  1. ZK
  2. ZK-5249

Transition from LMT to TimeZone

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 9.6.2
    • Fix Version/s: None
    • Component/s: Components
    • Labels:
    • Environment:
      • OpenJDK 1.8.0_345
      • Tomcat 9.0.62
      • ZK 9.6.2

      Description

      Steps to Reproduce

      Given following view model and zul

      public class OldDateVM {
      	private static String formatAtUTC(Date date) {
      		final SimpleDateFormat df = new SimpleDateFormat("yyyy.M.d.H.m.s.S", Locale.US);
      		df.setTimeZone(TimeZone.getTimeZone("UTC"));
      		return df.format(date);
      	}
      
      	private static String formatAtUTC(ZonedDateTime zonedDateTime) {
      		final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy.M.d.H.m.s.S");
      		zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("UTC"));
      		return df.format(zonedDateTime);
      	}
      
      	private static String formatLocal(Date date) {
      		final SimpleDateFormat df = new SimpleDateFormat("yyyy.M.d.H.m.s.S", Locale.US);
      		return df.format(date);
      	}
      
      	private static String formatLocal(ZonedDateTime zonedDateTime) {
      		final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy.M.d.H.m.s.S");
      		return df.format(zonedDateTime);
      	}
      
      	private String isoDate;
      	private ZonedDateTime zonedDateTime;
      	private Date date;
      
      	@DependsOn("isoDate")
      	public Date getDate() {
      		return date;
      	}
      
      	@DependsOn("date")
      	public String getDateLocal() {
      		return Optional.ofNullable(date).map(OldDateVM::formatLocal).orElse(null);
      	}
      
      	@DependsOn("date")
      	public Long getDateMilliseconds() {
      		return Optional.ofNullable(date).map(Date::getTime).orElse(null);
      	}
      
      	@DependsOn("date")
      	public String getDateUTC() {
      		return Optional.ofNullable(date).map(OldDateVM::formatAtUTC).orElse(null);
      	}
      
      	public String getIsoDate() {
      		return isoDate;
      	}
      
      	public String getJavaVendor() {
      		return System.getProperty("java.vendor");
      	}
      
      	public String getJavaVersion() {
      		return System.getProperty("java.version");
      	}
      
      	public String getTimezone() {
      		return System.getProperty("user.timezone");
      	}
      
      	@DependsOn("isoDate")
      	public ZonedDateTime getZonedDateTime() {
      		return zonedDateTime;
      	}
      
      	@DependsOn("zonedDateTime")
      	public String getZonedDateTimeLocal() {
      		return Optional.ofNullable(zonedDateTime).map(OldDateVM::formatLocal).orElse(null);
      	}
      
      	@DependsOn("zonedDateTime")
      	public Long getZonedDateTimeMilliseconds() {
      		return Optional.ofNullable(zonedDateTime).map(ZonedDateTime::toInstant).map(Instant::toEpochMilli).orElse(null);
      	}
      
      	@DependsOn("zonedDateTime")
      	public String getZonedDateTimeUTC() {
      		return Optional.ofNullable(zonedDateTime).map(OldDateVM::formatAtUTC).orElse(null);
      	}
      
      	public String getZoneId() {
      		return ZoneId.systemDefault().toString();
      	}
      
      	@Init
      	public void init() throws ParseException {
      		isoDate = "1893-11-01";
      		updateDates();
      	}
      
      	public void setDate(Date date) {
      		this.date = date;
      	}
      
      	public void setIsoDate(String isoDate) throws ParseException {
      		this.isoDate = isoDate;
      		updateDates();
      	}
      
      	public void setZonedDateTime(ZonedDateTime zonedDateTime) {
      		this.zonedDateTime = zonedDateTime;
      	}
      
      	private void updateDates() throws ParseException {
      		zonedDateTime = LocalDate.parse(isoDate).atStartOfDay(ZoneId.systemDefault());
      		date = new SimpleDateFormat("yyyy-MM-dd").parse(isoDate);
      	}
      }
      
      <window viewModel="@id('vm') @init('OldDateVM')">
      	<vlayout>
      		<grid hflex="min">
      			<columns>
      				<column label="Java Vendor" width="200px" />
      				<column label="Java Version" width="190px" />
      				<column label="User Time Zone" width="200px" />
      				<column label="Zone Id" width="200px" />
      			</columns>
      			<rows>
      				<row>
      					<label value="@load(vm.javaVendor)" />
      					<label value="@load(vm.javaVersion)" />
      					<label value="@load(vm.timezone)" />
      					<label value="@load(vm.zoneId)" />
      				</row>
      			</rows>
      		</grid>
      		<hlayout valign="middle">
      			<label value="ISO 8601 Date" />
      			<textbox value="@bind(vm.isoDate)"
      				constraint="/\d{4}-\d{2}-\d{2}/" />
      		</hlayout>
      		<grid hflex="min">
      			<columns>
      				<column label="Java class" width="210px" />
      				<column label="Milliseconds" width="160px" />
      				<column label="Local Date Time" width="200px" />
      				<column label="ZK JSON format" width="200px" />
      				<column label="DateBox (dd/MM/yyyy HH:mm:ss)"
      					width="270px" />
      			</columns>
      			<rows>
      				<row>
      					<label value="java.time.ZonedDateTime" />
      					<label value="@load(vm.zonedDateTimeMilliseconds)" />
      					<label value="@load(vm.zonedDateTimeLocal)" />
      					<label value="@load(vm.zonedDateTimeUTC)" />
      					<datebox
      						valueInZonedDateTime="@bind(vm.zonedDateTime)" cols="21"
      						format="dd/MM/yyyy HH:mm:ss" buttonVisible="true"
      						displayedTimeZones="Europe/Rome" />
      				</row>
      				<row>
      					<label value="java.util.Date" />
      					<label value="@load(vm.dateMilliseconds)" />
      					<label value="@load(vm.dateLocal)" />
      					<label value="@load(vm.dateUTC)" />
      					<datebox value="@bind(vm.date)" cols="21"
      						format="dd/MM/yyyy HH:mm:ss" buttonVisible="true"
      						displayedTimeZones="Europe/Rome" />
      				</row>
      			</rows>
      		</grid>
      	</vlayout>
      </window>
      
      1. Enter "1893-10-31" as ISO 8601 date

      Current Result

      • datebox using "valueInZonedDateTime" shows "31/10/1893 00:00:00"
      • datebox using "value" shows "30/10/1893 23:49:56"

      Expected Result

      Both datebox shall show  "31/10/1893 00:00:00".

      Debug Information

      When countries adopted TimeZone standard time adjustments were required.

      Italy, for example, adjusted its clock as "1893-10-31 23:49:55" (iso format) to "1893-11-01 00:00:00".

      Most countries did analogous adjustments.

      Client-side JS and java.time API support these transitions, while GregorianCalendar API does not. See JDK bugs:

      Since latest versions of ZK use JDK 1.8, (de)serialization in zcommon/src/org/zkoss/json/JSONs.java shall use java.time API.

      Conversion between java.time API and GregorianCalendar (java.util.Date) shall not use milliseconds. It shall extract fields (year, month ecc) from one API and use these fields to create an object in the other.

      Workaround

      none

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            benedetti benedetti
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Dates

              Created:
              Updated: