Convert milliseconds to date string back and forth using java 7 - date

I have the following code which uses all recommendations discussed in similar questions.
public class DateUtils {
static String secondsToDate(String seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(Long.parseLong(seconds) * 1000);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
return String.format("%d-%d-%d", year, month, day);
}
static String dateToSeconds(String date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parsed = format.parse(date);
long timeInMillis = parsed.getTime();
return Long.toString(timeInMillis / 1000);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String timestamp = "1409515200";
String date = secondsToDate(timestamp);
String timestamp2 = dateToSeconds(date);
System.out.printf("%s %s", timestamp, timestamp2);
}
}
The result of the code:
1409515200 1406836800
As you can see the conversion back and forth doesn't work. What's wrong?

Your problem here is the rounding. In the first method, you are converting your timestamp (which is the number of milliseconds from 1970) into a date. You are now getting only the date, discarding hours, minutes, seconds and milliseconds and converting it back. This means that you will always have a difference of the amount you are discarding (between 0 at 00:00:00:000 and 86400000 at 23:59:59:999). To fix it, simply change your date format to include the hours with milliseconds precision.

The answer by Aurasphere is correct and should be accepted.
Some further tips…
Use date-time classes for date-time values, rather than strings. Perform your business logic using date-time objects, and pass around such objects amongst your code rather than strings.
Avoid tracking date-times as a count-from-epoch. When you do need to serialize to text, use the unambiguous and easy-to-read formats defined by the ISO 8601 standard such as 2016-05-09T16:47:54Z.
You are using old troublesome legacy classes that have been supplanted by the java.time framework built into Java 8 and later. Much of that functionality has been back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted for Android in the ThreeTenABP project.
Using java.time classes will make your work easier and your code easier to comprehend, less likely to encounter the confusion seen in the Question.
An Instant is a moment on the timeline in UTC, with a resolution of nanoseconds. That class offers a convenient factory method ofEpochSecond, so no need to multiply by a thousand for milliseconds.
String input = "1409515200";
long seconds = Long.parseLong( input );
Instant instant = Instant.ofEpochSecond( seconds );
To get the wall-clock time for some locality, assign a time zone to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
To generate a string in standard ISO 8601 format, call toString. Note that this method extends that format to append the name of the time zone in square brackets. For example, 2007-12-03T10:15:30+01:00[Europe/Paris].
String output = zdt.toString();
To get the date-only as in the Question, extract a LocalDate object.
LocalDate localDate = zdt.toLocalDate();
From there you can determine the first moment of the day. The first moment is not always the time-of-day 00:00:00.0, so let java.time determine that.
ZonedDateTime zdtStartOfDay = localDate.atStartOfDay( zoneId );
To get the two long integer counts of seconds seen in the Question, extract an Instant from each of our ZonedDateTime objects, and ask for the seconds-since-epoch. Note that you might be losing data as the ZonedDateTime/Instant objects can store values with a resolution up to nanoseconds. The call asking for whole seconds from epoch means any fraction of a second is truncated.
long seconds1 = zdt.toInstant().getEpochSecond();
long seconds2 = zdtStartOfDay.toInstant().getEpochSecond();

Related

Date Format For Google Classroom API ScheduledTime

