001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.utils; 019 020import io.stallion.Context; 021import io.stallion.exceptions.UsageException; 022import io.stallion.settings.Settings; 023import io.stallion.utils.json.JSON; 024import org.parboiled.common.StringUtils; 025 026import java.time.Instant; 027import java.time.LocalDateTime; 028import java.time.ZoneId; 029import java.time.ZonedDateTime; 030import java.time.format.DateTimeFormatter; 031import java.util.Date; 032 033import static io.stallion.utils.Literals.empty; 034import static io.stallion.utils.Literals.emptyInstance; 035 036 037public class DateUtils { 038 static ZoneId UTC = ZoneId.of("UTC"); 039 040 public static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("MMM d, YYYY h:mm a"); 041 public static final DateTimeFormatter SLUG_FORMAT = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmm-ssSS"); 042 public static final DateTimeFormatter ISO_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); 043 public static final DateTimeFormatter SQL_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 044 public static final DateTimeFormatter MINUTE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); 045 046 /** 047 * Gets the current time in UTC 048 * @return a ZonedDateTime with the current time in UTC 049 */ 050 public static ZonedDateTime utcNow() 051 { 052 return ZonedDateTime.now(UTC); 053 } 054 055 /** 056 * Milliseconds since the epoch 057 * @return 058 */ 059 public static long mils() { 060 return new Date().getTime(); 061 } 062 063 /** 064 * Converts milliseconds since the unix epoch to a ZonedDateTime in UTC 065 * @param mils 066 * @return 067 */ 068 public static ZonedDateTime milsToDateTime(long mils) { 069 return ZonedDateTime.ofInstant(Instant.ofEpochMilli(mils), UTC); 070 } 071 072 073 /** 074 * Formats a ZonedDateTime into a string with the given DateTimeFormatter pattern 075 * @param date 076 * @param formatPattern 077 * @return 078 */ 079 public static String formatLocalDateFromZonedDate(ZonedDateTime date, String formatPattern) { 080 if (date == null) { 081 return ""; 082 } 083 084 ZonedDateTime localDt = date.withZoneSameInstant(Context.getSettings().getTimeZoneId()); 085 086 DateTimeFormatter formatter; 087 if (StringUtils.isEmpty(formatPattern)) { 088 formatter = DEFAULT_FORMAT; 089 } else if ("iso".equals(formatPattern.toLowerCase())) { 090 formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; 091 } else { 092 formatter = DateTimeFormatter.ofPattern(formatPattern); 093 } 094 return localDt.format(formatter); 095 } 096 097 /** 098 * Turns the date into a format that has no spaces, colons, or any other invalid characters invalid for a file name 099 * The actual format used is: YYYY-MM-dd-HHmm-ssSS 100 * @param epochMillis 101 * @return 102 */ 103 public static String slugifyDate(Long epochMillis) { 104 return slugifyDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC)); 105 } 106 107 /** 108 * Turns the date into a format that has no spaces, colons, or any other invalid characters invalid for a file name 109 * The actual format used is: YYYY-MM-dd-HHmm-ssSS 110 * @param date 111 * @return 112 */ 113 public static String slugifyDate(ZonedDateTime date) { 114 return date.format(SLUG_FORMAT); 115 } 116 117 /** 118 * Gets the current time in the local time zone. The local time zone is first determined by the current user, if no 119 * user, then use the zone defined in settings, if no settings, use the zone of the server 120 * @return 121 */ 122 public static ZonedDateTime localNow() { 123 return ZonedDateTime.now(Context.getSettings().getTimeZoneId()); 124 } 125 126 127 public static ZoneId currentUserTimeZoneId() { 128 if (!empty(Context.getUser().getTimeZoneId())) { 129 return ZoneId.of(Context.getUser().getTimeZoneId()); 130 } else if (!emptyInstance(Settings.instance().getTimeZoneId())) { 131 return Settings.instance().getTimeZoneId(); 132 } else { 133 return ZoneId.of("UTC"); 134 } 135 } 136 137 public static int currentUserUtcOffsetMinutes() { 138 ZonedDateTime now = ZonedDateTime.now(currentUserTimeZoneId()); 139 return now.getOffset().getTotalSeconds() / 60; 140 } 141 142 /** 143 * Format the current time using a date format string 144 * @param format 145 * @return 146 */ 147 public static String formatNow(String format) { 148 DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); 149 return utcNow().format(formatter); 150 } 151 152 /* We have to add this generic Object overload, because Method typing dispatching 153 * does not work correctly when called from the templates. So instead we have to 154 * include the type in the name of each function. Blech. 155 * */ 156 public static String formatLocalDate(Object dt, String formatPattern) { 157 if (dt instanceof Long) { 158 return formatLocalDateFromLong((Long) dt, formatPattern); 159 } else if (dt instanceof ZonedDateTime) { 160 return formatLocalDateFromZonedDate((ZonedDateTime) dt, formatPattern); 161 } else if (dt instanceof Date) { 162 return formatLocalDateFromJDate((Date) dt, formatPattern); 163 } else if (dt instanceof String) { 164 return formatLocalDateFromString((String)dt, formatPattern); 165 } 166 return ""; 167 } 168 169 public static String formatLocalDate(Object dt) { 170 return formatLocalDate(dt, null); 171 } 172 173 174 public static String formatLocalDateFromJDate(Date date) { 175 return formatLocalDateFromJDate(date, null); 176 } 177 178 public static String formatLocalDateFromJDate(Date date, String formatPattern) { 179 if (date == null) { 180 return ""; 181 } 182 return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(date.toInstant(), UTC), formatPattern); 183 } 184 185 public static String formatLocalDateFromString(String dateStamp, String formatPattern) { 186 LocalDateTime ldt = null; 187 if (dateStamp.length() == 19) { 188 ldt = LocalDateTime.parse(dateStamp, SQL_FORMAT); 189 } else if (dateStamp.length() == 20) { 190 ldt = LocalDateTime.parse(dateStamp, SLUG_FORMAT); 191 } 192 if (ldt == null) { 193 throw new UsageException("DateStamp " + dateStamp + " did not have a recognizable format."); 194 } 195 return formatLocalDateFromZonedDate(ldt.atZone(UTC), formatPattern); 196 } 197 198 public static String formatLocalDateFromLong(long epochMillis) { 199 return formatLocalDateFromLong(epochMillis, null); 200 } 201 202 public static String formatLocalDateFromLong(long epochMillis, String formatPattern) { 203 if (epochMillis == 0L) { 204 return ""; 205 } 206 return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC), formatPattern); 207 } 208 209 public static String formatLocalDateFromLong(Long epochMillis) { 210 return formatLocalDateFromLong(epochMillis, null); 211 } 212 213 public static String formatLocalDateFromLong(Long epochMillis, String formatPattern) { 214 if (epochMillis == 0L || epochMillis == null) { 215 return ""; 216 } 217 return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC), formatPattern); 218 } 219 220 public static String formatLocalDateFromZonedDate(ZonedDateTime date) { 221 return formatLocalDateFromZonedDate(date, null); 222 } 223 224}