我们正在使用JavaMail API发送日历条目。但Outlook的收件人有时区问题,因为会议显示错误的时间。一般来说,我们的方法如下:
首先我们有, SimpleDateFormat iCalendarDateFormat = new SimpleDateFormat(" yyyyMMdd' T' HHmmss");
然后我们使用iCalendarDateFormat.setTimeZone(TimeZone.getTimeZone(receiverTimeZone));
最后,我们使用Calendar.getInstance()来开始和结束操作Calendar字段, 因此我们有Date startDate = startTime.getTime(); 日期endDate = endTime.getTime();
当我们要根据icalendar规范发送请求时,我们有 " DTSTAMP:" + iCalendarDateFormat.format(startDate)+" \ n" + " DTSTART:" + iCalendarDateFormat.format(startDate)+" \ n" " DTEND:" + iCalendarDateFormat.format(endDate)+" \ n"
这是正确的做法吗?请评论。
由于
答案 0 :(得分:1)
iCalendar格式与其预期时区分开跟踪日期时间。您必须适当地处理两个部分。
始终使用 java.time 类。切勿使用Calendar
和SimpleDateFormat
之类的旧类。
注意:我之前没有使用过 iCalendar 数据。因此,我的理解可能不正确。
从pages 31-33 of the RFC 5545 spec看,该规范的作者似乎假设您始终希望将日期时间与时区分开记录。
时刻,即时间轴上的一点,需要时区或UTC偏移量的上下文。例如,“明年2021年1月23日中午”不是一个时刻。我们不知道您是指日本东京的中午,法国图卢兹的中午还是美国俄亥俄州的托莱多中午-都是非常不同的时刻,相隔几个小时。
要提供偏移量的上下文,日期和时间必须带有若干小时-分钟-秒,例如08:00
。对于零时分秒,请使用+00:00
。
2021-01-23T12:00:00 + 00:00
作为零位偏移量+00:00
的缩写,可以使用字母Z
,发音为“ Zulu”。例如:
2021-01-23T12:00:00Z
但是,奇怪的是,iCalendar规范想要跟踪与时区分开的日期和时间。所以这个:
2021-01-23T12:00:00
...以及其他地方的时区字段:
美国/纽约
并且iCalendar规范选择了ISO 8601允许的难以理解的“基本”变体,从而最大程度地减少了定界符的使用。所以这个:
20210123T120000
对于这样的字符串,我们必须将其解析为LocalDateTime
。此类表示带有日期的日期,但缺少任何时区或UTC偏移量。
DateTimeFormatter f = dateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmss" ) ;
String input = "20210123T120000" ; // “Basic” variation of ISO 8601 format.
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
要确定时刻,我们必须应用时区。我假设iCalendar使用proper time zone names(Continent/Region
格式),而不是2-4个字母的伪区域,例如PST
,CST
,IST
等。
String zoneName = receiverTimeZone ; // Variable name taken from your code example, though you neglected to show its origins.
ZoneId z = ZoneId.of( zoneName ) ;
应用区域以在时间轴上获得一个ZonedDateTime
,一刻,一个点。
ZonedDateTime zdt = ldt.atZone( z ) ;
往另一个方向,让我们从当前时刻开始。
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
并为iCalendar生成字符串值。
DateTimeFormatter f = dateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmss" ) ;
String iCal_DateTime = now.format( f ) ;
String iCal_ZoneName = now.getZone().toString() ;
请不要使用与Java最早的版本捆绑在一起的可怕的旧式日期时间类:Calendar
,GregorianCalendar
,java.util.Date
,SimpleDateFormat
,依此类推。几年前,这些已被JSR 310中定义的现代 java.time 类取代。
答案 1 :(得分:0)
很难说没有看到iCalendar文件的实际内容,以及带有时区信息的预期开始和结束日期时间,但您似乎在浮动时间(具有本地时间的日期时间)生成DTSTART。虽然您的代码示例似乎暗示您可以访问收件人的时区(receiverTimezone),但这是一种非常脆弱的方法。
相反,您应该使用具有UTC时间的日期时间或具有本地时间和时区的日期时间(其中时区不必是接收器时区)。 如果事件不再发生,最简单的方法是使用UTC时间的日期时间。
有关每种格式的定义,请参阅https://tools.ietf.org/html/rfc5545#section-3.3.5。
答案 2 :(得分:0)
我遇到了同样的问题,为此我苦苦挣扎。因此,下面是我的发现:
Outlook在UTC
时区可以正常工作。如果我们使用UTC
时区设置日期和时间,则Outlook会自动将此UTC
时间转换为用户对应的Timezone
。我们将不得不为DTSTART:, DTEND:
和DTSTAMP
使用“即时”对象(可选,但建议使用)。
快速测试只需在ic字符串中使用"DTSTART:"+Instant.now()
。
在Java 8中用于获取UTC
时间Java time API提供了Instant.now()
,通过它您可以以UTC
格式获取系统时间。 Java 8还提供了类似的方法
a.
Instant.ofEpochMilli()
-返回Instant,可以直接在ical Sting中使用。
b.
new Date().toInstant()
返回UTC
即时对象。
在几种情况下,输入日期和时间源是不同的:
如果要从数据库中获取日期和时间,则在这种情况下,数据库不存储时区,而是唯一保存的日期和时间。因此,首先要在保存数据库的那个时区中转换日期和时间,在我的情况下,我是在“ EST”时区中转换后存储日期和时间,而日期值为EST
,但时区不存在在数据库中。因此,在从数据库中获取日期和时间值时,我在日期值中附加了时区,然后使用以下方法进一步转换为EPOC
时间
public static long getEpocTimeWithTimezone(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter dateTimePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(simpleDateFormat.format(date), dateTimePattern);
long epochInMilliSeconds = dateTime.atZone(ZoneId.of("America/New_York")).toEpochSecond() * 1000;
return epochInMilliSeconds;
}
然后仅对ical String使用以下代码:
Instant startDt = Instant.ofEpochMilli(getEpocTimeWithTimezone(//pass your date here
)).truncatedTo(ChronoUnit.MINUTES);
现在将此即时对象(startDt)直接设置为"DTSTART:"
:
"DTSTART:"+startDt+"....then in same fashion "DTEND:" also.
在第二种情况下,您具有带时区的日期(请确保在转换后您不会丢失实际的时区,就像在第一种情况下,将Date保存在数据库中后,我们实际上丢失了时区,但显示的是时区IST
那是假的,所以要小心点)
因此,在这种情况下,只需假设myDateObject
是Date对象。因此,只需获取Instant
(
通过使用myDateObject
类的toInstant()
,将Date
中的UTC对象放入。
Instant startDt = myDateObject.toInstant().truncatedTo(ChronoUnit.MINUTES);
我正在使用.truncatedTo(ChronoUnit.MINUTES);
,因为如果我们不使用它,那么
我们可能在“会议邀请时间”部分获得了额外的分钟或秒。
因此,Outlook邮件的最终字符串应类似于:
.
.
.
"BEGIN:VEVENT\n"+
"DTSTART:"+startDt+"\n"+
"DTEND:"+endDt+"\n"+
.
.
.
VVI Note:
由于Z
是UTC时区的表示,因此仅在最后一个时间添加Z
不会被UTC
划分为时间,您必须进行转换日期和时间,那么Outlook只会显示准确的时间。要验证您的时间是否为UTC格式,只需将.ics附加文件(您从电子邮件中获取)保存在本地,然后检查日期和时间是否为DTSTART:2020-05-15T13:57:00Z
,如果不是,则不转换日期正确地在UTC
中。