I'm trying to set the scheduled time when creating an assignment using the Google Classroom API. However, I'm confused about which date format is needed. By the error messages, it seems to accept a string which holds a timestamp and a timezone or Z at the end. Among others, I've tried using System.currentTimeMillis() + "Z", as well as googleDate.getValue() + "Z", googleDate.getValue() since Google Date format seems to be the way to go based on this doc but none of them seem to work.
Any ideas perhaps?
Thank you.
String timezone = timestamp + offset + "";
System.currentTimeMillis()
com.google.api.client.util.DateTime googleDate =
new com.google.api.client.util.DateTime(new java.util.Date());
// Date javaDate = new Date(googleDate.getValue());
CourseWork courseWork = new CourseWork()
.setCourseId(course.getId())
.setTitle("title PUBLISHED 2")
.setDescription("desc")
.setScheduledTime(googleDate.getValue() + "Z")
.setMaxPoints(100.0)
.setDueDate(date)
.setDueTime(timeOfDay)
.setWorkType("ASSIGNMENT")
.setState("PUBLISHED")
;
This is what I get when I manually add a timestamp and turn it into a string.
And this using the Google date instead.
And this with the new Java 8 apis
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work. The following code gives the same result as the code from your answer.
LocalDate localDate = LocalDate.now().plusDays(7);
String s = localDate.atStartOfDay(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
System.out.println(s);
Output in my time zone today:
2021-10-20T00:00:00+02:00
Compared to your own answer you have fewer conversions, and you are freed from writing your own format pattern string since the formatter we need is built in.
This worked:
LocalDate localDate = LocalDate.now().plusDays(7);
java.util.Date date1 = java.util.Date.from(localDate.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant());
String s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(date1);
It seems the imports were using the Google Date class instead of java.util.date.

How to make a program that reads a date and calculate the number of days left until the end of year?

i have a really simple problem, i don't know how to deduct the user's date by 01/01 / (the user year) +1. Im really stuck at this point.
public static void main(String[] args)
{
String date;
Scanner teclado = new Scanner (System.in);
System.out.println("Dame una fecha formato dd/mm/yyyy");
date=teclado.next();
Date mydate =FinalAnio.ParseFecha(date);
System.out.println(mydate);
}
public static Date ParseFecha(String fecha)
{
SimpleDateFormat formato = new SimpleDateFormat("dd/mm/yyyy");
Date fechaDate = null;
try
{
fechaDate = formato.parse(fecha);
}
catch (ParseException ex)
{
System.out.println(ex);
}
return fechaDate;
}
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Do not use mm for the month as it is used for the minute. For the month, the correct symbol is MM. Check DateTimeFormatter to learn more about various symbols used for parsing/formatting string/date-time.
Learn about the calculations of the period and duration from Period and Duration tutorial from Oracle. It would also be worth going through this Wikipedia page on Durations.
Demo:
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter date in the format dd/MM/yyyy: ");
String strDate = scanner.next();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd/MM/uuuu", Locale.ENGLISH);
LocalDate userDate = LocalDate.parse(strDate, dtf);
// The date representing 01/01/(the user year)+1
LocalDate targetDate = userDate.withDayOfMonth(1).withMonth(1).plusYears(1);
System.out.println("User's date: " + strDate);
System.out.println("Target date: " + targetDate.format(dtf));
Period period = Period.between(userDate, targetDate);
System.out.printf("Difference: %d days %d months %d years%n", period.getDays(), period.getMonths(),
period.getYears());
System.out.println("The difference in terms of days: " + ChronoUnit.DAYS.between(userDate, targetDate));
}
}
A sample run:
Enter date in the format dd/MM/yyyy: 20/10/2015
User's date: 20/10/2015
Target date: 01/01/2016
Difference: 12 days 2 months 0 years
The difference in terms of days: 73
Learn about the modern date-time API from Trail: Date Time.
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work.
DateTimeFormatter formatador = DateTimeFormatter.ofPattern("dd/MM/uuuu");
String entradaUsuario = "02/12/2020";
LocalDate fecha = LocalDate.parse(entradaUsuario, formatador);
LocalDate finDeAño = fecha.with(MonthDay.of(Month.DECEMBER, 31));
long diasRestantes = ChronoUnit.DAYS.between(fecha, finDeAño);
System.out.println(diasRestantes);
Output is:
29
In the format pattern string upper case MM is for month of year (lower case mm would be minute of hour, so not useful here). uuuu is for year (yyyy would work too).
fecha.with(MonthDay.of(Month.DECEMBER, 31)) adjusts the date to December 31 in the same year.
Link
Oracle tutorial: Date Time explaining how to use java.time.

I have a string date format 01/01/2017 6:54 PM and want to convert it to 2017-01-01T00:00:05.383+0100 ISOFormat in scala

def cleantz( time : String ) : String = {
var sign_builder= new StringBuilder ++= time
println(sign_builder)
var clean_sign = ""
if (sign_builder.charAt(23).toString == "-"){
clean_sign= sign_builder.replace(23,24,"-").toString()
}else{
clean_sign = sign_builder.replace(23,24,"+").toString()
}
var time_builder= new StringBuilder ++= clean_sign
if (time_builder.charAt(26).toString == ":"){
val cleanz = time_builder.deleteCharAt(26)
cleanz.toString()
}else{
time_builder.toString()
}
}
val start = ISO8601Format.parse(cleantz(01/01/2017 6:54 PM))
I get this error:
java.lang.StringIndexOutOfBoundsException: String index out of range: 23
java.time
For the sake of completeness I should like to contribute the modern answer. It’s quite simple and straightforward.
I am sorry that I can neither write Scala code nor test it on my computer. I have to trust you to translate from Java.
private static DateTimeFormatter inputFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy h:mm a", Locale.US);
public static String cleantz(String time) {
return LocalDateTime.parse(time, inputFormatter)
.atOffset(ZoneOffset.ofHours(1))
.toString();
}
Now cleantz("01/01/2017 6:54 PM") returns 2017-01-01T18:54+01:00, which is in ISO 8601 format. I would immediately suppose that you’re set. If for some reason you want or need the seconds too, replace .toString(); with:
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Now the result is 2017-01-01T18:54:00+01:00. In both cases the milliseconds would have been printed if there were any.
Since AM and PM are hardly used in other languages than English, I suggest you give an English-speaking locale to DateTimeFormatter.ofPattern() (in my example I used Locale.US). Failing to provide a locale will cause the code to fail on many computers with non-English language settings.
Why java.time?
SimpleDateFormat and friends are long outdated and notoriously troublesome. I cannot count the questions asked on Stack Overflow because SimpleDateFormat behaved differently from what every sane programmer would have expected, or offered no help to debug the simple errors we all make from time to time.
Joda-Time was good for a long time. Today the Joda-Time homepage says:
Note that Joda-Time is considered to be a largely “finished” project.
No major enhancements are planned. If using Java SE 8, please migrate
to java.time (JSR-310).
java.time is the modern Java date & time API built using the experience from Joda-Time and under the same lead developer, Stephen Colebourne. It is built into Java 8 and later, and a backport exists for Java 6 and 7, so you can use the same classes there too.
Assuming that your input string is 01/01/2017 6:54 PM: it has 18 characters. When you call charAt(23), it tries to get the character at position 23, which doesn't exist: the string has positions from zero (the first 0) to 17 (the M). If you try to get a position greater than that, it throws a StringIndexOutOfBoundsException.
But you don't need to do all this string manipulation. If you have a string that represents a date in some format, and want to convert it to another format, all you need is:
parse the original string to a date
format this date to another format
So you need 2 different Joda formatter's (one for each step). But there's one additional detail.
The input has a date (01/01/2017) and a time (6:54 PM), and the output has a date (2017-01-01), a time (18:54:00.000) and the UTC offset (+0100). So you'll have an additional step:
parse the original string to a date
add the +0100 offset to the parsed date
format this date to another format
With Joda-Time, this can be achieved with the following code:
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.ISODateTimeFormat
val fmt = DateTimeFormat.forPattern("dd/MM/yyyy h:mm a")
// parse the date
val localDate = LocalDateTime.parse("01/01/2017 6:54 PM", fmt)
// add the +01:00 offset
val dt = localDate.toDateTime(DateTimeZone.forOffsetHours(1))
// format to ISO8601
print(ISODateTimeFormat.dateTime().print(dt))
The output will be:
2017-01-01T18:54:00.000+01:00
Note that the offset is printed as +01:00. If you want exactly +0100 (without the :), you'll need to create another formatter:
val formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
println(formatter.print(dt))
The output will be:
2017-01-01T18:54:00.000+0100
This is the code I used to achieve the same result. The error occurred because I was trying to parse the wrong date format.
val inputForm = new SimpleDateFormat("MM/dd/yyyy h:mm a")
val outputForm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
val dateFormat1 = start_iso
val dateFormat2 = stop_iso
val start = outputForm.format(inputForm.parse(start_iso))
val stop = outputForm.format(inputForm.parse(stop_iso))
println(start)
println(stop)

Why substracting two Dates gives me an extra hour?

I wanted to perform some benchmarks on some methods of my program so I wrote this little snippet
object ExecutionTime {
private val format = new java.text.SimpleDateFormat("hh:mm:ss")
private var timeList = scala.collection.mutable.MutableList[String]()
def startTimer() : Long = {
val start = System.currentTimeMillis()
start
}
def getExecutionTime(start : Long) {
val executionTime = System.currentTimeMillis() - start
timeList.+=(format.format(new Date(executionTime)))
}
def printResults() {
timeList.mkString(" /n ")
}
}
To test this, I ran this little piece of code :
object Test {
val begin = ExecutionTime.startTimer()
waitFor2Seconds()
ExecutionTime.getExecutionTime(begin)
ExecutionTime.printResults()
}
However, when I run this I am getting a strange output :
01:00:02 // Should be 00:00:02
The result I have is the one that I expect, but with 1 extra hour.
Anyone have an idea on this?
You are constructing a Date object using the time delta between clock measurements. Therefore the SimpleDateFormat is translating the Date to local time when constructing the String.
From the documentation (emphasis mine):
SimpleDateFormat is a concrete class for formatting and parsing dates
in a locale-sensitive manner.
Your constructed Date is epoch + 2 seconds, so your local time must be (epoch + 1 hour + 2 seconds); and that is what the formatter is printing.
To have SimpleDateFormat print without the 1 hour addition you need to set the locale to be GMT and change your hour format to be "HH":
private val format = new java.text.SimpleDateFormat("HH:mm:ss")
format.setTimeZone(java.util.TimeZone.getTimeZone("GMT"))
As another suggestion, look into the Joda time library (here's a scala wrapper). This library can be used to easily subtract and add periods of time and convert to time formats on the fly. I much prefer it to the SimpleDateFormat class.

How to parse string with date, but without time in local format to ZonedDateTime?

This question is similar to How to parse ZonedDateTime with default zone? but addinitional condition.
I have a string param that represent a date in UK format: "3/6/09". It doesn't contain time, only date. But may contain it and even time zone.
And I want to parse it to ZonedDateTime.
public static ZonedDateTime parse(String value) {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(SHORT).withLocale(Locale.UK).withZone(ZoneId.systemDefault());
TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from);
if (temporalAccessor instanceof ZonedDateTime) {
return ((ZonedDateTime) temporalAccessor);
}
if (temporalAccessor instanceof LocalDateTime) {
return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault());
}
return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault());
}
But, it fails with exception:
java.time.format.DateTimeParseException: Text '3/6/2009' could not be parsed at index 6
It's a bug for me, or isn't?
In my opinion is not a bug. Your approach is flawed.
First of all you are returning a ZonedDateTime so it is expected that the String contains full date, time and zone information. The string "3/6/09" should be parsed to a LocalDate.
Second, you are delegating a runtime detection of format to the library. Again, you should be parsing/formatting an expected format. Your application should know wether is expecting a full date & time or a partial (only date or only time).
Anyway you will have more luck detecting the format and then using different parsing methods.
Only local date:
DateTimeFormatter
.ofLocalizedDate(FormatStyle.SHORT)
.parse(value, LocalDate::from)`
Zoned date and time:
DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
.parse(value, ZonedDateTime::from)`
The format used can be seen using the getLocalizedDateTimePattern() method:
String fmt = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.UK);
The result is "dd/MM/yy HH:mm".
As such, the format is expecting both a date and a time with a space separator, so that is what must be provided.
In addition, the format/parse expects there to be two digits for the day-of-month and two digits for the month-of-year. Thus, you would need to pass in "03/06/09 00:00" in order to get the result you expect, in which case you can parse directly to a LocalDateTime.
Alternatively, use ofLocalizedDate():
DateTimeFormatter formatter =
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.UK);
LocalDate date = LocalDate.parse("03/06/99", formatter);
Note that the input must still have two digits for the day and month.
Alternatively, parse using a specific pattern that can handle the missing leading zeroes:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yy");
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"
Update: Lenient parsing also handles this case:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseLenient().appendPattern("dd/MM/yy").toFormatter();
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"